diff --git a/.github/workflows/builds.yml b/.github/workflows/builds.yml index 92da5a94e84..cad6b7ad3c6 100644 --- a/.github/workflows/builds.yml +++ b/.github/workflows/builds.yml @@ -82,6 +82,7 @@ jobs: docker-push: name: ${{ matrix.role }} images runs-on: ubuntu-latest + environment: Production Docker Registry needs: matrix_builder # setup jobs for each role diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index b708d0b973c..3b5620f43c2 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -1,10 +1,18 @@ name: CD on: - push: - tags: - - '*' - - "!daily-*" +# Workflow dispatch for now, while we're testing environments + # push: + # tags: + # - '*' + # - "!daily-*" + workflow_dispatch: + inputs: + tag: + description: 'Tag/commit' + required: true + type: string + env: GO_VERSION: "1.22" @@ -13,6 +21,7 @@ jobs: docker-push: name: Push to container registry runs-on: ubuntu-latest + environment: Production Docker Registry steps: - name: Setup Go uses: actions/setup-go@v4 @@ -21,6 +30,8 @@ jobs: go-version: ${{ env.GO_VERSION }} - name: Checkout repo uses: actions/checkout@v2 + with: + ref: ${{ inputs.tag }} # Provide Google Service Account credentials to Github Action, allowing interaction with the Google Container Registry # Logging in as github-actions@dl-flow.iam.gserviceaccount.com - id: auth diff --git a/.golangci.yml b/.golangci.yml index 1671d0e0313..346e365cbaa 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -5,7 +5,7 @@ linters-settings: goimports: # put imports beginning with prefix after 3rd-party packages; # it's a comma-separated list of prefixes - local-prefixes: github.com/onflow/flow-go + local-prefixes: github.com/onflow/flow-go/ gosec: # To select a subset of rules to run. diff --git a/CODEOWNERS b/CODEOWNERS index 0d8beae649c..79e45d18dc7 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -8,8 +8,8 @@ /engine/consensus/** @AlexHentschel @durkmurder @jordanschalm # Execution Stream -/cmd/execution/** @ramtinms -/engine/execution/** @ramtinms +/cmd/execution/** @zhangchiqing +/engine/execution/** @zhangchiqing # Access Stream /access/** @peterargue @@ -18,19 +18,18 @@ /engine/access/** @peterargue # Verification Stream -/cmd/verification/** @ramtinms @yhassanzadeh13 -/engine/verification/** @ramtinms @yhassanzadeh13 -/module/chunking/** @ramtinms -/integration/tests/verification @ramtinms @yhassanzadeh13 +/cmd/verification/** @zhangchiqing +/engine/verification/** @zhangchiqing +/integration/tests/verification @zhangchiqing # Ledger Stream -/ledger/** @ramtinms @AlexHentschel +/ledger/** @AlexHentschel # FVM Stream -/fvm/** @ramtinms @janezpodhostnik +/fvm/** @janezpodhostnik # Networking Stream -/network/** @yhassanzadeh13 +/network/** @Kay-Zee # Cryptography Stream /crypto/** @tarakby @@ -39,13 +38,13 @@ /cmd/bootstrap/** @zhangchiqing # Dev Tools Stream -.github/workflows/** @gomisha -/insecure/** @gomisha @yhassanzadeh13 -/integration/benchnet2/** @gomisha -/tools/test_monitor/** @gomisha +.github/workflows/** @Kay-Zee +/insecure/** @Kay-Zee +/integration/benchnet2/** @Kay-Zee +/tools/test_monitor/** @Kay-Zee # Performance Stream -/integration/benchmark/** @gomisha +/integration/benchmark/** @Kay-Zee # Execution Sync /module/executiondatasync/** @peterargue diff --git a/Makefile b/Makefile index 8538c94d42d..d0557991462 100644 --- a/Makefile +++ b/Makefile @@ -101,7 +101,7 @@ go-math-rand-check: # - "onflow/crypto/random" for deterministic randomness grep --include=\*.go \ --exclude=*test* --exclude=*helper* --exclude=*example* --exclude=*fixture* --exclude=*benchmark* --exclude=*profiler* \ - --exclude-dir=*test* --exclude-dir=*helper* --exclude-dir=*example* --exclude-dir=*fixture* --exclude-dir=*benchmark* --exclude-dir=*profiler* -rnw '"math/rand"'; \ + --exclude-dir=*test* --exclude-dir=*helper* --exclude-dir=*example* --exclude-dir=*fixture* --exclude-dir=*benchmark* --exclude-dir=*profiler* --exclude-dir=*emulator* -rnw '"math/rand"'; \ if [ $$? -ne 1 ]; then \ echo "[Error] Go production code should not use math/rand package"; exit 1; \ fi @@ -138,8 +138,8 @@ endif .PHONY: generate-openapi generate-openapi: - swagger-codegen generate -l go -i https://raw.githubusercontent.com/onflow/flow/master/openapi/access.yaml -D packageName=models,modelDocs=false,models -o engine/access/rest/models; - go fmt ./engine/access/rest/models + swagger-codegen generate -l go -i https://raw.githubusercontent.com/onflow/flow/master/openapi/access.yaml -D packageName=models,modelDocs=false,models -o engine/access/rest/http/models; + go fmt ./engine/access/rest/http/models .PHONY: generate generate: generate-proto generate-mocks generate-fvm-env-wrappers @@ -857,7 +857,7 @@ docker-all-tools: tool-util tool-remove-execution-fork PHONY: docker-build-util docker-build-util: - docker build -f cmd/Dockerfile --build-arg TARGET=./cmd/util --build-arg GOARCH=$(GOARCH) --build-arg VERSION=$(IMAGE_TAG) --build-arg CGO_FLAG=$(CRYPTO_FLAG) --target production \ + docker build -f cmd/Dockerfile --build-arg TARGET=./cmd/util --build-arg GOARCH=$(GOARCH) --build-arg VERSION=$(IMAGE_TAG) --build-arg CGO_FLAG=$(DISABLE_ADX) --target production \ -t "$(CONTAINER_REGISTRY)/util:latest" \ -t "$(CONTAINER_REGISTRY)/util:$(IMAGE_TAG)" . diff --git a/README.md b/README.md index ec725094a3e..026aeb86f65 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ The following table lists all work streams and links to their home directory and ## Installation - Clone this repository -- Install [Go](https://golang.org/doc/install) (Flow supports Go 1.18 and later) +- Install [Go](https://golang.org/doc/install) (Flow requires Go 1.22 and later) - Install [Docker](https://docs.docker.com/get-docker/), which is used for running a local network and integration tests - Make sure the [`GOPATH`](https://golang.org/cmd/go/#hdr-GOPATH_environment_variable) and `GOBIN` environment variables are set, and `GOBIN` is added to your path: diff --git a/access/errors.go b/access/errors.go index a663179f018..310c86b43b5 100644 --- a/access/errors.go +++ b/access/errors.go @@ -12,6 +12,10 @@ import ( // ErrUnknownReferenceBlock indicates that a transaction references an unknown block. var ErrUnknownReferenceBlock = errors.New("unknown reference block") +// IndexReporterNotInitialized is returned when indexReporter is nil because +// execution data syncing and indexing is disabled +var IndexReporterNotInitialized = errors.New("index reported not initialized") + // IncompleteTransactionError indicates that a transaction is missing one or more required fields. type IncompleteTransactionError struct { MissingFields []string @@ -115,3 +119,14 @@ func IsInsufficientBalanceError(err error) bool { var balanceError InsufficientBalanceError return errors.As(err, &balanceError) } + +// IndexedHeightFarBehindError indicates that a node is far behind on indexing. +type IndexedHeightFarBehindError struct { + SealedHeight uint64 + IndexedHeight uint64 +} + +func (e IndexedHeightFarBehindError) Error() string { + return fmt.Sprintf("the difference between the latest sealed height (%d) and indexed height (%d) exceeds the maximum gap allowed", + e.SealedHeight, e.IndexedHeight) +} diff --git a/access/handler.go b/access/handler.go index 3007fc6f691..25316e7f3dd 100644 --- a/access/handler.go +++ b/access/handler.go @@ -1435,15 +1435,19 @@ func (h *Handler) SendAndSubscribeTransactionStatuses( messageIndex := counters.NewMonotonousCounter(0) return subscription.HandleSubscription(sub, func(txResults []*TransactionResult) error { for i := range txResults { - value := messageIndex.Increment() + index := messageIndex.Value() + if ok := messageIndex.Set(index + 1); !ok { + return status.Errorf(codes.Internal, "message index already incremented to %d", messageIndex.Value()) + } err = stream.Send(&access.SendAndSubscribeTransactionStatusesResponse{ TransactionResults: TransactionResultToMessage(txResults[i]), - MessageIndex: value, + MessageIndex: index, }) if err != nil { return rpc.ConvertError(err, "could not send response", codes.Internal) } + } return nil diff --git a/access/mock/blocks.go b/access/mock/blocks.go index 153c2160321..088a50aa155 100644 --- a/access/mock/blocks.go +++ b/access/mock/blocks.go @@ -72,6 +72,34 @@ func (_m *Blocks) HeaderByID(id flow.Identifier) (*flow.Header, error) { return r0, r1 } +// IndexedHeight provides a mock function with given fields: +func (_m *Blocks) IndexedHeight() (uint64, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for IndexedHeight") + } + + var r0 uint64 + var r1 error + if rf, ok := ret.Get(0).(func() (uint64, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() uint64); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(uint64) + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // SealedHeader provides a mock function with given fields: func (_m *Blocks) SealedHeader() (*flow.Header, error) { ret := _m.Called() diff --git a/access/validator.go b/access/validator.go index f0dec8071d0..18d03acde6a 100644 --- a/access/validator.go +++ b/access/validator.go @@ -9,7 +9,7 @@ import ( "github.com/onflow/cadence" jsoncdc "github.com/onflow/cadence/encoding/json" - "github.com/onflow/cadence/runtime/parser" + "github.com/onflow/cadence/parser" "github.com/onflow/crypto" "github.com/onflow/flow-core-contracts/lib/go/templates" @@ -20,22 +20,32 @@ import ( "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/execution" "github.com/onflow/flow-go/module/metrics" + "github.com/onflow/flow-go/module/state_synchronization" "github.com/onflow/flow-go/state" "github.com/onflow/flow-go/state/protocol" ) +// DefaultSealedIndexedHeightThreshold is the default number of blocks between sealed and indexed height +// this sets a limit on how far into the past the payer validator will allow for checking the payer's balance. +const DefaultSealedIndexedHeightThreshold = 30 + type Blocks interface { HeaderByID(id flow.Identifier) (*flow.Header, error) FinalizedHeader() (*flow.Header, error) SealedHeader() (*flow.Header, error) + IndexedHeight() (uint64, error) } type ProtocolStateBlocks struct { - state protocol.State + state protocol.State + indexReporter state_synchronization.IndexReporter } -func NewProtocolStateBlocks(state protocol.State) *ProtocolStateBlocks { - return &ProtocolStateBlocks{state: state} +func NewProtocolStateBlocks(state protocol.State, indexReporter state_synchronization.IndexReporter) *ProtocolStateBlocks { + return &ProtocolStateBlocks{ + state: state, + indexReporter: indexReporter, + } } func (b *ProtocolStateBlocks) HeaderByID(id flow.Identifier) (*flow.Header, error) { @@ -56,7 +66,19 @@ func (b *ProtocolStateBlocks) FinalizedHeader() (*flow.Header, error) { } func (b *ProtocolStateBlocks) SealedHeader() (*flow.Header, error) { + return b.state.Sealed().Head() + +} + +// IndexedHeight returns the highest indexed height by calling corresponding function of indexReporter. +// Expected errors during normal operation: +// - access.IndexReporterNotInitialized - indexed reporter was not initialized. +func (b *ProtocolStateBlocks) IndexedHeight() (uint64, error) { + if b.indexReporter != nil { + return b.indexReporter.HighestIndexedHeight() + } + return 0, IndexReporterNotInitialized } // RateLimiter is an interface for checking if an address is rate limited. @@ -78,6 +100,59 @@ func (l *NoopLimiter) IsRateLimited(address flow.Address) bool { return false } +// PayerBalanceMode represents the mode for checking the payer's balance +// when validating transactions. It controls whether and how the balance +// check is performed during transaction validation. +// +// There are few modes available: +// +// - `Disabled` - Balance checking is completely disabled. No checks are +// performed to verify if the payer has sufficient balance to cover the +// transaction fees. +// - `WarnCheck` - Balance is checked, and a warning is logged if the payer +// does not have enough balance. The transaction is still accepted and +// processed regardless of the check result. +// - `EnforceCheck` - Balance is checked, and the transaction is rejected if +// the payer does not have sufficient balance to cover the transaction fees. +type PayerBalanceMode int + +const ( + // Disabled indicates that payer balance checking is turned off. + Disabled PayerBalanceMode = iota + + // WarnCheck logs a warning if the payer's balance is insufficient, but does not prevent the transaction from being accepted. + WarnCheck + + // EnforceCheck prevents the transaction from being accepted if the payer's balance is insufficient to cover transaction fees. + EnforceCheck +) + +func ParsePayerBalanceMode(s string) (PayerBalanceMode, error) { + switch s { + case Disabled.String(): + return Disabled, nil + case WarnCheck.String(): + return WarnCheck, nil + case EnforceCheck.String(): + return EnforceCheck, nil + default: + return 0, errors.New("invalid payer balance mode") + } +} + +func (m PayerBalanceMode) String() string { + switch m { + case Disabled: + return "disabled" + case WarnCheck: + return "warn" + case EnforceCheck: + return "enforce" + default: + return "" + } +} + type TransactionValidationOptions struct { Expiry uint ExpiryBuffer uint @@ -87,7 +162,7 @@ type TransactionValidationOptions struct { CheckScriptsParse bool MaxTransactionByteSize uint64 MaxCollectionByteSize uint64 - CheckPayerBalance bool + CheckPayerBalanceMode PayerBalanceMode } type ValidationStep struct { @@ -115,7 +190,7 @@ func NewTransactionValidator( options TransactionValidationOptions, executor execution.ScriptExecutor, ) (*TransactionValidator, error) { - if options.CheckPayerBalance && executor == nil { + if options.CheckPayerBalanceMode != Disabled && executor == nil { return nil, errors.New("transaction validator cannot use checkPayerBalance with nil executor") } @@ -193,7 +268,11 @@ func (v *TransactionValidator) Validate(ctx context.Context, tx *flow.Transactio // prevent the transaction from proceeding. if IsInsufficientBalanceError(err) { v.transactionValidationMetrics.TransactionValidationFailed(metrics.InsufficientBalance) - return err + + if v.options.CheckPayerBalanceMode == EnforceCheck { + log.Warn().Err(err).Str("transactionID", tx.ID().String()).Str("payerAddress", tx.Payer.String()).Msg("enforce check error") + return err + } } // log and ignore all other errors @@ -320,11 +399,20 @@ func (v *TransactionValidator) checkExpiry(tx *flow.TransactionBody) error { return nil } -func (v *TransactionValidator) checkCanBeParsed(tx *flow.TransactionBody) error { +func (v *TransactionValidator) checkCanBeParsed(tx *flow.TransactionBody) (err error) { + defer func() { + if r := recover(); r != nil { + if panicErr, ok := r.(error); ok { + err = InvalidScriptError{ParserErr: panicErr} + } else { + err = InvalidScriptError{ParserErr: fmt.Errorf("non-error-typed panic: %v", r)} + } + } + }() if v.options.CheckScriptsParse { - _, err := parser.ParseProgram(nil, tx.Script, parser.Config{}) - if err != nil { - return InvalidScriptError{ParserErr: err} + _, parseErr := parser.ParseProgram(nil, tx.Script, parser.Config{}) + if parseErr != nil { + return InvalidScriptError{ParserErr: parseErr} } } @@ -389,7 +477,7 @@ func (v *TransactionValidator) checkSignatureFormat(tx *flow.TransactionBody) er } func (v *TransactionValidator) checkSufficientBalanceToPayForTransaction(ctx context.Context, tx *flow.TransactionBody) error { - if !v.options.CheckPayerBalance { + if v.options.CheckPayerBalanceMode == Disabled { return nil } @@ -398,6 +486,19 @@ func (v *TransactionValidator) checkSufficientBalanceToPayForTransaction(ctx con return fmt.Errorf("could not fetch block header: %w", err) } + indexedHeight, err := v.blocks.IndexedHeight() + if err != nil { + return fmt.Errorf("could not get indexed height: %w", err) + } + + // we use latest indexed block to get the most up-to-date state data available for executing scripts. + // check here to make sure indexing is within an acceptable tolerance of sealing to avoid issues + // if indexing falls behind + sealedHeight := header.Height + if indexedHeight < sealedHeight-DefaultSealedIndexedHeightThreshold { + return IndexedHeightFarBehindError{SealedHeight: sealedHeight, IndexedHeight: indexedHeight} + } + payerAddress := cadence.NewAddress(tx.Payer) inclusionEffort := cadence.UFix64(tx.InclusionEffort()) gasLimit := cadence.UFix64(tx.GasLimit) @@ -407,7 +508,7 @@ func (v *TransactionValidator) checkSufficientBalanceToPayForTransaction(ctx con return fmt.Errorf("failed to encode cadence args for script executor: %w", err) } - result, err := v.scriptExecutor.ExecuteAtBlockHeight(ctx, v.verifyPayerBalanceScript, args, header.Height) + result, err := v.scriptExecutor.ExecuteAtBlockHeight(ctx, v.verifyPayerBalanceScript, args, indexedHeight) if err != nil { return fmt.Errorf("script finished with error: %w", err) } @@ -423,7 +524,7 @@ func (v *TransactionValidator) checkSufficientBalanceToPayForTransaction(ctx con } // return no error if payer has sufficient balance - if bool(canExecuteTransaction) { + if canExecuteTransaction { return nil } diff --git a/access/validator_test.go b/access/validator_test.go index 87b7ade62a9..8f87f871e3a 100644 --- a/access/validator_test.go +++ b/access/validator_test.go @@ -6,7 +6,7 @@ import ( "testing" "github.com/onflow/cadence" - "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/common" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" @@ -58,7 +58,7 @@ func (s *TransactionValidatorSuite) SetupTest() { s.chain = flow.Testnet.Chain() s.validatorOptions = access.TransactionValidationOptions{ - CheckPayerBalance: true, + CheckPayerBalanceMode: access.EnforceCheck, MaxTransactionByteSize: flow.DefaultMaxTransactionByteSize, MaxCollectionByteSize: flow.DefaultMaxCollectionByteSize, } @@ -88,6 +88,10 @@ func (s *TransactionValidatorSuite) TestTransactionValidator_ScriptExecutorInter scriptExecutor := execmock.NewScriptExecutor(s.T()) assert.NotNil(s.T(), scriptExecutor) + s.blocks. + On("IndexedHeight"). + Return(s.header.Height, nil) + scriptExecutor. On("ExecuteAtBlockHeight", mock.Anything, mock.Anything, mock.Anything, mock.Anything). Return(nil, errors.New("script executor internal error")). @@ -115,6 +119,10 @@ func (s *TransactionValidatorSuite) TestTransactionValidator_SufficientBalance() actualResponse, err := jsoncdc.Encode(actualResponseValue) assert.NoError(s.T(), err) + s.blocks. + On("IndexedHeight"). + Return(s.header.Height, nil) + scriptExecutor. On("ExecuteAtBlockHeight", mock.Anything, mock.Anything, mock.Anything, mock.Anything). Return(actualResponse, nil). @@ -142,27 +150,61 @@ func (s *TransactionValidatorSuite) TestTransactionValidator_InsufficientBalance actualResponse, err := jsoncdc.Encode(actualResponseValue) assert.NoError(s.T(), err) + s.blocks. + On("IndexedHeight"). + Return(s.header.Height, nil) + scriptExecutor. On("ExecuteAtBlockHeight", mock.Anything, mock.Anything, mock.Anything, mock.Anything). - Return(actualResponse, nil). - Once() + Return(actualResponse, nil).Twice() actualAccountResponse, err := unittest.AccountFixture() assert.NoError(s.T(), err) assert.NotNil(s.T(), actualAccountResponse) + validateTx := func() error { + txBody := unittest.TransactionBodyFixture() + validator, err := access.NewTransactionValidator(s.blocks, s.chain, s.metrics, s.validatorOptions, scriptExecutor) + assert.NoError(s.T(), err) + assert.NotNil(s.T(), validator) + + return validator.Validate(context.Background(), &txBody) + } + + s.Run("with enforce check", func() { + err := validateTx() + + expectedError := access.InsufficientBalanceError{ + Payer: unittest.AddressFixture(), + RequiredBalance: requiredBalance, + } + assert.ErrorIs(s.T(), err, expectedError) + }) + + s.Run("with warn check", func() { + s.validatorOptions.CheckPayerBalanceMode = access.WarnCheck + err := validateTx() + assert.NoError(s.T(), err) + }) +} + +func (s *TransactionValidatorSuite) TestTransactionValidator_SealedIndexedHeightThresholdLimit() { + scriptExecutor := execmock.NewScriptExecutor(s.T()) + + // setting indexed height to be behind of sealed by bigger number than allowed(DefaultSealedIndexedHeightThreshold) + indexedHeight := s.header.Height - 40 + + s.blocks. + On("IndexedHeight"). + Return(indexedHeight, nil) + validator, err := access.NewTransactionValidator(s.blocks, s.chain, s.metrics, s.validatorOptions, scriptExecutor) assert.NoError(s.T(), err) assert.NotNil(s.T(), validator) txBody := unittest.TransactionBodyFixture() - expectedError := access.InsufficientBalanceError{ - Payer: unittest.AddressFixture(), - RequiredBalance: requiredBalance, - } - - actualErr := validator.Validate(context.Background(), &txBody) + err = validator.Validate(context.Background(), &txBody) + assert.NoError(s.T(), err) - assert.ErrorIs(s.T(), actualErr, expectedError) } diff --git a/admin/README.md b/admin/README.md index 9da63d0831e..4603b90c7e8 100644 --- a/admin/README.md +++ b/admin/README.md @@ -115,3 +115,8 @@ curl localhost:9002/admin/run_command -H 'Content-Type: application/json' -d '{" curl localhost:9002/admin/run_command -H 'Content-Type: application/json' -d '{"commandName": "protocol-snapshot"}' curl localhost:9002/admin/run_command -H 'Content-Type: application/json' -d '{"commandName": "protocol-snapshot", "data": { "blocks-to-skip": 10 }}' ``` + +### To backfill transaction error messages +``` +curl localhost:9002/admin/run_command -H 'Content-Type: application/json' -d '{"commandName": "backfill-tx-error-messages", "data": { "start-height": 340, "end-height": 343, "execution-node-ids":["ec7b934df29248d574ae1cc33ae77f22f0fcf96a79e009224c46374d1837824e", "8cbdc8d24a28899a33140cb68d4146cd6f2f6c18c57f54c299f26351d126919e"] }}' +``` diff --git a/admin/commands/storage/backfill_tx_error_messages.go b/admin/commands/storage/backfill_tx_error_messages.go new file mode 100644 index 00000000000..0517bda41b0 --- /dev/null +++ b/admin/commands/storage/backfill_tx_error_messages.go @@ -0,0 +1,197 @@ +package storage + +import ( + "context" + "fmt" + + "github.com/onflow/flow-go/admin" + "github.com/onflow/flow-go/admin/commands" + "github.com/onflow/flow-go/engine/access/ingestion/tx_error_messages" + commonrpc "github.com/onflow/flow-go/engine/common/rpc" + "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/model/flow/filter" + "github.com/onflow/flow-go/state/protocol" +) + +var _ commands.AdminCommand = (*BackfillTxErrorMessagesCommand)(nil) + +// backfillTxErrorMessagesRequest represents the input parameters for +// backfilling transaction error messages. +type backfillTxErrorMessagesRequest struct { + startHeight uint64 // Start height from which to begin backfilling. + endHeight uint64 // End height up to which backfilling is performed. + executionNodeIds flow.IdentitySkeletonList // List of execution node IDs to be used for backfilling. +} + +// BackfillTxErrorMessagesCommand executes a command to backfill +// transaction error messages by fetching them from execution nodes. +type BackfillTxErrorMessagesCommand struct { + state protocol.State + txErrorMessagesCore *tx_error_messages.TxErrorMessagesCore +} + +// NewBackfillTxErrorMessagesCommand creates a new instance of BackfillTxErrorMessagesCommand +func NewBackfillTxErrorMessagesCommand( + state protocol.State, + txErrorMessagesCore *tx_error_messages.TxErrorMessagesCore, +) commands.AdminCommand { + return &BackfillTxErrorMessagesCommand{ + state: state, + txErrorMessagesCore: txErrorMessagesCore, + } +} + +// Validator validates the input for the backfill command. The input is validated +// for field types, boundaries, and coherence of start and end heights. +// +// Expected errors during normal operation: +// - admin.InvalidAdminReqError - if start-height is greater than end-height or +// if the input format is invalid, if an invalid execution node ID is provided. +func (b *BackfillTxErrorMessagesCommand) Validator(request *admin.CommandRequest) error { + input, ok := request.Data.(map[string]interface{}) + if !ok { + return admin.NewInvalidAdminReqFormatError("expected map[string]any") + } + + data := &backfillTxErrorMessagesRequest{} + + rootHeight := b.state.Params().SealedRoot().Height + data.startHeight = rootHeight // Default value + + sealed, err := b.state.Sealed().Head() + if err != nil { + return fmt.Errorf("failed to lookup sealed header: %w", err) + } + + lastSealedHeight := sealed.Height + if startHeightIn, ok := input["start-height"]; ok { + startHeight, err := parseN(startHeightIn) + if err != nil { + return admin.NewInvalidAdminReqErrorf("invalid 'start-height' field: %w", err) + } + + if startHeight > lastSealedHeight { + return admin.NewInvalidAdminReqErrorf( + "'start-height' %d must not be greater than latest sealed block %d", + startHeight, + lastSealedHeight, + ) + } + + if startHeight < rootHeight { + return admin.NewInvalidAdminReqErrorf( + "'start-height' %d must not be less than root block %d", + startHeight, + rootHeight, + ) + } + + data.startHeight = startHeight + } + + data.endHeight = lastSealedHeight // Default value + if endHeightIn, ok := input["end-height"]; ok { + endHeight, err := parseN(endHeightIn) + if err != nil { + return admin.NewInvalidAdminReqErrorf("invalid 'end-height' field: %w", err) + } + + if endHeight > lastSealedHeight { + return admin.NewInvalidAdminReqErrorf( + "'end-height' %d must not be greater than latest sealed block %d", + endHeight, + lastSealedHeight, + ) + } + + data.endHeight = endHeight + } + + if data.endHeight < data.startHeight { + return admin.NewInvalidAdminReqErrorf( + "'start-height' %d must not be less than 'end-height' %d", + data.startHeight, + data.endHeight, + ) + } + + identities, err := b.state.Final().Identities(filter.HasRole[flow.Identity](flow.RoleExecution)) + if err != nil { + return fmt.Errorf("failed to retreive execution IDs: %w", err) + } + + if executionNodeIdsIn, ok := input["execution-node-ids"]; ok { + executionNodeIds, err := b.parseExecutionNodeIds(executionNodeIdsIn, identities) + if err != nil { + return err + } + data.executionNodeIds = executionNodeIds + } else { + // in case no execution node ids provided, the command will use any valid execution node + data.executionNodeIds = identities.ToSkeleton() + } + + request.ValidatorData = data + + return nil +} + +// Handler performs the backfilling operation by fetching missing transaction +// error messages for blocks within the specified height range. Uses execution nodes +// from data.executionNodeIds if available, otherwise defaults to valid execution nodes. +// +// No errors are expected during normal operation. +func (b *BackfillTxErrorMessagesCommand) Handler(ctx context.Context, request *admin.CommandRequest) (interface{}, error) { + if b.txErrorMessagesCore == nil { + return nil, fmt.Errorf("failed to backfill, could not get transaction error messages storage") + } + + data := request.ValidatorData.(*backfillTxErrorMessagesRequest) + + for height := data.startHeight; height <= data.endHeight; height++ { + header, err := b.state.AtHeight(height).Head() + if err != nil { + return nil, fmt.Errorf("failed to get block header: %w", err) + } + + blockID := header.ID() + err = b.txErrorMessagesCore.HandleTransactionResultErrorMessagesByENs(ctx, blockID, data.executionNodeIds) + if err != nil { + return nil, fmt.Errorf("error encountered while processing transaction result error message for block: %d, %w", height, err) + } + } + + return nil, nil +} + +// parseExecutionNodeIds converts a list of node IDs from input to flow.IdentitySkeletonList. +// Returns an error if the IDs are invalid or empty. +// +// Expected errors during normal operation: +// - admin.InvalidAdminReqParameterError - if execution-node-ids is empty or has an invalid format. +func (b *BackfillTxErrorMessagesCommand) parseExecutionNodeIds(executionNodeIdsIn interface{}, allIdentities flow.IdentityList) (flow.IdentitySkeletonList, error) { + var ids flow.IdentityList + + switch executionNodeIds := executionNodeIdsIn.(type) { + case []string: + if len(executionNodeIds) == 0 { + return nil, admin.NewInvalidAdminReqParameterError("execution-node-ids", "must be a non empty list of strings", executionNodeIdsIn) + } + requestedENIdentifiers, err := commonrpc.IdentifierList(executionNodeIds) + if err != nil { + return nil, admin.NewInvalidAdminReqParameterError("execution-node-ids", err.Error(), executionNodeIdsIn) + } + + for _, enId := range requestedENIdentifiers { + id, exists := allIdentities.ByNodeID(enId) + if !exists { + return nil, admin.NewInvalidAdminReqParameterError("execution-node-ids", "could not find execution node by provided id", enId) + } + ids = append(ids, id) + } + default: + return nil, admin.NewInvalidAdminReqParameterError("execution-node-ids", "must be a list of strings", executionNodeIdsIn) + } + + return ids.ToSkeleton(), nil +} diff --git a/admin/commands/storage/backfill_tx_error_messages_test.go b/admin/commands/storage/backfill_tx_error_messages_test.go new file mode 100644 index 00000000000..2f80106c0de --- /dev/null +++ b/admin/commands/storage/backfill_tx_error_messages_test.go @@ -0,0 +1,538 @@ +package storage + +import ( + "context" + "fmt" + "os" + "testing" + + execproto "github.com/onflow/flow/protobuf/go/flow/execution" + "github.com/rs/zerolog" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + + "github.com/onflow/flow-go/admin" + "github.com/onflow/flow-go/admin/commands" + "github.com/onflow/flow-go/engine/access/ingestion/tx_error_messages" + accessmock "github.com/onflow/flow-go/engine/access/mock" + "github.com/onflow/flow-go/engine/access/rpc/backend" + connectionmock "github.com/onflow/flow-go/engine/access/rpc/connection/mock" + commonrpc "github.com/onflow/flow-go/engine/common/rpc" + "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/state/protocol" + "github.com/onflow/flow-go/state/protocol/invalid" + protocolmock "github.com/onflow/flow-go/state/protocol/mock" + "github.com/onflow/flow-go/storage" + storagemock "github.com/onflow/flow-go/storage/mock" + "github.com/onflow/flow-go/utils/unittest" + unittestMocks "github.com/onflow/flow-go/utils/unittest/mocks" +) + +const expectedErrorMsg = "expected test error" + +type BackfillTxErrorMessagesSuite struct { + suite.Suite + + command commands.AdminCommand + + log zerolog.Logger + state *protocolmock.State + snapshot *protocolmock.Snapshot + params *protocolmock.Params + + txErrorMessages *storagemock.TransactionResultErrorMessages + transactionResults *storagemock.LightTransactionResults + receipts *storagemock.ExecutionReceipts + headers *storagemock.Headers + + execClient *accessmock.ExecutionAPIClient + + connFactory *connectionmock.ConnectionFactory + allENIDs flow.IdentityList + + backend *backend.Backend + txResultErrorMessagesCore *tx_error_messages.TxErrorMessagesCore + + blockHeadersMap map[uint64]*flow.Header + + nodeRootBlock flow.Block + sealedBlock *flow.Block + blockCount int +} + +func TestBackfillTxErrorMessages(t *testing.T) { + t.Parallel() + suite.Run(t, new(BackfillTxErrorMessagesSuite)) +} + +func (suite *BackfillTxErrorMessagesSuite) SetupTest() { + suite.log = zerolog.New(os.Stderr) + + suite.state = new(protocolmock.State) + suite.headers = new(storagemock.Headers) + suite.receipts = new(storagemock.ExecutionReceipts) + suite.transactionResults = storagemock.NewLightTransactionResults(suite.T()) + suite.txErrorMessages = new(storagemock.TransactionResultErrorMessages) + suite.execClient = new(accessmock.ExecutionAPIClient) + + suite.blockCount = 5 + suite.blockHeadersMap = make(map[uint64]*flow.Header, suite.blockCount) + suite.nodeRootBlock = unittest.BlockFixture() + suite.nodeRootBlock.Header.Height = 0 + suite.blockHeadersMap[suite.nodeRootBlock.Header.Height] = suite.nodeRootBlock.Header + + parent := suite.nodeRootBlock.Header + + for i := 1; i <= suite.blockCount; i++ { + block := unittest.BlockWithParentFixture(parent) + // update for next iteration + parent = block.Header + suite.blockHeadersMap[block.Header.Height] = block.Header + suite.sealedBlock = block + } + + suite.params = protocolmock.NewParams(suite.T()) + suite.params.On("SealedRoot").Return( + func() *flow.Header { + return suite.nodeRootBlock.Header + }, nil) + suite.state.On("Params").Return(suite.params, nil).Maybe() + + suite.snapshot = createSnapshot(suite.T(), suite.sealedBlock.Header) + suite.state.On("Sealed").Return(suite.snapshot) + suite.state.On("Final").Return(suite.snapshot) + + suite.state.On("AtHeight", mock.Anything).Return( + func(height uint64) protocol.Snapshot { + if int(height) < len(suite.blockHeadersMap) { + header := suite.blockHeadersMap[height] + return createSnapshot(suite.T(), header) + } + return invalid.NewSnapshot(fmt.Errorf("invalid height: %v", height)) + }, + ) + + // Mock the protocol snapshot to return fixed execution node IDs. + suite.allENIDs = unittest.IdentityListFixture(1, unittest.WithRole(flow.RoleExecution)) + suite.snapshot.On("Identities", mock.Anything).Return( + func(flow.IdentityFilter[flow.Identity]) (flow.IdentityList, error) { + return suite.allENIDs, nil + }, nil).Maybe() + + // create a mock connection factory + suite.connFactory = connectionmock.NewConnectionFactory(suite.T()) + + executionNodeIdentitiesProvider := commonrpc.NewExecutionNodeIdentitiesProvider( + suite.log, + suite.state, + suite.receipts, + nil, + nil, + ) + + var err error + suite.backend, err = backend.New(backend.Params{ + State: suite.state, + ExecutionReceipts: suite.receipts, + ConnFactory: suite.connFactory, + MaxHeightRange: backend.DefaultMaxHeightRange, + Log: suite.log, + SnapshotHistoryLimit: backend.DefaultSnapshotHistoryLimit, + Communicator: backend.NewNodeCommunicator(false), + ScriptExecutionMode: backend.IndexQueryModeExecutionNodesOnly, + TxResultQueryMode: backend.IndexQueryModeExecutionNodesOnly, + ChainID: flow.Testnet, + ExecNodeIdentitiesProvider: executionNodeIdentitiesProvider, + }) + require.NoError(suite.T(), err) + + suite.txResultErrorMessagesCore = tx_error_messages.NewTxErrorMessagesCore( + suite.log, + suite.backend, + suite.txErrorMessages, + executionNodeIdentitiesProvider, + ) + + suite.command = NewBackfillTxErrorMessagesCommand( + suite.state, + suite.txResultErrorMessagesCore, + ) +} + +// TestValidateInvalidFormat validates that invalid input formats trigger appropriate error responses. +// It tests several invalid cases such as: +// - Invalid "start-height" and "end-height" fields where values are in an incorrect format or out of valid ranges. +// - Invalid combinations of "start-height" and "end-height" where logical constraints are violated. +// - Invalid types for "execution-node-ids" which must be a list of strings, and invalid node IDs. +func (suite *BackfillTxErrorMessagesSuite) TestValidateInvalidFormat() { + // invalid start-height + suite.Run("invalid start-height field", func() { + err := suite.command.Validator(&admin.CommandRequest{ + Data: map[string]interface{}{ + "start-height": "123", + }, + }) + suite.Error(err) + suite.Equal(err, admin.NewInvalidAdminReqErrorf( + "invalid 'start-height' field: %w", + fmt.Errorf("invalid value for \"n\": %v", 0))) + }) + + // invalid start-height, start-height is greater than latest sealed block + suite.Run("start-height is greater than latest sealed block", func() { + startHeight := 100 + err := suite.command.Validator(&admin.CommandRequest{ + Data: map[string]interface{}{ + "start-height": float64(startHeight), + }, + }) + suite.Error(err) + suite.Equal(err, admin.NewInvalidAdminReqErrorf( + "'start-height' %d must not be greater than latest sealed block %d", startHeight, suite.sealedBlock.Header.Height)) + }) + + // invalid start-height, start-height is less than root block + suite.Run("start-height is less than root block", func() { + suite.nodeRootBlock.Header = suite.blockHeadersMap[2] // mock sealed root block to height 2 + + startHeight := 1 + err := suite.command.Validator(&admin.CommandRequest{ + Data: map[string]interface{}{ + "start-height": float64(startHeight), + }, + }) + suite.Error(err) + suite.Equal(err, admin.NewInvalidAdminReqErrorf( + "'start-height' %d must not be less than root block %d", startHeight, suite.nodeRootBlock.Header.Height)) + + suite.nodeRootBlock.Header = suite.blockHeadersMap[0] // mock sealed root block back to height 0 + }) + + // invalid end-height + suite.Run("invalid end-height field", func() { + err := suite.command.Validator(&admin.CommandRequest{ + Data: map[string]interface{}{ + "end-height": "123", + }, + }) + suite.Error(err) + suite.Equal(err, admin.NewInvalidAdminReqErrorf( + "invalid 'end-height' field: %w", + fmt.Errorf("invalid value for \"n\": %v", 0))) + }) + + // end-height is greater than latest sealed block + suite.Run("invalid end-height is greater than latest sealed block", func() { + endHeight := 100 + err := suite.command.Validator(&admin.CommandRequest{ + Data: map[string]interface{}{ + "start-height": float64(1), // raw json parses to float64 + "end-height": float64(endHeight), // raw json parses to float64 + "execution-node-ids": []string{suite.allENIDs[0].ID().String()}, + }, + }) + suite.Error(err) + suite.Equal(err, admin.NewInvalidAdminReqErrorf( + "'end-height' %d must not be greater than latest sealed block %d", + endHeight, + suite.sealedBlock.Header.Height, + )) + }) + + suite.Run("invalid combination of start-height and end-height fields", func() { + startHeight := 3 + endHeight := 1 + err := suite.command.Validator(&admin.CommandRequest{ + Data: map[string]interface{}{ + "start-height": float64(startHeight), // raw json parses to float64 + "end-height": float64(endHeight), // raw json parses to float64 + }, + }) + suite.Error(err) + suite.Equal(err, admin.NewInvalidAdminReqErrorf( + "'start-height' %d must not be less than 'end-height' %d", startHeight, endHeight)) + }) + + // invalid execution-node-ids param + suite.Run("invalid execution-node-ids field", func() { + // invalid type + err := suite.command.Validator(&admin.CommandRequest{ + Data: map[string]interface{}{ + "execution-node-ids": []int{1, 2, 3}, + }, + }) + suite.Error(err) + suite.Equal(err, admin.NewInvalidAdminReqParameterError( + "execution-node-ids", "must be a list of strings", []int{1, 2, 3})) + + // invalid type + err = suite.command.Validator(&admin.CommandRequest{ + Data: map[string]interface{}{ + "execution-node-ids": "123", + }, + }) + suite.Error(err) + suite.Equal(err, admin.NewInvalidAdminReqParameterError( + "execution-node-ids", "must be a list of strings", "123")) + + // invalid execution node id + invalidENID := unittest.IdentifierFixture() + err = suite.command.Validator(&admin.CommandRequest{ + Data: map[string]interface{}{ + "start-height": float64(1), // raw json parses to float64 + "end-height": float64(4), // raw json parses to float64 + "execution-node-ids": []string{invalidENID.String()}, + }, + }) + suite.Error(err) + suite.Equal(err, admin.NewInvalidAdminReqParameterError( + "execution-node-ids", "could not find execution node by provided id", invalidENID.String())) + }) +} + +// TestValidateValidFormat verifies that valid input parameters result in no validation errors +// in the command validator. +// It tests various valid cases, such as: +// - Default parameters (start-height, end-height, execution-node-ids) are used. +// - Provided parameters (start-height, end-height, execution-node-ids) values are within expected ranges. +func (suite *BackfillTxErrorMessagesSuite) TestValidateValidFormat() { + // start-height and end-height are not provided, the root block and the latest sealed block + // will be used as the start and end heights respectively. + // execution-node-ids is not provided, any valid execution node will be used. + suite.Run("happy case, all default parameters", func() { + err := suite.command.Validator(&admin.CommandRequest{ + Data: map[string]interface{}{}, + }) + suite.NoError(err) + }) + + // all parameters are provided + suite.Run("happy case, all parameters are provided", func() { + err := suite.command.Validator(&admin.CommandRequest{ + Data: map[string]interface{}{ + "start-height": float64(1), // raw json parses to float64 + "end-height": float64(3), // raw json parses to float64 + "execution-node-ids": []string{suite.allENIDs[0].ID().String()}, + }, + }) + suite.NoError(err) + }) +} + +// TestHandleBackfillTxErrorMessages handles the transaction error backfill logic for different scenarios. +// It validates behavior when transaction error messages exist or do not exist in the database, handling both default and custom parameters. +func (suite *BackfillTxErrorMessagesSuite) TestHandleBackfillTxErrorMessages() { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + // default parameters + req := &admin.CommandRequest{ + Data: map[string]interface{}{}, + } + suite.Require().NoError(suite.command.Validator(req)) + + suite.Run("happy case, all default parameters, tx error messages do not exist in db", func() { + // Create a mock execution client to simulate communication with execution nodes. + suite.connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &unittestMocks.MockCloser{}, nil) + + for i := suite.nodeRootBlock.Header.Height; i <= suite.blockHeadersMap[uint64(suite.blockCount)].Height; i++ { + blockId := suite.blockHeadersMap[i].ID() + + // Setup mock storing the transaction error message after retrieving the failed result. + suite.txErrorMessages.On("Exists", blockId).Return(false, nil).Once() + + results := suite.generateResultsForBlock() + + // Mock the execution node API calls to fetch the error messages. + suite.mockTransactionErrorMessagesResponseByBlockID(blockId, results) + + // Setup mock storing the transaction error message after retrieving the failed result. + suite.mockStoreTxErrorMessages(blockId, results, suite.allENIDs[0].ID()) + } + + _, err := suite.command.Handler(ctx, req) + suite.Require().NoError(err) + suite.assertAllExpectations() + }) + + suite.Run("happy case, all default parameters, tx error messages exist in db", func() { + for i := suite.nodeRootBlock.Header.Height; i <= suite.blockHeadersMap[uint64(suite.blockCount)].Height; i++ { + blockId := suite.blockHeadersMap[i].ID() + + // Setup mock storing the transaction error message after retrieving the failed result. + suite.txErrorMessages.On("Exists", blockId).Return(true, nil).Once() + } + + _, err := suite.command.Handler(ctx, req) + suite.Require().NoError(err) + suite.assertAllExpectations() + }) + + suite.Run("happy case, all custom parameters, tx error messages do not exist in db", func() { + // custom parameters + startHeight := 1 + endHeight := 4 + + suite.allENIDs = unittest.IdentityListFixture(3, unittest.WithRole(flow.RoleExecution)) + + executorID := suite.allENIDs[1].ID() + req = &admin.CommandRequest{ + Data: map[string]interface{}{ + "start-height": float64(startHeight), // raw json parses to float64 + "end-height": float64(endHeight), // raw json parses to float64 + "execution-node-ids": []string{executorID.String()}, + }, + } + suite.Require().NoError(suite.command.Validator(req)) + + // Create a mock execution client to simulate communication with execution nodes. + suite.connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &unittestMocks.MockCloser{}, nil) + + for i := startHeight; i <= endHeight; i++ { + blockId := suite.blockHeadersMap[uint64(i)].ID() + + // Setup mock storing the transaction error message after retrieving the failed result. + suite.txErrorMessages.On("Exists", blockId).Return(false, nil).Once() + + results := suite.generateResultsForBlock() + + // Mock the execution node API calls to fetch the error messages. + suite.mockTransactionErrorMessagesResponseByBlockID(blockId, results) + + // Setup mock storing the transaction error message after retrieving the failed result. + suite.mockStoreTxErrorMessages(blockId, results, executorID) + } + + _, err := suite.command.Handler(ctx, req) + suite.Require().NoError(err) + suite.assertAllExpectations() + }) +} + +// TestHandleBackfillTxErrorMessagesErrors tests various error scenarios for the +// Handler method of BackfillTxErrorMessagesCommand to ensure proper error handling +// when prerequisites or dependencies are not met. +// +// It tests various valid cases, such as: +// - Handling of nil txErrorMessagesCore dependency. +// - Failure when retrieving block headers. +func (suite *BackfillTxErrorMessagesSuite) TestHandleBackfillTxErrorMessagesErrors() { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + suite.Run("error when txErrorMessagesCore is nil", func() { + req := &admin.CommandRequest{Data: map[string]interface{}{}} + command := NewBackfillTxErrorMessagesCommand( + suite.state, + nil, + ) + suite.Require().NoError(command.Validator(req)) + + _, err := command.Handler(ctx, req) + assert.Error(suite.T(), err) + assert.Contains(suite.T(), err.Error(), "failed to backfill, could not get transaction error messages storage") + }) + + suite.Run("error when failing to retrieve block header", func() { + req := &admin.CommandRequest{ + Data: map[string]interface{}{ + "start-height": float64(1), // raw json parses to float64 + }, + } + suite.Require().NoError(suite.command.Validator(req)) + + snapNotFound := protocolmock.NewSnapshot(suite.T()) + snapNotFound.On("Head").Return(nil, storage.ErrNotFound).Once() + + suite.state.On("AtHeight", uint64(1)).Return(snapNotFound).Unset() + suite.state.On("AtHeight", uint64(1)).Return(snapNotFound).Once() + + _, err := suite.command.Handler(ctx, req) + assert.Error(suite.T(), err) + assert.Contains(suite.T(), err.Error(), "failed to get block header") + }) +} + +// generateResultsForBlock generates mock transaction results for a block. +// It creates a mix of failed and non-failed transaction results to simulate different transaction outcomes. +func (suite *BackfillTxErrorMessagesSuite) generateResultsForBlock() []flow.LightTransactionResult { + results := make([]flow.LightTransactionResult, 0) + + for i := 0; i < 5; i++ { + results = append(results, flow.LightTransactionResult{ + TransactionID: unittest.IdentifierFixture(), + Failed: i%2 == 0, // create a mix of failed and non-failed transactions + ComputationUsed: 0, + }) + } + + return results +} + +// mockTransactionErrorMessagesResponseByBlockID mocks the response of transaction error messages +// by block ID for failed transactions. It simulates API calls that retrieve error messages from execution nodes. +func (suite *BackfillTxErrorMessagesSuite) mockTransactionErrorMessagesResponseByBlockID( + blockID flow.Identifier, + results []flow.LightTransactionResult, +) { + exeEventReq := &execproto.GetTransactionErrorMessagesByBlockIDRequest{ + BlockId: blockID[:], + } + + exeErrMessagesResp := &execproto.GetTransactionErrorMessagesResponse{} + for i, result := range results { + r := result + if r.Failed { + errMsg := fmt.Sprintf("%s.%s", expectedErrorMsg, r.TransactionID) + exeErrMessagesResp.Results = append(exeErrMessagesResp.Results, &execproto.GetTransactionErrorMessagesResponse_Result{ + TransactionId: r.TransactionID[:], + ErrorMessage: errMsg, + Index: uint32(i), + }) + } + } + + suite.execClient.On("GetTransactionErrorMessagesByBlockID", mock.Anything, exeEventReq). + Return(exeErrMessagesResp, nil). + Once() +} + +// mockStoreTxErrorMessages mocks the process of storing transaction error messages in the database +// after retrieving the results of failed transactions . +func (suite *BackfillTxErrorMessagesSuite) mockStoreTxErrorMessages( + blockID flow.Identifier, + results []flow.LightTransactionResult, + executorID flow.Identifier, +) { + var txErrorMessages []flow.TransactionResultErrorMessage + + for i, result := range results { + r := result + if r.Failed { + errMsg := fmt.Sprintf("%s.%s", expectedErrorMsg, r.TransactionID) + + txErrorMessages = append(txErrorMessages, + flow.TransactionResultErrorMessage{ + TransactionID: result.TransactionID, + ErrorMessage: errMsg, + Index: uint32(i), + ExecutorID: executorID, + }) + } + } + + suite.txErrorMessages.On("Store", blockID, txErrorMessages).Return(nil).Once() +} + +// assertAllExpectations asserts that all the expectations set on various mocks are met, +// ensuring the test results are valid. +func (suite *BackfillTxErrorMessagesSuite) assertAllExpectations() { + suite.snapshot.AssertExpectations(suite.T()) + suite.state.AssertExpectations(suite.T()) + suite.headers.AssertExpectations(suite.T()) + suite.execClient.AssertExpectations(suite.T()) + suite.transactionResults.AssertExpectations(suite.T()) + suite.txErrorMessages.AssertExpectations(suite.T()) +} diff --git a/admin/commands/storage/read_blocks_test.go b/admin/commands/storage/read_blocks_test.go index 334260fe1a8..a2d5e761288 100644 --- a/admin/commands/storage/read_blocks_test.go +++ b/admin/commands/storage/read_blocks_test.go @@ -38,14 +38,14 @@ func TestReadBlocks(t *testing.T) { suite.Run(t, new(ReadBlocksSuite)) } -func createSnapshot(head *flow.Header) protocol.Snapshot { - snapshot := &protocolmock.Snapshot{} +func createSnapshot(t *testing.T, head *flow.Header) *protocolmock.Snapshot { + snapshot := protocolmock.NewSnapshot(t) snapshot.On("Head").Return( func() *flow.Header { return head }, nil, - ) + ).Maybe() return snapshot } @@ -70,13 +70,13 @@ func (suite *ReadBlocksSuite) SetupTest() { suite.sealed = sealed suite.final = final - suite.state.On("Final").Return(createSnapshot(final.Header)) - suite.state.On("Sealed").Return(createSnapshot(sealed.Header)) + suite.state.On("Final").Return(createSnapshot(suite.T(), final.Header)) + suite.state.On("Sealed").Return(createSnapshot(suite.T(), sealed.Header)) suite.state.On("AtBlockID", mock.Anything).Return( func(blockID flow.Identifier) protocol.Snapshot { for _, block := range blocks { if block.ID() == blockID { - return createSnapshot(block.Header) + return createSnapshot(suite.T(), block.Header) } } return invalid.NewSnapshot(fmt.Errorf("invalid block ID: %v", blockID)) @@ -86,7 +86,7 @@ func (suite *ReadBlocksSuite) SetupTest() { func(height uint64) protocol.Snapshot { if int(height) < len(blocks) { block := blocks[height] - return createSnapshot(block.Header) + return createSnapshot(suite.T(), block.Header) } return invalid.NewSnapshot(fmt.Errorf("invalid height: %v", height)) }, diff --git a/admin/commands/storage/read_results_test.go b/admin/commands/storage/read_results_test.go index 44937aaadf8..93c7966a512 100644 --- a/admin/commands/storage/read_results_test.go +++ b/admin/commands/storage/read_results_test.go @@ -93,13 +93,13 @@ func (suite *ReadResultsSuite) SetupTest() { suite.finalResult = finalResult suite.sealedResult = sealedResult - suite.state.On("Final").Return(createSnapshot(final.Header)) - suite.state.On("Sealed").Return(createSnapshot(sealed.Header)) + suite.state.On("Final").Return(createSnapshot(suite.T(), final.Header)) + suite.state.On("Sealed").Return(createSnapshot(suite.T(), sealed.Header)) suite.state.On("AtBlockID", mock.Anything).Return( func(blockID flow.Identifier) protocol.Snapshot { for _, block := range blocks { if block.ID() == blockID { - return createSnapshot(block.Header) + return createSnapshot(suite.T(), block.Header) } } return invalid.NewSnapshot(fmt.Errorf("invalid block ID: %v", blockID)) @@ -109,7 +109,7 @@ func (suite *ReadResultsSuite) SetupTest() { func(height uint64) protocol.Snapshot { if int(height) < len(blocks) { block := blocks[height] - return createSnapshot(block.Header) + return createSnapshot(suite.T(), block.Header) } return invalid.NewSnapshot(fmt.Errorf("invalid height: %v", height)) }, diff --git a/cmd/access/node_builder/access_node_builder.go b/cmd/access/node_builder/access_node_builder.go index 926b25357ff..a8f238ae0a3 100644 --- a/cmd/access/node_builder/access_node_builder.go +++ b/cmd/access/node_builder/access_node_builder.go @@ -27,6 +27,7 @@ import ( "google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials/insecure" + accessNode "github.com/onflow/flow-go/access" "github.com/onflow/flow-go/admin/commands" stateSyncCommands "github.com/onflow/flow-go/admin/commands/state_synchronization" storageCommands "github.com/onflow/flow-go/admin/commands/storage" @@ -44,9 +45,11 @@ import ( "github.com/onflow/flow-go/engine" "github.com/onflow/flow-go/engine/access/index" "github.com/onflow/flow-go/engine/access/ingestion" + "github.com/onflow/flow-go/engine/access/ingestion/tx_error_messages" pingeng "github.com/onflow/flow-go/engine/access/ping" "github.com/onflow/flow-go/engine/access/rest" - "github.com/onflow/flow-go/engine/access/rest/routes" + commonrest "github.com/onflow/flow-go/engine/access/rest/common" + "github.com/onflow/flow-go/engine/access/rest/router" "github.com/onflow/flow-go/engine/access/rpc" "github.com/onflow/flow-go/engine/access/rpc/backend" rpcConnection "github.com/onflow/flow-go/engine/access/rpc/connection" @@ -55,6 +58,8 @@ import ( "github.com/onflow/flow-go/engine/access/subscription" followereng "github.com/onflow/flow-go/engine/common/follower" "github.com/onflow/flow-go/engine/common/requester" + commonrpc "github.com/onflow/flow-go/engine/common/rpc" + "github.com/onflow/flow-go/engine/common/stop" synceng "github.com/onflow/flow-go/engine/common/synchronization" "github.com/onflow/flow-go/engine/common/version" "github.com/onflow/flow-go/engine/execution/computation/query" @@ -148,6 +153,7 @@ type AccessNodeConfig struct { logTxTimeToFinalized bool logTxTimeToExecuted bool logTxTimeToFinalizedExecuted bool + logTxTimeToSealed bool retryEnabled bool rpcMetricsEnabled bool executionDataSyncEnabled bool @@ -161,7 +167,6 @@ type AccessNodeConfig struct { executionDataConfig edrequester.ExecutionDataConfig PublicNetworkConfig PublicNetworkConfig TxResultCacheSize uint - TxErrorMessagesCacheSize uint executionDataIndexingEnabled bool registersDBPath string checkpointFile string @@ -171,8 +176,11 @@ type AccessNodeConfig struct { registerCacheType string registerCacheSize uint programCacheSize uint - checkPayerBalance bool + checkPayerBalanceMode string versionControlEnabled bool + storeTxResultErrorMessages bool + stopControlEnabled bool + registerDBPruneThreshold uint64 } type PublicNetworkConfig struct { @@ -213,10 +221,11 @@ func DefaultAccessNodeConfig() *AccessNodeConfig { TxResultQueryMode: backend.IndexQueryModeExecutionNodesOnly.String(), // default to ENs only for now }, RestConfig: rest.Config{ - ListenAddress: "", - WriteTimeout: rest.DefaultWriteTimeout, - ReadTimeout: rest.DefaultReadTimeout, - IdleTimeout: rest.DefaultIdleTimeout, + ListenAddress: "", + WriteTimeout: rest.DefaultWriteTimeout, + ReadTimeout: rest.DefaultReadTimeout, + IdleTimeout: rest.DefaultIdleTimeout, + MaxRequestSize: commonrest.DefaultMaxRequestSize, }, MaxMsgSize: grpcutils.DefaultMaxMsgSize, CompressorName: grpcutils.NoCompressor, @@ -237,6 +246,7 @@ func DefaultAccessNodeConfig() *AccessNodeConfig { logTxTimeToFinalized: false, logTxTimeToExecuted: false, logTxTimeToFinalizedExecuted: false, + logTxTimeToSealed: false, pingEnabled: false, retryEnabled: false, rpcMetricsEnabled: false, @@ -244,7 +254,6 @@ func DefaultAccessNodeConfig() *AccessNodeConfig { apiRatelimits: nil, apiBurstlimits: nil, TxResultCacheSize: 0, - TxErrorMessagesCacheSize: 1000, PublicNetworkConfig: PublicNetworkConfig{ BindAddress: cmd.NotSet, Metrics: metrics.NewNoopCollector(), @@ -274,8 +283,11 @@ func DefaultAccessNodeConfig() *AccessNodeConfig { registerCacheType: pstorage.CacheTypeTwoQueue.String(), registerCacheSize: 0, programCacheSize: 0, - checkPayerBalance: false, + checkPayerBalanceMode: accessNode.Disabled.String(), versionControlEnabled: true, + storeTxResultErrorMessages: false, + stopControlEnabled: false, + registerDBPruneThreshold: pruner.DefaultThreshold, } } @@ -296,6 +308,7 @@ type FlowAccessNodeBuilder struct { CollectionsToMarkFinalized *stdmap.Times CollectionsToMarkExecuted *stdmap.Times BlocksToMarkExecuted *stdmap.Times + BlockTransactions *stdmap.IdentifierMap TransactionMetrics *metrics.TransactionCollector TransactionValidationMetrics *metrics.TransactionValidationCollector RestMetrics *metrics.RestCollector @@ -325,6 +338,7 @@ type FlowAccessNodeBuilder struct { ExecutionDatastoreManager edstorage.DatastoreManager ExecutionDataTracker tracker.Storage VersionControl *version.VersionControl + StopControl *stop.StopControl // The sync engine participants provider is the libp2p peer store for the access node // which is not available until after the network has started. @@ -344,6 +358,10 @@ type FlowAccessNodeBuilder struct { stateStreamGrpcServer *grpcserver.GrpcServer stateStreamBackend *statestreambackend.StateStreamBackend + nodeBackend *backend.Backend + + ExecNodeIdentitiesProvider *commonrpc.ExecutionNodeIdentitiesProvider + TxResultErrorMessagesCore *tx_error_messages.TxErrorMessagesCore } func (builder *FlowAccessNodeBuilder) buildFollowerState() *FlowAccessNodeBuilder { @@ -900,7 +918,7 @@ func (builder *FlowAccessNodeBuilder) BuildExecutionSyncComponents() *FlowAccess } } - registers, err := pstorage.NewRegisters(pdb) + registers, err := pstorage.NewRegisters(pdb, builder.registerDBPruneThreshold) if err != nil { return nil, fmt.Errorf("could not create registers storage: %w", err) } @@ -994,6 +1012,10 @@ func (builder *FlowAccessNodeBuilder) BuildExecutionSyncComponents() *FlowAccess return nil, err } + if builder.stopControlEnabled { + builder.StopControl.RegisterHeightRecorder(builder.ExecutionIndexer) + } + return builder.ExecutionIndexer, nil }, builder.IndexerDependencies) } @@ -1170,6 +1192,10 @@ func (builder *FlowAccessNodeBuilder) extraFlags() { defaultConfig.rpcConf.RestConfig.ReadTimeout, "timeout to use when reading REST request headers") flags.DurationVar(&builder.rpcConf.RestConfig.IdleTimeout, "rest-idle-timeout", defaultConfig.rpcConf.RestConfig.IdleTimeout, "idle timeout for REST connections") + flags.Int64Var(&builder.rpcConf.RestConfig.MaxRequestSize, + "rest-max-request-size", + defaultConfig.rpcConf.RestConfig.MaxRequestSize, + "the maximum request size in bytes for payload sent over REST server") flags.StringVarP(&builder.rpcConf.CollectionAddr, "static-collection-ingress-addr", "", @@ -1223,6 +1249,10 @@ func (builder *FlowAccessNodeBuilder) extraFlags() { "log-tx-time-to-finalized-executed", defaultConfig.logTxTimeToFinalizedExecuted, "log transaction time to finalized and executed") + flags.BoolVar(&builder.logTxTimeToSealed, + "log-tx-time-to-sealed", + defaultConfig.logTxTimeToSealed, + "log transaction time to sealed") flags.BoolVar(&builder.pingEnabled, "ping-enabled", defaultConfig.pingEnabled, @@ -1230,7 +1260,6 @@ func (builder *FlowAccessNodeBuilder) extraFlags() { flags.BoolVar(&builder.retryEnabled, "retry-enabled", defaultConfig.retryEnabled, "whether to enable the retry mechanism at the access node level") flags.BoolVar(&builder.rpcMetricsEnabled, "rpc-metrics-enabled", defaultConfig.rpcMetricsEnabled, "whether to enable the rpc metrics") flags.UintVar(&builder.TxResultCacheSize, "transaction-result-cache-size", defaultConfig.TxResultCacheSize, "transaction result cache size.(Disabled by default i.e 0)") - flags.UintVar(&builder.TxErrorMessagesCacheSize, "transaction-error-messages-cache-size", defaultConfig.TxErrorMessagesCacheSize, "transaction error messages cache size.(By default 1000)") flags.StringVarP(&builder.nodeInfoFile, "node-info-file", "", @@ -1260,6 +1289,10 @@ func (builder *FlowAccessNodeBuilder) extraFlags() { "version-control-enabled", defaultConfig.versionControlEnabled, "whether to enable the version control feature. Default value is true") + flags.BoolVar(&builder.stopControlEnabled, + "stop-control-enabled", + defaultConfig.stopControlEnabled, + "whether to enable the stop control feature. Default value is false") // ExecutionDataRequester config flags.BoolVar(&builder.executionDataSyncEnabled, "execution-data-sync-enabled", @@ -1360,7 +1393,10 @@ func (builder *FlowAccessNodeBuilder) extraFlags() { "tx-result-query-mode", defaultConfig.rpcConf.BackendConfig.TxResultQueryMode, "mode to use when querying transaction results. one of [local-only, execution-nodes-only(default), failover]") - + flags.BoolVar(&builder.storeTxResultErrorMessages, + "store-tx-result-error-messages", + defaultConfig.storeTxResultErrorMessages, + "whether to enable storing transaction error messages into the db") // Script Execution flags.StringVar(&builder.rpcConf.BackendConfig.ScriptExecutionMode, "script-execution-mode", @@ -1402,10 +1438,18 @@ func (builder *FlowAccessNodeBuilder) extraFlags() { "program-cache-size", defaultConfig.programCacheSize, "[experimental] number of blocks to cache for cadence programs. use 0 to disable cache. default: 0. Note: this is an experimental feature and may cause nodes to become unstable under certain workloads. Use with caution.") - flags.BoolVar(&builder.checkPayerBalance, - "check-payer-balance", - defaultConfig.checkPayerBalance, - "checks that a transaction payer has sufficient balance to pay fees before submitting it to collection nodes") + + // Payer Balance + flags.StringVar(&builder.checkPayerBalanceMode, + "check-payer-balance-mode", + defaultConfig.checkPayerBalanceMode, + "flag for payer balance validation that specifies whether or not to enforce the balance check. one of [disabled(default), warn, enforce]") + + // Register DB Pruning + flags.Uint64Var(&builder.registerDBPruneThreshold, + "registerdb-pruning-threshold", + defaultConfig.registerDBPruneThreshold, + fmt.Sprintf("specifies the number of blocks below the latest stored block height to keep in register db. default: %d", defaultConfig.registerDBPruneThreshold)) }).ValidateFlags(func() error { if builder.supportsObserver && (builder.PublicNetworkConfig.BindAddress == cmd.NotSet || builder.PublicNetworkConfig.BindAddress == "") { return errors.New("public-network-address must be set if supports-observer is true") @@ -1465,14 +1509,15 @@ func (builder *FlowAccessNodeBuilder) extraFlags() { return errors.New("circuit-breaker-restore-timeout must be greater than 0") } } - if builder.TxErrorMessagesCacheSize == 0 { - return errors.New("transaction-error-messages-cache-size must be greater than 0") - } - if builder.checkPayerBalance && !builder.executionDataIndexingEnabled { + if builder.checkPayerBalanceMode != accessNode.Disabled.String() && !builder.executionDataIndexingEnabled { return errors.New("execution-data-indexing-enabled must be set if check-payer-balance is enabled") } + if builder.rpcConf.RestConfig.MaxRequestSize <= 0 { + return errors.New("rest-max-request-size must be greater than 0") + } + return nil }) } @@ -1580,7 +1625,8 @@ func (builder *FlowAccessNodeBuilder) enqueueRelayNetwork() { } func (builder *FlowAccessNodeBuilder) Build() (cmd.Node, error) { - var processedBlockHeight storage.ConsumerProgress + var processedFinalizedBlockHeight storage.ConsumerProgress + var processedTxErrorMessagesBlockHeight storage.ConsumerProgress if builder.executionDataSyncEnabled { builder.BuildExecutionSyncComponents() @@ -1590,6 +1636,8 @@ func (builder *FlowAccessNodeBuilder) Build() (cmd.Node, error) { builder.IndexerDependencies.Add(ingestionDependable) versionControlDependable := module.NewProxiedReadyDoneAware() builder.IndexerDependencies.Add(versionControlDependable) + stopControlDependable := module.NewProxiedReadyDoneAware() + builder.IndexerDependencies.Add(stopControlDependable) var lastFullBlockHeight *counters.PersistentStrictMonotonicCounter builder. @@ -1652,6 +1700,11 @@ func (builder *FlowAccessNodeBuilder) Build() (cmd.Node, error) { return err } + builder.BlockTransactions, err = stdmap.NewIdentifierMap(10000) + if err != nil { + return err + } + builder.BlocksToMarkExecuted, err = stdmap.NewTimes(1 * 300) // assume 1 block per second * 300 seconds return err @@ -1663,6 +1716,7 @@ func (builder *FlowAccessNodeBuilder) Build() (cmd.Node, error) { builder.logTxTimeToFinalized, builder.logTxTimeToExecuted, builder.logTxTimeToFinalizedExecuted, + builder.logTxTimeToSealed, ) return nil }). @@ -1671,7 +1725,7 @@ func (builder *FlowAccessNodeBuilder) Build() (cmd.Node, error) { return nil }). Module("rest metrics", func(node *cmd.NodeConfig) error { - m, err := metrics.NewRestCollector(routes.URLToRoute, node.MetricsRegisterer) + m, err := metrics.NewRestCollector(router.URLToRoute, node.MetricsRegisterer) if err != nil { return err } @@ -1697,6 +1751,7 @@ func (builder *FlowAccessNodeBuilder) Build() (cmd.Node, error) { builder.BlocksToMarkExecuted, builder.Storage.Collections, builder.Storage.Blocks, + builder.BlockTransactions, ) if err != nil { return err @@ -1774,8 +1829,8 @@ func (builder *FlowAccessNodeBuilder) Build() (cmd.Node, error) { builder.TxResultsIndex = index.NewTransactionResultsIndex(builder.Reporter, builder.Storage.LightTransactionResults) return nil }). - Module("processed block height consumer progress", func(node *cmd.NodeConfig) error { - processedBlockHeight = bstorage.NewConsumerProgress(builder.DB, module.ConsumeProgressIngestionEngineBlockHeight) + Module("processed finalized block height consumer progress", func(node *cmd.NodeConfig) error { + processedFinalizedBlockHeight = bstorage.NewConsumerProgress(builder.DB, module.ConsumeProgressIngestionEngineBlockHeight) return nil }). Module("processed last full block height monotonic consumer progress", func(node *cmd.NodeConfig) error { @@ -1792,6 +1847,13 @@ func (builder *FlowAccessNodeBuilder) Build() (cmd.Node, error) { return nil }). + Module("transaction result error messages storage", func(node *cmd.NodeConfig) error { + if builder.storeTxResultErrorMessages { + builder.Storage.TransactionResultErrorMessages = bstorage.NewTransactionResultErrorMessages(node.Metrics.Cache, node.DB, bstorage.DefaultCacheSize) + } + + return nil + }). Component("version control", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { if !builder.versionControlEnabled { noop := &module.NoopReadyDoneAware{} @@ -1824,6 +1886,24 @@ func (builder *FlowAccessNodeBuilder) Build() (cmd.Node, error) { return versionControl, nil }). + Component("stop control", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { + if !builder.stopControlEnabled { + noop := &module.NoopReadyDoneAware{} + stopControlDependable.Init(noop) + return noop, nil + } + + stopControl := stop.NewStopControl( + builder.Logger, + ) + + builder.VersionControl.AddVersionUpdatesConsumer(stopControl.OnVersionUpdate) + + builder.StopControl = stopControl + stopControlDependable.Init(builder.StopControl) + + return stopControl, nil + }). Component("RPC engine", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { config := builder.rpcConf backendConfig := config.BackendConfig @@ -1889,33 +1969,61 @@ func (builder *FlowAccessNodeBuilder) Build() (cmd.Node, error) { return nil, fmt.Errorf("transaction result query mode 'compare' is not supported") } - nodeBackend, err := backend.New(backend.Params{ - State: node.State, - CollectionRPC: builder.CollectionRPC, - HistoricalAccessNodes: builder.HistoricalAccessRPCs, - Blocks: node.Storage.Blocks, - Headers: node.Storage.Headers, - Collections: node.Storage.Collections, - Transactions: node.Storage.Transactions, - ExecutionReceipts: node.Storage.Receipts, - ExecutionResults: node.Storage.Results, - ChainID: node.RootChainID, - AccessMetrics: builder.AccessMetrics, - ConnFactory: connFactory, - RetryEnabled: builder.retryEnabled, - MaxHeightRange: backendConfig.MaxHeightRange, - PreferredExecutionNodeIDs: backendConfig.PreferredExecutionNodeIDs, - FixedExecutionNodeIDs: backendConfig.FixedExecutionNodeIDs, - Log: node.Logger, - SnapshotHistoryLimit: backend.DefaultSnapshotHistoryLimit, - Communicator: backend.NewNodeCommunicator(backendConfig.CircuitBreakerConfig.Enabled), - TxResultCacheSize: builder.TxResultCacheSize, - TxErrorMessagesCacheSize: builder.TxErrorMessagesCacheSize, - ScriptExecutor: builder.ScriptExecutor, - ScriptExecutionMode: scriptExecMode, - CheckPayerBalance: builder.checkPayerBalance, - EventQueryMode: eventQueryMode, - BlockTracker: blockTracker, + // If execution data syncing and indexing is disabled, pass nil indexReporter + var indexReporter state_synchronization.IndexReporter + if builder.executionDataSyncEnabled && builder.executionDataIndexingEnabled { + indexReporter = builder.Reporter + } + + checkPayerBalanceMode, err := accessNode.ParsePayerBalanceMode(builder.checkPayerBalanceMode) + if err != nil { + return nil, fmt.Errorf("could not parse payer balance mode: %w", err) + + } + + preferredENIdentifiers, err := commonrpc.IdentifierList(backendConfig.PreferredExecutionNodeIDs) + if err != nil { + return nil, fmt.Errorf("failed to convert node id string to Flow Identifier for preferred EN map: %w", err) + } + + fixedENIdentifiers, err := commonrpc.IdentifierList(backendConfig.FixedExecutionNodeIDs) + if err != nil { + return nil, fmt.Errorf("failed to convert node id string to Flow Identifier for fixed EN map: %w", err) + } + + builder.ExecNodeIdentitiesProvider = commonrpc.NewExecutionNodeIdentitiesProvider( + node.Logger, + node.State, + node.Storage.Receipts, + preferredENIdentifiers, + fixedENIdentifiers, + ) + + builder.nodeBackend, err = backend.New(backend.Params{ + State: node.State, + CollectionRPC: builder.CollectionRPC, + HistoricalAccessNodes: builder.HistoricalAccessRPCs, + Blocks: node.Storage.Blocks, + Headers: node.Storage.Headers, + Collections: node.Storage.Collections, + Transactions: node.Storage.Transactions, + ExecutionReceipts: node.Storage.Receipts, + ExecutionResults: node.Storage.Results, + TxResultErrorMessages: node.Storage.TransactionResultErrorMessages, + ChainID: node.RootChainID, + AccessMetrics: builder.AccessMetrics, + ConnFactory: connFactory, + RetryEnabled: builder.retryEnabled, + MaxHeightRange: backendConfig.MaxHeightRange, + Log: node.Logger, + SnapshotHistoryLimit: backend.DefaultSnapshotHistoryLimit, + Communicator: backend.NewNodeCommunicator(backendConfig.CircuitBreakerConfig.Enabled), + TxResultCacheSize: builder.TxResultCacheSize, + ScriptExecutor: builder.ScriptExecutor, + ScriptExecutionMode: scriptExecMode, + CheckPayerBalanceMode: checkPayerBalanceMode, + EventQueryMode: eventQueryMode, + BlockTracker: blockTracker, SubscriptionHandler: subscription.NewSubscriptionHandler( builder.Logger, broadcaster, @@ -1923,22 +2031,18 @@ func (builder *FlowAccessNodeBuilder) Build() (cmd.Node, error) { builder.stateStreamConf.ResponseLimit, builder.stateStreamConf.ClientSendBufferSize, ), - EventsIndex: builder.EventsIndex, - TxResultQueryMode: txResultQueryMode, - TxResultsIndex: builder.TxResultsIndex, - LastFullBlockHeight: lastFullBlockHeight, - VersionControl: builder.VersionControl, + EventsIndex: builder.EventsIndex, + TxResultQueryMode: txResultQueryMode, + TxResultsIndex: builder.TxResultsIndex, + LastFullBlockHeight: lastFullBlockHeight, + IndexReporter: indexReporter, + VersionControl: builder.VersionControl, + ExecNodeIdentitiesProvider: builder.ExecNodeIdentitiesProvider, }) if err != nil { return nil, fmt.Errorf("could not initialize backend: %w", err) } - // If execution data syncing and indexing is disabled, pass nil indexReporter - var indexReporter state_synchronization.IndexReporter - if builder.executionDataSyncEnabled && builder.executionDataIndexingEnabled { - indexReporter = builder.Reporter - } - engineBuilder, err := rpc.NewBuilder( node.Logger, node.State, @@ -1947,8 +2051,8 @@ func (builder *FlowAccessNodeBuilder) Build() (cmd.Node, error) { builder.AccessMetrics, builder.rpcMetricsEnabled, builder.Me, - nodeBackend, - nodeBackend, + builder.nodeBackend, + builder.nodeBackend, builder.secureGrpcServer, builder.unsecureGrpcServer, builder.stateStreamBackend, @@ -1987,6 +2091,15 @@ func (builder *FlowAccessNodeBuilder) Build() (cmd.Node, error) { return nil, fmt.Errorf("could not create requester engine: %w", err) } + if builder.storeTxResultErrorMessages { + builder.TxResultErrorMessagesCore = tx_error_messages.NewTxErrorMessagesCore( + node.Logger, + builder.nodeBackend, + node.Storage.TransactionResultErrorMessages, + builder.ExecNodeIdentitiesProvider, + ) + } + builder.IngestEng, err = ingestion.New( node.Logger, node.EngineRegistry, @@ -2000,8 +2113,9 @@ func (builder *FlowAccessNodeBuilder) Build() (cmd.Node, error) { node.Storage.Results, node.Storage.Receipts, builder.collectionExecutedMetric, - processedBlockHeight, + processedFinalizedBlockHeight, lastFullBlockHeight, + builder.TxResultErrorMessagesCore, ) if err != nil { return nil, err @@ -2017,8 +2131,39 @@ func (builder *FlowAccessNodeBuilder) Build() (cmd.Node, error) { // order for it to properly start and shut down, we should still return it as its own engine here, so it can // be handled by the scaffold. return builder.RequestEng, nil + }). + AdminCommand("backfill-tx-error-messages", func(config *cmd.NodeConfig) commands.AdminCommand { + return storageCommands.NewBackfillTxErrorMessagesCommand( + builder.State, + builder.TxResultErrorMessagesCore, + ) }) + if builder.storeTxResultErrorMessages { + builder.Module("processed error messages block height consumer progress", func(node *cmd.NodeConfig) error { + processedTxErrorMessagesBlockHeight = bstorage.NewConsumerProgress( + builder.DB, + module.ConsumeProgressEngineTxErrorMessagesBlockHeight, + ) + return nil + }) + builder.Component("transaction result error messages engine", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { + engine, err := tx_error_messages.New( + node.Logger, + node.State, + node.Storage.Headers, + processedTxErrorMessagesBlockHeight, + builder.TxResultErrorMessagesCore, + ) + if err != nil { + return nil, err + } + builder.FollowerDistributor.AddOnBlockFinalizedConsumer(engine.OnFinalizedBlock) + + return engine, nil + }) + } + if builder.supportsObserver { builder.Component("public sync request handler", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { syncRequestHandler, err := synceng.NewRequestHandlerEngine( diff --git a/cmd/bootstrap/cmd/final_list.go b/cmd/bootstrap/cmd/final_list.go index ca34739de2a..cc41c741881 100644 --- a/cmd/bootstrap/cmd/final_list.go +++ b/cmd/bootstrap/cmd/final_list.go @@ -62,13 +62,16 @@ func finalList(cmd *cobra.Command, args []string) { registeredNodes := readStakingContractDetails() // merge internal and partner node infos (from local files) - localNodes := mergeNodeInfos(internalNodes, partnerNodes) + localNodes, err := mergeNodeInfos(internalNodes, partnerNodes) + if err != nil { + log.Fatal().Err(err).Msg("failed to merge node infos") + } // reconcile nodes from staking contract nodes validateNodes(localNodes, registeredNodes) // write node-config.json with the new list of nodes to be used for the `finalize` command - err := common.WriteJSON(model.PathFinallist, flagOutdir, model.ToPublicNodeInfoList(localNodes)) + err = common.WriteJSON(model.PathFinallist, flagOutdir, model.ToPublicNodeInfoList(localNodes)) if err != nil { log.Fatal().Err(err).Msg("failed to write json") } diff --git a/cmd/bootstrap/cmd/finalize.go b/cmd/bootstrap/cmd/finalize.go index 0578a3dcebc..b935a2ac86f 100644 --- a/cmd/bootstrap/cmd/finalize.go +++ b/cmd/bootstrap/cmd/finalize.go @@ -132,7 +132,10 @@ func finalize(cmd *cobra.Command, args []string) { log.Info().Msg("") log.Info().Msg("assembling network and staking keys") - stakingNodes := mergeNodeInfos(internalNodes, partnerNodes) + stakingNodes, err := mergeNodeInfos(internalNodes, partnerNodes) + if err != nil { + log.Fatal().Err(err).Msgf("failed to merge internal and partner nodes: %v", err) + } log.Info().Msg("") // create flow.IdentityList representation of participant set @@ -312,29 +315,31 @@ func readRootBlockVotes() []*hotstuff.Vote { // // IMPORTANT: node infos are returned in the canonical ordering, meaning this // is safe to use as the input to the DKG and protocol state. -func mergeNodeInfos(internalNodes, partnerNodes []model.NodeInfo) []model.NodeInfo { +func mergeNodeInfos(internalNodes, partnerNodes []model.NodeInfo) ([]model.NodeInfo, error) { nodes := append(internalNodes, partnerNodes...) // test for duplicate Addresses addressLookup := make(map[string]struct{}) for _, node := range nodes { if _, ok := addressLookup[node.Address]; ok { - log.Fatal().Str("address", node.Address).Msg("duplicate node address") + return nil, fmt.Errorf("duplicate node address: %v", node.Address) } + addressLookup[node.Address] = struct{}{} } // test for duplicate node IDs idLookup := make(map[flow.Identifier]struct{}) for _, node := range nodes { if _, ok := idLookup[node.NodeID]; ok { - log.Fatal().Str("NodeID", node.NodeID.String()).Msg("duplicate node ID") + return nil, fmt.Errorf("duplicate node ID: %v", node.NodeID.String()) } + idLookup[node.NodeID] = struct{}{} } // sort nodes using the canonical ordering nodes = model.Sort(nodes, flow.Canonical[flow.Identity]) - return nodes + return nodes, nil } // readRootBlock reads root block data from disc, this file needs to be prepared with diff --git a/cmd/bootstrap/cmd/finalize_test.go b/cmd/bootstrap/cmd/finalize_test.go index 1f7fee3f2c0..1edb039e2a3 100644 --- a/cmd/bootstrap/cmd/finalize_test.go +++ b/cmd/bootstrap/cmd/finalize_test.go @@ -190,3 +190,30 @@ func checkClusterConstraint(clusters flow.ClusterList, partnersInfo []model.Node } return true } + +func TestMergeNodeInfos(t *testing.T) { + partnersLen := 7 + internalLen := 22 + partners := unittest.NodeInfosFixture(partnersLen, unittest.WithRole(flow.RoleCollection)) + internals := unittest.NodeInfosFixture(internalLen, unittest.WithRole(flow.RoleCollection)) + + // Check if there is no overlap, then should pass + merged, err := mergeNodeInfos(partners, internals) + require.NoError(t, err) + require.Len(t, merged, partnersLen+internalLen) + + // Check if internals and partners have overlap, then should fail + internalAndPartnersHaveOverlap := append(partners, internals[0]) + _, err = mergeNodeInfos(internalAndPartnersHaveOverlap, internals) + require.Error(t, err) + + // Check if partners have overlap, then should fail + partnersHaveOverlap := append(partners, partners[0]) + _, err = mergeNodeInfos(partnersHaveOverlap, internals) + require.Error(t, err) + + // Check if internals have overlap, then should fail + internalsHaveOverlap := append(internals, internals[0]) + _, err = mergeNodeInfos(partners, internalsHaveOverlap) + require.Error(t, err) +} diff --git a/cmd/bootstrap/cmd/rootblock.go b/cmd/bootstrap/cmd/rootblock.go index 505d3239024..43fff799969 100644 --- a/cmd/bootstrap/cmd/rootblock.go +++ b/cmd/bootstrap/cmd/rootblock.go @@ -145,8 +145,13 @@ func rootBlock(cmd *cobra.Command, args []string) { log.Fatal().Err(err).Msg("invalid epoch timing config") } + // Read partner node's information and internal node's information. + // With "internal nodes" we reference nodes, whose private keys we have. In comparison, + // for "partner nodes" we generally do not have their keys. However, we allow some overlap, + // in that we tolerate a configuration where information about an "internal node" is also + // duplicated in the list of "partner nodes". log.Info().Msg("collecting partner network and staking keys") - partnerNodes, err := common.ReadFullPartnerNodeInfos(log, flagPartnerWeights, flagPartnerNodeInfoDir) + rawPartnerNodes, err := common.ReadFullPartnerNodeInfos(log, flagPartnerWeights, flagPartnerNodeInfoDir) if err != nil { log.Fatal().Err(err).Msg("failed to read full partner node infos") } @@ -160,12 +165,23 @@ func rootBlock(cmd *cobra.Command, args []string) { log.Info().Msg("") + // we now convert to the strict meaning of: "internal nodes" vs "partner nodes" + // • "internal nodes" we have they private keys for + // • "partner nodes" we don't have the keys for + // • both sets are disjoint (no common nodes) + log.Info().Msg("remove internal partner nodes") + partnerNodes := common.FilterInternalPartners(rawPartnerNodes, internalNodes) + log.Info().Msgf("removed %d internal partner nodes", len(rawPartnerNodes)-len(partnerNodes)) + log.Info().Msg("checking constraints on consensus nodes") checkConstraints(partnerNodes, internalNodes) log.Info().Msg("") log.Info().Msg("assembling network and staking keys") - stakingNodes := mergeNodeInfos(internalNodes, partnerNodes) + stakingNodes, err := mergeNodeInfos(internalNodes, partnerNodes) + if err != nil { + log.Fatal().Err(err).Msgf("failed to merge node infos") + } err = common.WriteJSON(model.PathNodeInfosPub, flagOutdir, model.ToPublicNodeInfoList(stakingNodes)) if err != nil { log.Fatal().Err(err).Msg("failed to write json") diff --git a/cmd/bootstrap/cmd/rootblock_test.go b/cmd/bootstrap/cmd/rootblock_test.go index 01222c0c476..8c3080d0b11 100644 --- a/cmd/bootstrap/cmd/rootblock_test.go +++ b/cmd/bootstrap/cmd/rootblock_test.go @@ -21,7 +21,10 @@ const rootBlockHappyPathLogs = "collecting partner network and staking keys" + `read \d+ internal private node-info files` + `read internal node configurations` + `read \d+ weights for internal nodes` + + `remove internal partner nodes` + + `removed 0 internal partner nodes` + `checking constraints on consensus nodes` + + `assembling network and staking keys` + `wrote file \S+/node-infos.pub.json` + `running DKG for consensus nodes` + diff --git a/cmd/bootstrap/run/qc.go b/cmd/bootstrap/run/qc.go index 4a31a125ff3..6f1c4dcdf29 100644 --- a/cmd/bootstrap/run/qc.go +++ b/cmd/bootstrap/run/qc.go @@ -169,7 +169,7 @@ func GenerateQCParticipantData(allNodes, internalNodes []bootstrap.NodeInfo, dkg // length of DKG participants needs to match stakingNodes, since we run DKG for external and internal validators if len(allNodes) != len(dkgData.PrivKeyShares) { - return nil, fmt.Errorf("need exactly the same number of staking public keys as DKG private participants") + return nil, fmt.Errorf("need exactly the same number of staking public keys as DKG private participants, (all=%d, dkg=%d)", len(allNodes), len(dkgData.PrivKeyShares)) } qcData := &ParticipantData{} diff --git a/cmd/bootstrap/utils/key_generation.go b/cmd/bootstrap/utils/key_generation.go index fd4c8c53444..6cb662be21c 100644 --- a/cmd/bootstrap/utils/key_generation.go +++ b/cmd/bootstrap/utils/key_generation.go @@ -13,6 +13,7 @@ import ( sdk "github.com/onflow/flow-go-sdk" sdkcrypto "github.com/onflow/flow-go-sdk/crypto" + "github.com/onflow/flow-go/fvm/systemcontracts" "github.com/onflow/flow-go/model/bootstrap" model "github.com/onflow/flow-go/model/bootstrap" diff --git a/cmd/bootstrap/utils/md5.go b/cmd/bootstrap/utils/md5.go index 3abe9c42948..65823fd6e96 100644 --- a/cmd/bootstrap/utils/md5.go +++ b/cmd/bootstrap/utils/md5.go @@ -1,9 +1,8 @@ package utils // The google storage API only provides md5 and crc32 hence overriding the linter flag for md5 -// #nosec import ( - "crypto/md5" + "crypto/md5" //nolint:gosec "io" "os" ) diff --git a/cmd/collection/main.go b/cmd/collection/main.go index c3d1496918a..1a241ba703b 100644 --- a/cmd/collection/main.go +++ b/cmd/collection/main.go @@ -9,6 +9,7 @@ import ( client "github.com/onflow/flow-go-sdk/access/grpc" sdkcrypto "github.com/onflow/flow-go-sdk/crypto" + "github.com/onflow/flow-go/admin/commands" collectionCommands "github.com/onflow/flow-go/admin/commands/collection" storageCommands "github.com/onflow/flow-go/admin/commands/storage" diff --git a/cmd/consensus/main.go b/cmd/consensus/main.go index 874385b2b3f..616e7657d10 100644 --- a/cmd/consensus/main.go +++ b/cmd/consensus/main.go @@ -12,6 +12,7 @@ import ( client "github.com/onflow/flow-go-sdk/access/grpc" "github.com/onflow/flow-go-sdk/crypto" + "github.com/onflow/flow-go/cmd" "github.com/onflow/flow-go/cmd/util/cmd/common" "github.com/onflow/flow-go/consensus" diff --git a/cmd/execution_builder.go b/cmd/execution_builder.go index 86b74a8be6f..c9c7899270f 100644 --- a/cmd/execution_builder.go +++ b/cmd/execution_builder.go @@ -49,9 +49,9 @@ import ( "github.com/onflow/flow-go/engine/execution/checker" "github.com/onflow/flow-go/engine/execution/computation" "github.com/onflow/flow-go/engine/execution/computation/committer" + txmetrics "github.com/onflow/flow-go/engine/execution/computation/metrics" "github.com/onflow/flow-go/engine/execution/ingestion" "github.com/onflow/flow-go/engine/execution/ingestion/fetcher" - "github.com/onflow/flow-go/engine/execution/ingestion/loader" "github.com/onflow/flow-go/engine/execution/ingestion/stop" "github.com/onflow/flow-go/engine/execution/ingestion/uploader" exeprovider "github.com/onflow/flow-go/engine/execution/provider" @@ -127,7 +127,7 @@ type ExecutionNode struct { ingestionUnit *engine.Unit - collector module.ExecutionMetrics + collector *metrics.ExecutionCollector executionState state.ExecutionState followerState protocol.FollowerState committee hotstuff.DynamicCommittee @@ -160,6 +160,7 @@ type ExecutionNode struct { executionDataTracker tracker.Storage blobService network.BlobService blobserviceDependable *module.ProxiedReadyDoneAware + metricsProvider txmetrics.TransactionExecutionMetricsProvider } func (builder *ExecutionNodeBuilder) LoadComponentsAndModules() { @@ -228,6 +229,7 @@ func (builder *ExecutionNodeBuilder) LoadComponentsAndModules() { Component("block data upload manager", exeNode.LoadBlockUploaderManager). Component("GCP block data uploader", exeNode.LoadGCPBlockDataUploader). Component("S3 block data uploader", exeNode.LoadS3BlockDataUploader). + Component("transaction execution metrics", exeNode.LoadTransactionExecutionMetrics). Component("provider engine", exeNode.LoadProviderEngine). Component("checker engine", exeNode.LoadCheckerEngine). Component("ingestion engine", exeNode.LoadIngestionEngine). @@ -544,10 +546,27 @@ func (exeNode *ExecutionNode) LoadProviderEngine( vmCtx := fvm.NewContext(opts...) + var collector module.ExecutionMetrics + collector = exeNode.collector + if exeNode.exeConf.transactionExecutionMetricsEnabled { + // inject the transaction execution metrics + collector = exeNode.collector.WithTransactionCallback( + func(dur time.Duration, stats module.TransactionExecutionResultStats, info module.TransactionExecutionResultInfo) { + exeNode.metricsProvider.Collect( + info.BlockID, + info.BlockHeight, + txmetrics.TransactionExecutionMetrics{ + TransactionID: info.TransactionID, + ExecutionTime: dur, + ExecutionEffortWeights: stats.ComputationIntensities, + }) + }) + } + ledgerViewCommitter := committer.NewLedgerViewCommitter(exeNode.ledgerStorage, node.Tracer) manager, err := computation.New( node.Logger, - exeNode.collector, + collector, node.Tracer, node.Me, node.State, @@ -869,7 +888,7 @@ func (exeNode *ExecutionNode) LoadRegisterStore( return fmt.Errorf("could not import registers from checkpoint: %w", err) } } - diskStore, err := storagepebble.NewRegisters(pebbledb) + diskStore, err := storagepebble.NewRegisters(pebbledb, storagepebble.PruningDisabled) if err != nil { return fmt.Errorf("could not create registers storage: %w", err) } @@ -1048,6 +1067,9 @@ func (exeNode *ExecutionNode) LoadIngestionEngine( // consistency of collection can be checked by checking hash, and hash comes from trusted source (blocks from consensus follower) // hence we not need to check origin requester.WithValidateStaking(false), + // we have observed execution nodes occasionally fail to retrieve collections using this engine, which can cause temporary execution halts + // setting a retry maximum of 10s results in a much faster recovery from these faults (default is 2m) + requester.WithRetryMaximum(10*time.Second), ) if err != nil { @@ -1058,61 +1080,24 @@ func (exeNode *ExecutionNode) LoadIngestionEngine( exeNode.collectionRequester = reqEng } - if exeNode.exeConf.enableNewIngestionEngine { - _, core, err := ingestion.NewMachine( - node.Logger, - node.ProtocolEvents, - exeNode.collectionRequester, - colFetcher, - node.Storage.Headers, - node.Storage.Blocks, - node.Storage.Collections, - exeNode.executionState, - node.State, - exeNode.collector, - exeNode.computationManager, - exeNode.providerEngine, - exeNode.blockDataUploader, - exeNode.stopControl, - ) - - return core, err - } - - var blockLoader ingestion.BlockLoader - if exeNode.exeConf.enableStorehouse { - blockLoader = loader.NewUnfinalizedLoader(node.Logger, node.State, node.Storage.Headers, exeNode.executionState) - } else { - blockLoader = loader.NewUnexecutedLoader(node.Logger, node.State, node.Storage.Headers, exeNode.executionState) - } - - ingestionEng, err := ingestion.New( - exeNode.ingestionUnit, + _, core, err := ingestion.NewMachine( node.Logger, - node.EngineRegistry, + node.ProtocolEvents, + exeNode.collectionRequester, colFetcher, node.Storage.Headers, node.Storage.Blocks, node.Storage.Collections, - exeNode.computationManager, - exeNode.providerEngine, exeNode.executionState, + node.State, exeNode.collector, - node.Tracer, - exeNode.exeConf.extensiveLog, - exeNode.executionDataPruner, + exeNode.computationManager, + exeNode.providerEngine, exeNode.blockDataUploader, exeNode.stopControl, - blockLoader, ) - // TODO: we should solve these mutual dependencies better - // => https://github.com/dapperlabs/flow-go/issues/4360 - exeNode.collectionRequester.WithHandle(ingestionEng.OnCollection) - - node.ProtocolEvents.AddConsumer(ingestionEng) - - return ingestionEng, err + return core, err } // create scripts engine for handling script execution @@ -1127,6 +1112,24 @@ func (exeNode *ExecutionNode) LoadScriptsEngine(node *NodeConfig) (module.ReadyD return exeNode.scriptsEng, nil } +func (exeNode *ExecutionNode) LoadTransactionExecutionMetrics( + node *NodeConfig, +) (module.ReadyDoneAware, error) { + lastFinalizedHeader := node.LastFinalizedHeader + + metricsProvider := txmetrics.NewTransactionExecutionMetricsProvider( + node.Logger, + exeNode.executionState, + node.Storage.Headers, + lastFinalizedHeader.Height, + exeNode.exeConf.transactionExecutionMetricsBufferSize, + ) + + node.ProtocolEvents.AddConsumer(metricsProvider) + exeNode.metricsProvider = metricsProvider + return metricsProvider, nil +} + func (exeNode *ExecutionNode) LoadConsensusCommittee( node *NodeConfig, ) ( @@ -1328,6 +1331,7 @@ func (exeNode *ExecutionNode) LoadGrpcServer( exeNode.results, exeNode.txResults, node.Storage.Commits, + exeNode.metricsProvider, node.RootChainID, signature.NewBlockSignerDecoder(exeNode.committee), exeNode.exeConf.apiRatelimits, diff --git a/cmd/execution_config.go b/cmd/execution_config.go index 4a65850c789..97f808ae6e7 100644 --- a/cmd/execution_config.go +++ b/cmd/execution_config.go @@ -25,35 +25,37 @@ import ( // ExecutionConfig contains the configs for starting up execution nodes type ExecutionConfig struct { - rpcConf rpc.Config - triedir string - executionDataDir string - registerDir string - mTrieCacheSize uint32 - transactionResultsCacheSize uint - checkpointDistance uint - checkpointsToKeep uint - chunkDataPackDir string - chunkDataPackCacheSize uint - chunkDataPackRequestsCacheSize uint32 - requestInterval time.Duration - extensiveLog bool - pauseExecution bool - chunkDataPackQueryTimeout time.Duration - chunkDataPackDeliveryTimeout time.Duration - enableBlockDataUpload bool - gcpBucketName string - s3BucketName string - apiRatelimits map[string]int - apiBurstlimits map[string]int - executionDataAllowedPeers string - executionDataPrunerHeightRangeTarget uint64 - executionDataPrunerThreshold uint64 - blobstoreRateLimit int - blobstoreBurstLimit int - chunkDataPackRequestWorkers uint - maxGracefulStopDuration time.Duration - importCheckpointWorkerCount int + rpcConf rpc.Config + triedir string + executionDataDir string + registerDir string + mTrieCacheSize uint32 + transactionResultsCacheSize uint + checkpointDistance uint + checkpointsToKeep uint + chunkDataPackDir string + chunkDataPackCacheSize uint + chunkDataPackRequestsCacheSize uint32 + requestInterval time.Duration + extensiveLog bool + pauseExecution bool + chunkDataPackQueryTimeout time.Duration + chunkDataPackDeliveryTimeout time.Duration + enableBlockDataUpload bool + gcpBucketName string + s3BucketName string + apiRatelimits map[string]int + apiBurstlimits map[string]int + executionDataAllowedPeers string + executionDataPrunerHeightRangeTarget uint64 + executionDataPrunerThreshold uint64 + blobstoreRateLimit int + blobstoreBurstLimit int + chunkDataPackRequestWorkers uint + maxGracefulStopDuration time.Duration + importCheckpointWorkerCount int + transactionExecutionMetricsEnabled bool + transactionExecutionMetricsBufferSize uint // evm tracing configuration evmTracingEnabled bool @@ -67,11 +69,10 @@ type ExecutionConfig struct { // It works around an issue where some collection nodes are not configured with enough // this works around an issue where some collection nodes are not configured with enough // file descriptors causing connection failures. - onflowOnlyLNs bool - enableStorehouse bool - enableChecker bool - enableNewIngestionEngine bool - publicAccessID string + onflowOnlyLNs bool + enableStorehouse bool + enableChecker bool + publicAccessID string } func (exeConf *ExecutionConfig) SetupFlags(flags *pflag.FlagSet) { @@ -122,13 +123,17 @@ func (exeConf *ExecutionConfig) SetupFlags(flags *pflag.FlagSet) { flags.IntVar(&exeConf.blobstoreBurstLimit, "blobstore-burst-limit", 0, "outgoing burst limit for Execution Data blobstore") flags.DurationVar(&exeConf.maxGracefulStopDuration, "max-graceful-stop-duration", stop.DefaultMaxGracefulStopDuration, "the maximum amount of time stop control will wait for ingestion engine to gracefully shutdown before crashing") flags.IntVar(&exeConf.importCheckpointWorkerCount, "import-checkpoint-worker-count", 10, "number of workers to import checkpoint file during bootstrap") + flags.BoolVar(&exeConf.transactionExecutionMetricsEnabled, "tx-execution-metrics", true, "enable collection of transaction execution metrics") + flags.UintVar(&exeConf.transactionExecutionMetricsBufferSize, "tx-execution-metrics-buffer-size", 200, "buffer size for transaction execution metrics. The buffer size is the number of blocks that are kept in memory by the metrics provider engine") flags.BoolVar(&exeConf.evmTracingEnabled, "evm-tracing-enabled", false, "enable EVM tracing, when set it will generate traces and upload them to the GCP bucket provided by the --evm-traces-gcp-bucket. Warning: this might affect speed of execution") flags.StringVar(&exeConf.evmTracesGCPBucket, "evm-traces-gcp-bucket", "", "define GCP bucket name used for uploading EVM traces, must be used in combination with --evm-tracing-enabled. if left empty the upload step is skipped") flags.BoolVar(&exeConf.onflowOnlyLNs, "temp-onflow-only-lns", false, "do not use unless required. forces node to only request collections from onflow collection nodes") flags.BoolVar(&exeConf.enableStorehouse, "enable-storehouse", false, "enable storehouse to store registers on disk, default is false") flags.BoolVar(&exeConf.enableChecker, "enable-checker", true, "enable checker to check the correctness of the execution result, default is true") - flags.BoolVar(&exeConf.enableNewIngestionEngine, "enable-new-ingestion-engine", true, "enable new ingestion engine, default is true") + // deprecated. Retain it to prevent nodes that previously had this configuration from crashing. + var deprecatedEnableNewIngestionEngine bool + flags.BoolVar(&deprecatedEnableNewIngestionEngine, "enable-new-ingestion-engine", true, "enable new ingestion engine, default is true") flags.StringVar(&exeConf.publicAccessID, "public-access-id", "", "public access ID for the node") } diff --git a/cmd/observer/node_builder/observer_builder.go b/cmd/observer/node_builder/observer_builder.go index 6a75ded5de7..21fe924ac0d 100644 --- a/cmd/observer/node_builder/observer_builder.go +++ b/cmd/observer/node_builder/observer_builder.go @@ -19,10 +19,7 @@ import ( "github.com/ipfs/go-cid" "github.com/ipfs/go-datastore" badgerds "github.com/ipfs/go-ds-badger2" - dht "github.com/libp2p/go-libp2p-kad-dht" - "github.com/libp2p/go-libp2p/core/host" "github.com/libp2p/go-libp2p/core/peer" - "github.com/libp2p/go-libp2p/core/routing" "github.com/onflow/crypto" "github.com/rs/zerolog" "github.com/spf13/pflag" @@ -46,7 +43,8 @@ import ( "github.com/onflow/flow-go/engine/access/index" "github.com/onflow/flow-go/engine/access/rest" restapiproxy "github.com/onflow/flow-go/engine/access/rest/apiproxy" - "github.com/onflow/flow-go/engine/access/rest/routes" + commonrest "github.com/onflow/flow-go/engine/access/rest/common" + "github.com/onflow/flow-go/engine/access/rest/router" "github.com/onflow/flow-go/engine/access/rpc" "github.com/onflow/flow-go/engine/access/rpc/backend" rpcConnection "github.com/onflow/flow-go/engine/access/rpc/connection" @@ -54,6 +52,8 @@ import ( statestreambackend "github.com/onflow/flow-go/engine/access/state_stream/backend" "github.com/onflow/flow-go/engine/access/subscription" "github.com/onflow/flow-go/engine/common/follower" + commonrpc "github.com/onflow/flow-go/engine/common/rpc" + "github.com/onflow/flow-go/engine/common/stop" synceng "github.com/onflow/flow-go/engine/common/synchronization" "github.com/onflow/flow-go/engine/common/version" "github.com/onflow/flow-go/engine/execution/computation/query" @@ -91,17 +91,12 @@ import ( "github.com/onflow/flow-go/network/converter" "github.com/onflow/flow-go/network/p2p" "github.com/onflow/flow-go/network/p2p/blob" - p2pbuilder "github.com/onflow/flow-go/network/p2p/builder" - p2pbuilderconfig "github.com/onflow/flow-go/network/p2p/builder/config" "github.com/onflow/flow-go/network/p2p/cache" "github.com/onflow/flow-go/network/p2p/conduit" - p2pdht "github.com/onflow/flow-go/network/p2p/dht" "github.com/onflow/flow-go/network/p2p/keyutils" p2plogging "github.com/onflow/flow-go/network/p2p/logging" - networkingsubscription "github.com/onflow/flow-go/network/p2p/subscription" "github.com/onflow/flow-go/network/p2p/translator" "github.com/onflow/flow-go/network/p2p/unicast/protocols" - "github.com/onflow/flow-go/network/p2p/utils" "github.com/onflow/flow-go/network/slashing" "github.com/onflow/flow-go/network/underlay" "github.com/onflow/flow-go/network/validator" @@ -136,8 +131,6 @@ import ( // For a node running as a standalone process, the config fields will be populated from the command line params, // while for a node running as a library, the config fields are expected to be initialized by the caller. type ObserverServiceConfig struct { - bootstrapNodeAddresses []string - bootstrapNodePublicKeys []string observerNetworkingKeyPath string bootstrapIdentities flow.IdentitySkeletonList // the identity list of bootstrap peers the node uses to discover other nodes apiRatelimits map[string]int @@ -156,6 +149,7 @@ type ObserverServiceConfig struct { logTxTimeToFinalized bool logTxTimeToExecuted bool logTxTimeToFinalizedExecuted bool + logTxTimeToSealed bool executionDataSyncEnabled bool executionDataIndexingEnabled bool executionDataDBMode string @@ -164,6 +158,7 @@ type ObserverServiceConfig struct { executionDataPruningInterval time.Duration localServiceAPIEnabled bool versionControlEnabled bool + stopControlEnabled bool executionDataDir string executionDataStartHeight uint64 executionDataConfig edrequester.ExecutionDataConfig @@ -172,6 +167,7 @@ type ObserverServiceConfig struct { registerCacheType string registerCacheSize uint programCacheSize uint + registerDBPruneThreshold uint64 } // DefaultObserverServiceConfig defines all the default values for the ObserverServiceConfig @@ -196,10 +192,11 @@ func DefaultObserverServiceConfig() *ObserverServiceConfig { TxResultQueryMode: backend.IndexQueryModeExecutionNodesOnly.String(), // default to ENs only for now }, RestConfig: rest.Config{ - ListenAddress: "", - WriteTimeout: rest.DefaultWriteTimeout, - ReadTimeout: rest.DefaultReadTimeout, - IdleTimeout: rest.DefaultIdleTimeout, + ListenAddress: "", + WriteTimeout: rest.DefaultWriteTimeout, + ReadTimeout: rest.DefaultReadTimeout, + IdleTimeout: rest.DefaultIdleTimeout, + MaxRequestSize: commonrest.DefaultMaxRequestSize, }, MaxMsgSize: grpcutils.DefaultMaxMsgSize, CompressorName: grpcutils.NoCompressor, @@ -219,8 +216,6 @@ func DefaultObserverServiceConfig() *ObserverServiceConfig { rpcMetricsEnabled: false, apiRatelimits: nil, apiBurstlimits: nil, - bootstrapNodeAddresses: []string{}, - bootstrapNodePublicKeys: []string{}, observerNetworkingKeyPath: cmd.NotSet, apiTimeout: 3 * time.Second, upstreamNodeAddresses: []string{}, @@ -231,6 +226,7 @@ func DefaultObserverServiceConfig() *ObserverServiceConfig { logTxTimeToFinalized: false, logTxTimeToExecuted: false, logTxTimeToFinalizedExecuted: false, + logTxTimeToSealed: false, executionDataSyncEnabled: false, executionDataIndexingEnabled: false, executionDataDBMode: execution_data.ExecutionDataDBModeBadger.String(), @@ -239,6 +235,7 @@ func DefaultObserverServiceConfig() *ObserverServiceConfig { executionDataPruningInterval: pruner.DefaultPruningInterval, localServiceAPIEnabled: false, versionControlEnabled: true, + stopControlEnabled: false, executionDataDir: filepath.Join(homedir, ".flow", "execution_data"), executionDataStartHeight: 0, executionDataConfig: edrequester.ExecutionDataConfig{ @@ -249,11 +246,12 @@ func DefaultObserverServiceConfig() *ObserverServiceConfig { RetryDelay: edrequester.DefaultRetryDelay, MaxRetryDelay: edrequester.DefaultMaxRetryDelay, }, - scriptExecMinBlock: 0, - scriptExecMaxBlock: math.MaxUint64, - registerCacheType: pstorage.CacheTypeTwoQueue.String(), - registerCacheSize: 0, - programCacheSize: 0, + scriptExecMinBlock: 0, + scriptExecMaxBlock: math.MaxUint64, + registerCacheType: pstorage.CacheTypeTwoQueue.String(), + registerCacheSize: 0, + programCacheSize: 0, + registerDBPruneThreshold: pruner.DefaultThreshold, } } @@ -280,6 +278,7 @@ type ObserverServiceBuilder struct { TxResultsIndex *index.TransactionResultsIndex IndexerDependencies *cmd.DependencyList VersionControl *version.VersionControl + StopControl *stop.StopControl ExecutionDataDownloader execution_data.Downloader ExecutionDataRequester state_synchronization.ExecutionDataRequester @@ -327,7 +326,7 @@ func (builder *ObserverServiceBuilder) deriveBootstrapPeerIdentities() error { return nil } - ids, err := cmd.BootstrapIdentities(builder.bootstrapNodeAddresses, builder.bootstrapNodePublicKeys) + ids, err := builder.DeriveBootstrapPeerIdentities() if err != nil { return fmt.Errorf("failed to derive bootstrap peer identities: %w", err) } @@ -624,6 +623,10 @@ func (builder *ObserverServiceBuilder) extraFlags() { defaultConfig.rpcConf.RestConfig.ReadTimeout, "timeout to use when reading REST request headers") flags.DurationVar(&builder.rpcConf.RestConfig.IdleTimeout, "rest-idle-timeout", defaultConfig.rpcConf.RestConfig.IdleTimeout, "idle timeout for REST connections") + flags.Int64Var(&builder.rpcConf.RestConfig.MaxRequestSize, + "rest-max-request-size", + defaultConfig.rpcConf.RestConfig.MaxRequestSize, + "the maximum request size in bytes for payload sent over REST server") flags.UintVar(&builder.rpcConf.MaxMsgSize, "rpc-max-message-size", defaultConfig.rpcConf.MaxMsgSize, @@ -648,14 +651,6 @@ func (builder *ObserverServiceBuilder) extraFlags() { "observer-networking-key-path", defaultConfig.observerNetworkingKeyPath, "path to the networking key for observer") - flags.StringSliceVar(&builder.bootstrapNodeAddresses, - "bootstrap-node-addresses", - defaultConfig.bootstrapNodeAddresses, - "the network addresses of the bootstrap access node if this is an observer e.g. access-001.mainnet.flow.org:9653,access-002.mainnet.flow.org:9653") - flags.StringSliceVar(&builder.bootstrapNodePublicKeys, - "bootstrap-node-public-keys", - defaultConfig.bootstrapNodePublicKeys, - "the networking public key of the bootstrap access node if this is an observer (in the same order as the bootstrap node addresses) e.g. \"d57a5e9c5.....\",\"44ded42d....\"") flags.DurationVar(&builder.apiTimeout, "upstream-api-timeout", defaultConfig.apiTimeout, "tcp timeout for Flow API gRPC sockets to upstrem nodes") flags.StringSliceVar(&builder.upstreamNodeAddresses, "upstream-node-addresses", @@ -672,6 +667,10 @@ func (builder *ObserverServiceBuilder) extraFlags() { "log-tx-time-to-finalized-executed", defaultConfig.logTxTimeToFinalizedExecuted, "log transaction time to finalized and executed") + flags.BoolVar(&builder.logTxTimeToSealed, + "log-tx-time-to-sealed", + defaultConfig.logTxTimeToSealed, + "log transaction time to sealed") flags.BoolVar(&builder.rpcMetricsEnabled, "rpc-metrics-enabled", defaultConfig.rpcMetricsEnabled, "whether to enable the rpc metrics") flags.BoolVar(&builder.executionDataIndexingEnabled, "execution-data-indexing-enabled", @@ -681,6 +680,10 @@ func (builder *ObserverServiceBuilder) extraFlags() { "version-control-enabled", defaultConfig.versionControlEnabled, "whether to enable the version control feature. Default value is true") + flags.BoolVar(&builder.stopControlEnabled, + "stop-control-enabled", + defaultConfig.stopControlEnabled, + "whether to enable the stop control feature. Default value is false") flags.BoolVar(&builder.localServiceAPIEnabled, "local-service-api-enabled", defaultConfig.localServiceAPIEnabled, "whether to use local indexed data for api queries") flags.StringVar(&builder.registersDBPath, "execution-state-dir", defaultConfig.registersDBPath, "directory to use for execution-state database") flags.StringVar(&builder.checkpointFile, "execution-state-checkpoint", defaultConfig.checkpointFile, "execution-state checkpoint file") @@ -802,6 +805,12 @@ func (builder *ObserverServiceBuilder) extraFlags() { "program-cache-size", defaultConfig.programCacheSize, "[experimental] number of blocks to cache for cadence programs. use 0 to disable cache. default: 0. Note: this is an experimental feature and may cause nodes to become unstable under certain workloads. Use with caution.") + + // Register DB Pruning + flags.Uint64Var(&builder.registerDBPruneThreshold, + "registerdb-pruning-threshold", + defaultConfig.registerDBPruneThreshold, + fmt.Sprintf("specifies the number of blocks below the latest stored block height to keep in register db. default: %d", defaultConfig.registerDBPruneThreshold)) }).ValidateFlags(func() error { if builder.executionDataSyncEnabled { if builder.executionDataConfig.FetchTimeout <= 0 { @@ -848,6 +857,10 @@ func (builder *ObserverServiceBuilder) extraFlags() { } } + if builder.rpcConf.RestConfig.MaxRequestSize <= 0 { + return errors.New("rest-max-request-size must be greater than 0") + } + return nil }) } @@ -926,7 +939,7 @@ func (builder *ObserverServiceBuilder) InitIDProviders() { if flowID, err := builder.IDTranslator.GetFlowID(pid); err != nil { // TODO: this is an instance of "log error and continue with best effort" anti-pattern - builder.Logger.Err(err).Str("peer", p2plogging.PeerId(pid)).Msg("failed to translate to Flow ID") + builder.Logger.Debug().Str("peer", p2plogging.PeerId(pid)).Msg("failed to translate to Flow ID") } else { result = append(result, flowID) } @@ -985,10 +998,10 @@ func (builder *ObserverServiceBuilder) validateParams() error { if len(builder.bootstrapIdentities) > 0 { return nil } - if len(builder.bootstrapNodeAddresses) == 0 { + if len(builder.BootstrapNodeAddresses) == 0 { return errors.New("no bootstrap node address provided") } - if len(builder.bootstrapNodeAddresses) != len(builder.bootstrapNodePublicKeys) { + if len(builder.BootstrapNodeAddresses) != len(builder.BootstrapNodePublicKeys) { return errors.New("number of bootstrap node addresses and public keys should match") } if len(builder.upstreamNodePublicKeys) > 0 && len(builder.upstreamNodeAddresses) != len(builder.upstreamNodePublicKeys) { @@ -997,77 +1010,6 @@ func (builder *ObserverServiceBuilder) validateParams() error { return nil } -// initPublicLibp2pNode creates a libp2p node for the observer service in the public (unstaked) network. -// The factory function is later passed into the initMiddleware function to eventually instantiate the p2p.LibP2PNode instance -// The LibP2P host is created with the following options: -// * DHT as client and seeded with the given bootstrap peers -// * The specified bind address as the listen address -// * The passed in private key as the libp2p key -// * No connection gater -// * No connection manager -// * No peer manager -// * Default libp2p pubsub options. -// Args: -// - networkKey: the private key to use for the libp2p node -// Returns: -// - p2p.LibP2PNode: the libp2p node -// - error: if any error occurs. Any error returned is considered irrecoverable. -func (builder *ObserverServiceBuilder) initPublicLibp2pNode(networkKey crypto.PrivateKey) (p2p.LibP2PNode, error) { - var pis []peer.AddrInfo - - for _, b := range builder.bootstrapIdentities { - pi, err := utils.PeerAddressInfo(*b) - if err != nil { - return nil, fmt.Errorf("could not extract peer address info from bootstrap identity %v: %w", b, err) - } - - pis = append(pis, pi) - } - - node, err := p2pbuilder.NewNodeBuilder( - builder.Logger, - &builder.FlowConfig.NetworkConfig.GossipSub, - &p2pbuilderconfig.MetricsConfig{ - HeroCacheFactory: builder.HeroCacheMetricsFactory(), - Metrics: builder.Metrics.Network, - }, - network.PublicNetwork, - builder.BaseConfig.BindAddr, - networkKey, - builder.SporkID, - builder.IdentityProvider, - &builder.FlowConfig.NetworkConfig.ResourceManager, - p2pbuilderconfig.PeerManagerDisableConfig(), // disable peer manager for observer node. - &p2p.DisallowListCacheConfig{ - MaxSize: builder.FlowConfig.NetworkConfig.DisallowListNotificationCacheSize, - Metrics: metrics.DisallowListCacheMetricsFactory(builder.HeroCacheMetricsFactory(), network.PublicNetwork), - }, - &p2pbuilderconfig.UnicastConfig{ - Unicast: builder.FlowConfig.NetworkConfig.Unicast, - }). - SetSubscriptionFilter( - networkingsubscription.NewRoleBasedFilter( - networkingsubscription.UnstakedRole, builder.IdentityProvider, - ), - ). - SetRoutingSystem(func(ctx context.Context, h host.Host) (routing.Routing, error) { - return p2pdht.NewDHT(ctx, h, protocols.FlowPublicDHTProtocolID(builder.SporkID), - builder.Logger, - builder.Metrics.Network, - p2pdht.AsClient(), - dht.BootstrapPeers(pis...), - ) - }). - Build() - if err != nil { - return nil, fmt.Errorf("could not initialize libp2p node for observer: %w", err) - } - - builder.LibP2PNode = node - - return builder.LibP2PNode, nil -} - // initObserverLocal initializes the observer's ID, network key and network address // Currently, it reads a node-info.priv.json like any other node. // TODO: read the node ID from the special bootstrap files @@ -1428,7 +1370,7 @@ func (builder *ObserverServiceBuilder) BuildExecutionSyncComponents() *ObserverS } } - registers, err := pstorage.NewRegisters(pdb) + registers, err := pstorage.NewRegisters(pdb, builder.registerDBPruneThreshold) if err != nil { return nil, fmt.Errorf("could not create registers storage: %w", err) } @@ -1523,6 +1465,10 @@ func (builder *ObserverServiceBuilder) BuildExecutionSyncComponents() *ObserverS return nil, err } + if builder.stopControlEnabled { + builder.StopControl.RegisterHeightRecorder(builder.ExecutionIndexer) + } + return builder.ExecutionIndexer, nil }, builder.IndexerDependencies) } @@ -1652,11 +1598,13 @@ func (builder *ObserverServiceBuilder) enqueuePublicNetworkInit() { builder. Component("public libp2p node", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { var err error - publicLibp2pNode, err = builder.initPublicLibp2pNode(node.NetworkKey) + publicLibp2pNode, err = builder.BuildPublicLibp2pNode(builder.BaseConfig.BindAddr, builder.bootstrapIdentities) if err != nil { - return nil, fmt.Errorf("could not create public libp2p node: %w", err) + return nil, fmt.Errorf("could not build public libp2p node: %w", err) } + builder.LibP2PNode = publicLibp2pNode + return publicLibp2pNode, nil }). Component("public network", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { @@ -1739,11 +1687,12 @@ func (builder *ObserverServiceBuilder) enqueueRPCServer() { builder.logTxTimeToFinalized, builder.logTxTimeToExecuted, builder.logTxTimeToFinalizedExecuted, + builder.logTxTimeToSealed, ) return nil }) builder.Module("rest metrics", func(node *cmd.NodeConfig) error { - m, err := metrics.NewRestCollector(routes.URLToRoute, node.MetricsRegisterer) + m, err := metrics.NewRestCollector(router.URLToRoute, node.MetricsRegisterer) if err != nil { return err } @@ -1826,6 +1775,8 @@ func (builder *ObserverServiceBuilder) enqueueRPCServer() { versionControlDependable := module.NewProxiedReadyDoneAware() builder.IndexerDependencies.Add(versionControlDependable) + stopControlDependable := module.NewProxiedReadyDoneAware() + builder.IndexerDependencies.Add(stopControlDependable) builder.Component("version control", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { if !builder.versionControlEnabled { @@ -1859,6 +1810,25 @@ func (builder *ObserverServiceBuilder) enqueueRPCServer() { return versionControl, nil }) + builder.Component("stop control", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { + if !builder.stopControlEnabled { + noop := &module.NoopReadyDoneAware{} + stopControlDependable.Init(noop) + return noop, nil + } + + stopControl := stop.NewStopControl( + builder.Logger, + ) + + builder.VersionControl.AddVersionUpdatesConsumer(stopControl.OnVersionUpdate) + + builder.StopControl = stopControl + stopControlDependable.Init(builder.StopControl) + + return stopControl, nil + }) + builder.Component("RPC engine", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { accessMetrics := builder.AccessMetrics config := builder.rpcConf @@ -1904,25 +1874,47 @@ func (builder *ObserverServiceBuilder) enqueueRPCServer() { return nil, fmt.Errorf("failed to initialize block tracker: %w", err) } + // If execution data syncing and indexing is disabled, pass nil indexReporter + var indexReporter state_synchronization.IndexReporter + if builder.executionDataSyncEnabled && builder.executionDataIndexingEnabled { + indexReporter = builder.Reporter + } + + preferredENIdentifiers, err := commonrpc.IdentifierList(backendConfig.PreferredExecutionNodeIDs) + if err != nil { + return nil, fmt.Errorf("failed to convert node id string to Flow Identifier for preferred EN map: %w", err) + } + + fixedENIdentifiers, err := commonrpc.IdentifierList(backendConfig.FixedExecutionNodeIDs) + if err != nil { + return nil, fmt.Errorf("failed to convert node id string to Flow Identifier for fixed EN map: %w", err) + } + + execNodeIdentitiesProvider := commonrpc.NewExecutionNodeIdentitiesProvider( + node.Logger, + node.State, + node.Storage.Receipts, + preferredENIdentifiers, + fixedENIdentifiers, + ) + backendParams := backend.Params{ - State: node.State, - Blocks: node.Storage.Blocks, - Headers: node.Storage.Headers, - Collections: node.Storage.Collections, - Transactions: node.Storage.Transactions, - ExecutionReceipts: node.Storage.Receipts, - ExecutionResults: node.Storage.Results, - ChainID: node.RootChainID, - AccessMetrics: accessMetrics, - ConnFactory: connFactory, - RetryEnabled: false, - MaxHeightRange: backendConfig.MaxHeightRange, - PreferredExecutionNodeIDs: backendConfig.PreferredExecutionNodeIDs, - FixedExecutionNodeIDs: backendConfig.FixedExecutionNodeIDs, - Log: node.Logger, - SnapshotHistoryLimit: backend.DefaultSnapshotHistoryLimit, - Communicator: backend.NewNodeCommunicator(backendConfig.CircuitBreakerConfig.Enabled), - BlockTracker: blockTracker, + State: node.State, + Blocks: node.Storage.Blocks, + Headers: node.Storage.Headers, + Collections: node.Storage.Collections, + Transactions: node.Storage.Transactions, + ExecutionReceipts: node.Storage.Receipts, + ExecutionResults: node.Storage.Results, + ChainID: node.RootChainID, + AccessMetrics: accessMetrics, + ConnFactory: connFactory, + RetryEnabled: false, + MaxHeightRange: backendConfig.MaxHeightRange, + Log: node.Logger, + SnapshotHistoryLimit: backend.DefaultSnapshotHistoryLimit, + Communicator: backend.NewNodeCommunicator(backendConfig.CircuitBreakerConfig.Enabled), + BlockTracker: blockTracker, SubscriptionHandler: subscription.NewSubscriptionHandler( builder.Logger, broadcaster, @@ -1930,7 +1922,9 @@ func (builder *ObserverServiceBuilder) enqueueRPCServer() { builder.stateStreamConf.ResponseLimit, builder.stateStreamConf.ClientSendBufferSize, ), - VersionControl: builder.VersionControl, + IndexReporter: indexReporter, + VersionControl: builder.VersionControl, + ExecNodeIdentitiesProvider: execNodeIdentitiesProvider, } if builder.localServiceAPIEnabled { @@ -1958,12 +1952,6 @@ func (builder *ObserverServiceBuilder) enqueueRPCServer() { return nil, err } - // If execution data syncing and indexing is disabled, pass nil indexReporter - var indexReporter state_synchronization.IndexReporter - if builder.executionDataSyncEnabled && builder.executionDataIndexingEnabled { - indexReporter = builder.Reporter - } - engineBuilder, err := rpc.NewBuilder( node.Logger, node.State, diff --git a/cmd/scaffold.go b/cmd/scaffold.go index 5db4e75c395..a3a95cfff15 100644 --- a/cmd/scaffold.go +++ b/cmd/scaffold.go @@ -138,8 +138,8 @@ type FlowNodeBuilder struct { adminCommandBootstrapper *admin.CommandRunnerBootstrapper adminCommands map[string]func(config *NodeConfig) commands.AdminCommand componentBuilder component.ComponentManagerBuilder - bootstrapNodeAddresses []string - bootstrapNodePublicKeys []string + BootstrapNodeAddresses []string + BootstrapNodePublicKeys []string } var _ NodeBuilder = (*FlowNodeBuilder)(nil) @@ -254,13 +254,13 @@ func (fnb *FlowNodeBuilder) BaseFlags() { // observer mode allows a unstaked execution node to fetch blocks from a public staked access node, and being able to execute blocks fnb.flags.BoolVar(&fnb.BaseConfig.ObserverMode, "observer-mode", defaultConfig.ObserverMode, "whether the node is running in observer mode") - fnb.flags.StringSliceVar(&fnb.bootstrapNodePublicKeys, + fnb.flags.StringSliceVar(&fnb.BootstrapNodePublicKeys, "observer-mode-bootstrap-node-public-keys", - nil, + []string{}, "the networking public key of the bootstrap access node if this is an observer (in the same order as the bootstrap node addresses) e.g. \"d57a5e9c5.....\",\"44ded42d....\"") - fnb.flags.StringSliceVar(&fnb.bootstrapNodeAddresses, + fnb.flags.StringSliceVar(&fnb.BootstrapNodeAddresses, "observer-mode-bootstrap-node-addresses", - nil, + []string{}, "the network addresses of the bootstrap access node if this is an observer e.g. access-001.mainnet.flow.org:9653,access-002.mainnet.flow.org:9653") } @@ -413,8 +413,13 @@ func (fnb *FlowNodeBuilder) EnqueueNetworkInit() { } if fnb.ObserverMode { - // observer mode only init pulbic libp2p node - publicLibp2pNode, err := fnb.BuildPublicLibp2pNode(myAddr) + // observer mode only init public libp2p node + ids, err := fnb.DeriveBootstrapPeerIdentities() + if err != nil { + return nil, fmt.Errorf("failed to derive bootstrap peer identities: %w", err) + } + + publicLibp2pNode, err := fnb.BuildPublicLibp2pNode(myAddr, ids) if err != nil { return nil, fmt.Errorf("could not build public libp2p node: %w", err) } @@ -500,7 +505,18 @@ func (fnb *FlowNodeBuilder) HeroCacheMetricsFactory() metrics.HeroCacheMetricsFa return metrics.NewNoopHeroCacheMetricsFactory() } -// initPublicLibp2pNode creates a libp2p node for the observer service in the public (unstaked) network. +// DeriveBootstrapPeerIdentities derives the Flow Identity of the bootstrap peers from the parameters. +// These are the identities of the observers also acting as the DHT bootstrap server +func (fnb *FlowNodeBuilder) DeriveBootstrapPeerIdentities() (flow.IdentitySkeletonList, error) { + ids, err := BootstrapIdentities(fnb.BootstrapNodeAddresses, fnb.BootstrapNodePublicKeys) + if err != nil { + return nil, fmt.Errorf("failed to derive bootstrap peer identities: %w", err) + } + + return ids, nil +} + +// BuildPublicLibp2pNode creates a libp2p node for the observer service in the public (unstaked) network. // The factory function is later passed into the initMiddleware function to eventually instantiate the p2p.LibP2PNode instance // The LibP2P host is created with the following options: // * DHT as client and seeded with the given bootstrap peers @@ -515,24 +531,10 @@ func (fnb *FlowNodeBuilder) HeroCacheMetricsFactory() metrics.HeroCacheMetricsFa // Returns: // - p2p.LibP2PNode: the libp2p node // - error: if any error occurs. Any error returned is considered irrecoverable. -func (fnb *FlowNodeBuilder) BuildPublicLibp2pNode(address string) (p2p.LibP2PNode, error) { +func (fnb *FlowNodeBuilder) BuildPublicLibp2pNode(address string, bootstrapIdentities flow.IdentitySkeletonList) (p2p.LibP2PNode, error) { var pis []peer.AddrInfo - ids, err := BootstrapIdentities(fnb.bootstrapNodeAddresses, fnb.bootstrapNodePublicKeys) - if err != nil { - return nil, fmt.Errorf("could not create bootstrap identities: %w", err) - } - - for _, b := range ids { - pi, err := utils.PeerAddressInfo(*b) - if err != nil { - return nil, fmt.Errorf("could not extract peer address info from bootstrap identity %v: %w", b, err) - } - - pis = append(pis, pi) - } - - for _, b := range ids { + for _, b := range bootstrapIdentities { pi, err := utils.PeerAddressInfo(*b) if err != nil { return nil, fmt.Errorf("could not extract peer address info from bootstrap identity %v: %w", b, err) diff --git a/cmd/util/cmd/check-storage/cmd.go b/cmd/util/cmd/check-storage/cmd.go index 5727d7a51d5..5bd4e1d634a 100644 --- a/cmd/util/cmd/check-storage/cmd.go +++ b/cmd/util/cmd/check-storage/cmd.go @@ -2,18 +2,18 @@ package check_storage import ( "context" - "slices" "github.com/rs/zerolog/log" "github.com/spf13/cobra" "golang.org/x/sync/errgroup" + "github.com/onflow/cadence/common" "github.com/onflow/cadence/runtime" - "github.com/onflow/cadence/runtime/common" "github.com/onflow/flow-go/cmd/util/ledger/reporters" "github.com/onflow/flow-go/cmd/util/ledger/util" "github.com/onflow/flow-go/cmd/util/ledger/util/registers" + "github.com/onflow/flow-go/fvm/evm/emulator/state" "github.com/onflow/flow-go/fvm/systemcontracts" "github.com/onflow/flow-go/ledger" "github.com/onflow/flow-go/model/flow" @@ -29,6 +29,14 @@ var ( flagNWorker int ) +var ( + evmAccount flow.Address + evmStorageIDKeys = []string{ + state.AccountsStorageIDKey, + state.CodesStorageIDKey, + } +) + var Cmd = &cobra.Command{ Use: "check-storage", Short: "Check storage health", @@ -102,13 +110,8 @@ func run(*cobra.Command, []string) { log.Fatal().Msg("--state-commitment must be provided when --state is provided") } - // For now, skip EVM storage account since a different decoder is needed for decoding EVM registers. - - systemContracts := systemcontracts.SystemContractsForChain(chainID) - - acctsToSkip := []string{ - flow.AddressToRegisterOwner(systemContracts.EVMStorage.Address), - } + // Get EVM account by chain + evmAccount = systemcontracts.SystemContractsForChain(chainID).EVMStorage.Address // Create report in JSONL format rw := reporters.NewReportFileWriterFactoryWithFormat(flagOutputDirectory, log.Logger, reporters.ReportFormatJSONL). @@ -161,13 +164,13 @@ func run(*cobra.Command, []string) { len(payloads), ) - failedAccountAddresses, err := checkStorageHealth(registersByAccount, flagNWorker, rw, acctsToSkip) + failedAccountAddresses, err := checkStorageHealth(registersByAccount, flagNWorker, rw) if err != nil { log.Fatal().Err(err).Msg("failed to check storage health") } if len(failedAccountAddresses) == 0 { - log.Info().Msgf("All %d accounts are health", accountCount) + log.Info().Msgf("All %d accounts are healthy", accountCount) return } @@ -188,7 +191,6 @@ func checkStorageHealth( registersByAccount *registers.ByAccount, nWorkers int, rw reporters.ReportWriter, - acctsToSkip []string, ) (failedAccountAddresses []string, err error) { accountCount := registersByAccount.AccountCount() @@ -211,10 +213,6 @@ func checkStorageHealth( func(accountRegisters *registers.AccountRegisters) error { defer logAccount(1) - if slices.Contains(acctsToSkip, accountRegisters.Owner()) { - return nil - } - accountStorageIssues := checkAccountStorageHealth(accountRegisters, nWorkers) if len(accountStorageIssues) > 0 { @@ -281,9 +279,6 @@ func checkStorageHealth( err = registersByAccount.ForEachAccount( func(accountRegisters *registers.AccountRegisters) error { - if slices.Contains(acctsToSkip, accountRegisters.Owner()) { - return nil - } jobs <- job{accountRegisters: accountRegisters} return nil }) @@ -318,6 +313,10 @@ func checkAccountStorageHealth(accountRegisters *registers.AccountRegisters, nWo }} } + if isEVMAccount(address) { + return checkEVMAccountStorageHealth(address, accountRegisters) + } + var issues []accountStorageIssue // Check atree storage health @@ -331,7 +330,7 @@ func checkAccountStorageHealth(accountRegisters *registers.AccountRegisters, nWo issues, accountStorageIssue{ Address: address.Hex(), - Kind: storageErrorKindString[atreeStorageErrorKind], + Kind: storageErrorKindString[cadenceAtreeStorageErrorKind], Msg: err.Error(), }) } @@ -345,12 +344,14 @@ type storageErrorKind int const ( otherErrorKind storageErrorKind = iota - atreeStorageErrorKind + cadenceAtreeStorageErrorKind + evmAtreeStorageErrorKind ) var storageErrorKindString = map[storageErrorKind]string{ - otherErrorKind: "error_check_storage_failed", - atreeStorageErrorKind: "error_atree_storage", + otherErrorKind: "error_check_storage_failed", + cadenceAtreeStorageErrorKind: "error_cadence_atree_storage", + evmAtreeStorageErrorKind: "error_evm_atree_storage", } type accountStorageIssue struct { diff --git a/cmd/util/cmd/check-storage/evm_account_storage_health.go b/cmd/util/cmd/check-storage/evm_account_storage_health.go new file mode 100644 index 00000000000..80c57cd5a67 --- /dev/null +++ b/cmd/util/cmd/check-storage/evm_account_storage_health.go @@ -0,0 +1,498 @@ +package check_storage + +import ( + "bytes" + "cmp" + "fmt" + "slices" + + "golang.org/x/exp/maps" + + "github.com/onflow/atree" + + "github.com/onflow/cadence/common" + "github.com/onflow/cadence/runtime" + + "github.com/onflow/flow-go/cmd/util/ledger/util" + "github.com/onflow/flow-go/cmd/util/ledger/util/registers" + "github.com/onflow/flow-go/fvm/evm/emulator/state" + "github.com/onflow/flow-go/model/flow" +) + +var ( + compareSlabID = func(a, b atree.SlabID) int { + return a.Compare(b) + } + + equalSlabID = func(a, b atree.SlabID) bool { + return a.Compare(b) == 0 + } +) + +// checkEVMAccountStorageHealth checks storage health of cadence-atree +// registers and evm-atree registers in evm account. +func checkEVMAccountStorageHealth( + address common.Address, + accountRegisters *registers.AccountRegisters, +) []accountStorageIssue { + var issues []accountStorageIssue + + ledger := NewReadOnlyLedgerWithAtreeRegisterReadSet(accountRegisters) + + // Check health of cadence-atree registers. + issues = append( + issues, + checkCadenceAtreeRegistersInEVMAccount(address, ledger)..., + ) + + // Check health of evm-atree registers. + issues = append( + issues, + checkEVMAtreeRegistersInEVMAccount(address, ledger)..., + ) + + // Check unreferenced atree registers. + // If any atree registers are not accessed during health check of + // cadence-atree and evm-atree registers, these atree registers are + // unreferenced. + issues = append( + issues, + checkUnreferencedAtreeRegisters(address, ledger, accountRegisters)..., + ) + + return issues +} + +// checkCadenceAtreeRegistersInEVMAccount checks health of cadence-atree registers. +func checkCadenceAtreeRegistersInEVMAccount( + address common.Address, + ledger atree.Ledger, +) []accountStorageIssue { + var issues []accountStorageIssue + + storage := runtime.NewStorage(ledger, nil) + + // Load Cadence domains storage map, so atree slab iterator can traverse connected slabs from loaded root slab. + // NOTE: don't preload all atree slabs in evm account because evm-atree registers require evm-atree decoder. + + for _, domain := range util.StorageMapDomains { + _ = storage.GetStorageMap(address, domain, false) + } + + err := storage.CheckHealth() + if err != nil { + issues = append( + issues, + accountStorageIssue{ + Address: address.Hex(), + Kind: storageErrorKindString[cadenceAtreeStorageErrorKind], + Msg: fmt.Sprintf("cadence-atree registers health check failed in evm account: %s", err), + }) + } + + return issues +} + +// checkEVMAtreeRegistersInEVMAccount checks health of evm-atree registers. +func checkEVMAtreeRegistersInEVMAccount( + address common.Address, + ledger atree.Ledger, +) []accountStorageIssue { + var issues []accountStorageIssue + + baseStorage := atree.NewLedgerBaseStorage(ledger) + + storage, err := state.NewPersistentSlabStorage(baseStorage) + if err != nil { + issues = append( + issues, + accountStorageIssue{ + Address: address.Hex(), + Kind: storageErrorKindString[evmAtreeStorageErrorKind], + Msg: fmt.Sprintf("failed to create atree.PersistentSlabStorage for evm registers: %s", err), + }) + return issues + } + + domainSlabIDs := make(map[string]atree.SlabID) + + // Load evm domain root slabs. + for _, domain := range evmStorageIDKeys { + rawDomainSlabID, err := ledger.GetValue(address[:], []byte(domain)) + if err != nil { + issues = append(issues, accountStorageIssue{ + Address: address.Hex(), + Kind: storageErrorKindString[evmAtreeStorageErrorKind], + Msg: fmt.Sprintf("failed to get evm domain %s raw slab ID: %s", domain, err), + }) + continue + } + + if len(rawDomainSlabID) == 0 { + continue + } + + domainSlabID, err := atree.NewSlabIDFromRawBytes(rawDomainSlabID) + if err != nil { + issues = append(issues, accountStorageIssue{ + Address: address.Hex(), + Kind: storageErrorKindString[evmAtreeStorageErrorKind], + Msg: fmt.Sprintf("failed to convert evm domain %s raw slab ID %x to atree slab ID: %s", domain, rawDomainSlabID, err), + }) + continue + } + + // Retrieve evm domain storage register so slab iterator can traverse connected slabs from root slab. + + _, found, err := storage.Retrieve(domainSlabID) + if err != nil { + issues = append(issues, accountStorageIssue{ + Address: address.Hex(), + Kind: storageErrorKindString[evmAtreeStorageErrorKind], + Msg: fmt.Sprintf("failed to retrieve evm domain %s root slab %s: %s", domain, domainSlabID, err), + }) + continue + } + if !found { + issues = append(issues, accountStorageIssue{ + Address: address.Hex(), + Kind: storageErrorKindString[evmAtreeStorageErrorKind], + Msg: fmt.Sprintf("failed to find evm domain %s root slab %s", domain, domainSlabID), + }) + continue + } + + domainSlabIDs[domain] = domainSlabID + } + + if len(domainSlabIDs) == 0 { + return issues + } + + // Get evm storage slot slab IDs. + storageSlotSlabIDs, storageSlotIssues := getStorageSlotRootSlabIDs( + address, + domainSlabIDs[state.AccountsStorageIDKey], + storage) + + issues = append(issues, storageSlotIssues...) + + // Load evm storage slot slabs so slab iterator can traverse connected slabs in storage health check. + for _, id := range storageSlotSlabIDs { + _, found, err := storage.Retrieve(id) + if err != nil { + issues = append(issues, accountStorageIssue{ + Address: address.Hex(), + Kind: storageErrorKindString[evmAtreeStorageErrorKind], + Msg: fmt.Sprintf("failed to retrieve evm storage slot %s: %s", id, err), + }) + } + if !found { + issues = append(issues, accountStorageIssue{ + Address: address.Hex(), + Kind: storageErrorKindString[evmAtreeStorageErrorKind], + Msg: fmt.Sprintf("failed to find evm storage slot %s", id), + }) + } + } + + // Expected root slabs include domain root slabs and storage slot root slabs. + + expectedRootSlabIDs := make([]atree.SlabID, 0, len(domainSlabIDs)+len(storageSlotSlabIDs)) + expectedRootSlabIDs = append(expectedRootSlabIDs, maps.Values(domainSlabIDs)...) + expectedRootSlabIDs = append(expectedRootSlabIDs, storageSlotSlabIDs...) + + issues = append( + issues, + // Check storage health of evm-atree registers + checkHealthWithExpectedRootSlabIDs(address, storage, expectedRootSlabIDs)..., + ) + + return issues +} + +// getStorageSlotRootSlabIDs returns evm storage slot root slab IDs. +func getStorageSlotRootSlabIDs( + address common.Address, + accountStorageRootSlabID atree.SlabID, + storage *atree.PersistentSlabStorage, +) ([]atree.SlabID, []accountStorageIssue) { + + if accountStorageRootSlabID == atree.SlabIDUndefined { + return nil, nil + } + + var issues []accountStorageIssue + + // Load account storage map + m, err := atree.NewMapWithRootID(storage, accountStorageRootSlabID, atree.NewDefaultDigesterBuilder()) + if err != nil { + issues = append( + issues, + accountStorageIssue{ + Address: address.Hex(), + Kind: storageErrorKindString[evmAtreeStorageErrorKind], + Msg: fmt.Sprintf("failed to load evm storage slot %s: %s", accountStorageRootSlabID, err), + }) + return nil, issues + } + + storageSlotRootSlabIDs := make(map[atree.SlabID]struct{}) + + // Iterate accounts in account storage map to get storage slot collection ID. + err = m.IterateReadOnly(func(key, value atree.Value) (bool, error) { + // Check address type + acctAddress, ok := key.(state.ByteStringValue) + if !ok { + issues = append( + issues, + accountStorageIssue{ + Address: address.Hex(), + Kind: storageErrorKindString[evmAtreeStorageErrorKind], + Msg: fmt.Sprintf("expect evm account address as ByteStringValue, got %T", key), + }) + return true, nil + } + + // Check encoded account type + encodedAccount, ok := value.(state.ByteStringValue) + if !ok { + issues = append( + issues, + accountStorageIssue{ + Address: address.Hex(), + Kind: storageErrorKindString[evmAtreeStorageErrorKind], + Msg: fmt.Sprintf("expect evm account as ByteStringValue, got %T", key), + }) + return true, nil + } + + // Decode account + acct, err := state.DecodeAccount(encodedAccount.Bytes()) + if err != nil { + issues = append( + issues, + accountStorageIssue{ + Address: address.Hex(), + Kind: storageErrorKindString[evmAtreeStorageErrorKind], + Msg: fmt.Sprintf("failed to decode account %x in evm account storage: %s", acctAddress.Bytes(), err), + }) + return true, nil + } + + storageSlotCollectionID := acct.CollectionID + + if len(storageSlotCollectionID) == 0 { + return true, nil + } + + storageSlotSlabID, err := atree.NewSlabIDFromRawBytes(storageSlotCollectionID) + if err != nil { + issues = append( + issues, + accountStorageIssue{ + Address: address.Hex(), + Kind: storageErrorKindString[evmAtreeStorageErrorKind], + Msg: fmt.Sprintf("failed to convert storage slot collection ID %x to atree slab ID: %s", storageSlotCollectionID, err), + }) + return true, nil + } + + // Check storage slot is not double referenced. + if _, ok := storageSlotRootSlabIDs[storageSlotSlabID]; ok { + issues = append( + issues, + accountStorageIssue{ + Address: address.Hex(), + Kind: storageErrorKindString[evmAtreeStorageErrorKind], + Msg: fmt.Sprintf("found storage slot collection %x referenced by multiple accounts", storageSlotCollectionID), + }) + } + + storageSlotRootSlabIDs[storageSlotSlabID] = struct{}{} + + return true, nil + }) + if err != nil { + issues = append( + issues, + accountStorageIssue{ + Address: address.Hex(), + Kind: storageErrorKindString[evmAtreeStorageErrorKind], + Msg: fmt.Sprintf("failed to iterate EVM account storage map: %s", err), + }) + } + + return maps.Keys(storageSlotRootSlabIDs), nil +} + +// checkHealthWithExpectedRootSlabIDs checks atree registers in storage (loaded and connected registers). +func checkHealthWithExpectedRootSlabIDs( + address common.Address, + storage *atree.PersistentSlabStorage, + expectedRootSlabIDs []atree.SlabID, +) []accountStorageIssue { + var issues []accountStorageIssue + + // Check atree storage health + rootSlabIDSet, err := atree.CheckStorageHealth(storage, -1) + if err != nil { + issues = append( + issues, + accountStorageIssue{ + Address: address.Hex(), + Kind: storageErrorKindString[evmAtreeStorageErrorKind], + Msg: fmt.Sprintf("evm atree storage check failed: %s", err), + }) + return issues + } + + // Check if returned root slab IDs match expected root slab IDs. + + rootSlabIDs := maps.Keys(rootSlabIDSet) + slices.SortFunc(rootSlabIDs, compareSlabID) + + slices.SortFunc(expectedRootSlabIDs, compareSlabID) + + if !slices.EqualFunc(expectedRootSlabIDs, rootSlabIDs, equalSlabID) { + issues = append( + issues, + accountStorageIssue{ + Address: address.Hex(), + Kind: storageErrorKindString[evmAtreeStorageErrorKind], + Msg: fmt.Sprintf("root slabs %v from storage health check != expected root slabs %v", rootSlabIDs, expectedRootSlabIDs), + }) + } + + return issues +} + +// checkUnreferencedAtreeRegisters checks if all atree registers in account has been read through ledger. +func checkUnreferencedAtreeRegisters( + address common.Address, + ledger *ReadOnlyLedgerWithAtreeRegisterReadSet, + accountRegisters *registers.AccountRegisters, +) []accountStorageIssue { + var issues []accountStorageIssue + + allAtreeRegisterIDs, err := getAtreeRegisterIDsFromRegisters(accountRegisters) + if err != nil { + issues = append( + issues, + accountStorageIssue{ + Address: address.Hex(), + Kind: storageErrorKindString[otherErrorKind], + Msg: fmt.Sprintf("failed to get atree register IDs from account registers: %s", err), + }) + return issues + } + + // Check for unreferenced atree slabs by verifing all atree slabs in accountRegisters are read + // during storage health check for evm-atree and cadence-atree registers. + + if ledger.GetAtreeRegisterReadCount() == len(allAtreeRegisterIDs) { + return issues + } + + if ledger.GetAtreeRegisterReadCount() > len(allAtreeRegisterIDs) { + issues = append( + issues, + accountStorageIssue{ + Address: address.Hex(), + Kind: storageErrorKindString[otherErrorKind], + Msg: fmt.Sprintf("%d atree registers was read > %d atree registers in evm account", + ledger.GetAtreeRegisterReadCount(), + len(allAtreeRegisterIDs)), + }) + return issues + } + + unreferencedAtreeRegisterIDs := make([]flow.RegisterID, 0, len(allAtreeRegisterIDs)-ledger.GetAtreeRegisterReadCount()) + + for _, id := range allAtreeRegisterIDs { + if !ledger.IsAtreeRegisterRead(id) { + unreferencedAtreeRegisterIDs = append(unreferencedAtreeRegisterIDs, id) + } + } + + slices.SortFunc(unreferencedAtreeRegisterIDs, func(a, b flow.RegisterID) int { + return cmp.Compare(a.Key, b.Key) + }) + + issues = append(issues, accountStorageIssue{ + Address: address.Hex(), + Kind: storageErrorKindString[evmAtreeStorageErrorKind], + Msg: fmt.Sprintf( + "number of read atree slabs %d != number of atree slabs in storage %d: unreferenced atree registers %v", + ledger.GetAtreeRegisterReadCount(), + len(allAtreeRegisterIDs), + unreferencedAtreeRegisterIDs, + ), + }) + + return issues +} + +func getAtreeRegisterIDsFromRegisters(registers registers.Registers) ([]flow.RegisterID, error) { + registerIDs := make([]flow.RegisterID, 0, registers.Count()) + + err := registers.ForEach(func(owner string, key string, _ []byte) error { + if !flow.IsSlabIndexKey(key) { + return nil + } + + registerIDs = append( + registerIDs, + flow.NewRegisterID(flow.BytesToAddress([]byte(owner)), key), + ) + + return nil + }) + if err != nil { + return nil, err + } + + return registerIDs, nil +} + +func isEVMAccount(owner common.Address) bool { + return bytes.Equal(owner[:], evmAccount[:]) +} + +type ReadOnlyLedgerWithAtreeRegisterReadSet struct { + *registers.ReadOnlyLedger + atreeRegistersReadSet map[flow.RegisterID]struct{} +} + +var _ atree.Ledger = &ReadOnlyLedgerWithAtreeRegisterReadSet{} + +func NewReadOnlyLedgerWithAtreeRegisterReadSet( + accountRegisters *registers.AccountRegisters, +) *ReadOnlyLedgerWithAtreeRegisterReadSet { + return &ReadOnlyLedgerWithAtreeRegisterReadSet{ + ReadOnlyLedger: ®isters.ReadOnlyLedger{Registers: accountRegisters}, + atreeRegistersReadSet: make(map[flow.RegisterID]struct{}), + } +} + +func (l *ReadOnlyLedgerWithAtreeRegisterReadSet) GetValue(address, key []byte) (value []byte, err error) { + value, err = l.ReadOnlyLedger.GetValue(address, key) + if err != nil { + return nil, err + } + + if flow.IsSlabIndexKey(string(key)) { + registerID := flow.NewRegisterID(flow.BytesToAddress(address), string(key)) + l.atreeRegistersReadSet[registerID] = struct{}{} + } + return value, nil +} + +func (l *ReadOnlyLedgerWithAtreeRegisterReadSet) GetAtreeRegisterReadCount() int { + return len(l.atreeRegistersReadSet) +} + +func (l *ReadOnlyLedgerWithAtreeRegisterReadSet) IsAtreeRegisterRead(id flow.RegisterID) bool { + _, ok := l.atreeRegistersReadSet[id] + return ok +} diff --git a/cmd/util/cmd/check-storage/evm_account_storage_health_test.go b/cmd/util/cmd/check-storage/evm_account_storage_health_test.go new file mode 100644 index 00000000000..ea3384cc9fb --- /dev/null +++ b/cmd/util/cmd/check-storage/evm_account_storage_health_test.go @@ -0,0 +1,153 @@ +package check_storage + +import ( + "strconv" + "testing" + + "github.com/holiman/uint256" + "github.com/stretchr/testify/require" + "golang.org/x/exp/maps" + + "github.com/onflow/atree" + "github.com/onflow/cadence/common" + "github.com/onflow/cadence/interpreter" + "github.com/onflow/cadence/runtime" + gethCommon "github.com/onflow/go-ethereum/common" + + "github.com/onflow/flow-go/cmd/util/ledger/util" + "github.com/onflow/flow-go/cmd/util/ledger/util/registers" + "github.com/onflow/flow-go/fvm/evm/emulator/state" + "github.com/onflow/flow-go/fvm/evm/testutils" + "github.com/onflow/flow-go/fvm/evm/types" + "github.com/onflow/flow-go/ledger" + "github.com/onflow/flow-go/ledger/common/convert" + "github.com/onflow/flow-go/model/flow" +) + +func TestEVMAccountStorageHealth(t *testing.T) { + address := common.Address{1} + + t.Run("has storage slot", func(t *testing.T) { + led := createPayloadLedger() + + createEVMStorage(t, led, address) + + createCadenceStorage(t, led, address) + + payloads := maps.Values(led.Payloads) + + accountRegisters, err := registers.NewAccountRegistersFromPayloads(string(address[:]), payloads) + require.NoError(t, err) + + issues := checkEVMAccountStorageHealth( + address, + accountRegisters, + ) + require.Equal(t, 0, len(issues)) + }) + + t.Run("unreferenced slabs", func(t *testing.T) { + led := createPayloadLedger() + + createEVMStorage(t, led, address) + + createCadenceStorage(t, led, address) + + payloads := maps.Values(led.Payloads) + + // Add unreferenced slabs + slabIndex, err := led.AllocateSlabIndexFunc(address[:]) + require.NoError(t, err) + + registerID := flow.NewRegisterID( + flow.BytesToAddress(address[:]), + string(atree.SlabIndexToLedgerKey(slabIndex))) + + unreferencedPayload := ledger.NewPayload( + convert.RegisterIDToLedgerKey(registerID), + ledger.Value([]byte{1})) + + payloads = append(payloads, unreferencedPayload) + + accountRegisters, err := registers.NewAccountRegistersFromPayloads(string(address[:]), payloads) + require.NoError(t, err) + + issues := checkEVMAccountStorageHealth( + address, + accountRegisters, + ) + require.Equal(t, 1, len(issues)) + require.Equal(t, storageErrorKindString[evmAtreeStorageErrorKind], issues[0].Kind) + require.Contains(t, issues[0].Msg, "unreferenced atree registers") + }) +} + +func createEVMStorage(t *testing.T, ledger atree.Ledger, address common.Address) { + view, err := state.NewBaseView(ledger, flow.BytesToAddress(address[:])) + require.NoError(t, err) + + // Create an account without storage slot + addr1 := testutils.RandomCommonAddress(t) + + err = view.CreateAccount(addr1, uint256.NewInt(1), 2, []byte("ABC"), gethCommon.Hash{3, 4, 5}) + require.NoError(t, err) + + // Create an account with storage slot + addr2 := testutils.RandomCommonAddress(t) + + err = view.CreateAccount(addr2, uint256.NewInt(6), 7, []byte("DEF"), gethCommon.Hash{8, 9, 19}) + require.NoError(t, err) + + slot := types.SlotAddress{ + Address: addr2, + Key: testutils.RandomCommonHash(t), + } + + err = view.UpdateSlot(slot, testutils.RandomCommonHash(t)) + require.NoError(t, err) + + err = view.Commit() + require.NoError(t, err) +} + +func createCadenceStorage(t *testing.T, ledger atree.Ledger, address common.Address) { + storage := runtime.NewStorage(ledger, nil) + + inter, err := interpreter.NewInterpreter( + nil, + nil, + &interpreter.Config{ + Storage: storage, + }, + ) + require.NoError(t, err) + + // Create storage and public domains + for _, domain := range []string{"storage", "public"} { + storageDomain := storage.GetStorageMap(address, domain, true) + + // Create large domain map so there are more than one atree registers under the hood. + for i := 0; i < 100; i++ { + key := interpreter.StringStorageMapKey(domain + "_key_" + strconv.Itoa(i)) + value := interpreter.NewUnmeteredStringValue(domain + "_value_" + strconv.Itoa(i)) + storageDomain.SetValue(inter, key, value) + } + } + + // Commit domain data + err = storage.Commit(inter, false) + require.NoError(t, err) +} + +func createPayloadLedger() *util.PayloadsLedger { + nextSlabIndex := atree.SlabIndex{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1} + + return &util.PayloadsLedger{ + Payloads: make(map[flow.RegisterID]*ledger.Payload), + AllocateSlabIndexFunc: func([]byte) (atree.SlabIndex, error) { + var slabIndex atree.SlabIndex + slabIndex, nextSlabIndex = nextSlabIndex, nextSlabIndex.Next() + return slabIndex, nil + }, + } +} diff --git a/cmd/util/cmd/checkpoint-collect-stats/cmd.go b/cmd/util/cmd/checkpoint-collect-stats/cmd.go index 29c7bd1c5ef..c6c96f51eb3 100644 --- a/cmd/util/cmd/checkpoint-collect-stats/cmd.go +++ b/cmd/util/cmd/checkpoint-collect-stats/cmd.go @@ -1,12 +1,12 @@ package checkpoint_collect_stats import ( - "bufio" - "encoding/json" + "cmp" + "encoding/hex" "math" - "os" - "path/filepath" + "slices" "strings" + "sync" "github.com/montanaflynn/stats" "github.com/pkg/profile" @@ -17,9 +17,15 @@ import ( "github.com/onflow/atree" + "github.com/onflow/flow-go/cmd/util/ledger/reporters" + "github.com/onflow/flow-go/cmd/util/ledger/util" + "github.com/onflow/flow-go/fvm/evm/emulator/state" + "github.com/onflow/flow-go/fvm/evm/handler" + "github.com/onflow/flow-go/fvm/systemcontracts" "github.com/onflow/flow-go/ledger" "github.com/onflow/flow-go/ledger/common/pathfinder" "github.com/onflow/flow-go/ledger/complete" + "github.com/onflow/flow-go/ledger/complete/mtrie/trie" "github.com/onflow/flow-go/ledger/complete/wal" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module/metrics" @@ -27,61 +33,320 @@ import ( ) var ( - flagCheckpointDir string - flagOutputDir string - flagMemProfile bool + flagCheckpointDir string + flagStateCommitment string + flagPayloads string + flagOutputDir string + flagChain string + flagTopN int + flagMemProfile bool +) + +const ( + ledgerStatsReportName = "ledger-stats" + accountStatsReportName = "account-stats" +) + +const ( + domainTypePrefix = "domain " + payloadChannelBufferSize = 100_000 + initialAccountMapSize = 5_000_000 +) + +const ( + // EVM register keys from fvm/evm/handler/blockHashList.go + blockHashListMetaKey = "BlockHashListMeta" + blockHashListBucketKeyPrefix = "BlockHashListBucket" ) var Cmd = &cobra.Command{ Use: "checkpoint-collect-stats", - Short: "collects stats on tries stored in a checkpoint", - Run: run, + Short: "collects stats on tries stored in a checkpoint, or payloads from a payloads file", + Long: `checkpoint-collect-stats collects stats on tries stored in a checkpoint, or payloads from a payloads file. +Two kinds of input data are supported: +- checkpoint file(s) ("--checkpoint-dir" with optional "--state-commitment"), or +- payloads file ("--payload-filename")`, + Run: run, } func init() { Cmd.Flags().StringVar(&flagCheckpointDir, "checkpoint-dir", "", "Directory to load checkpoint files from") - _ = Cmd.MarkFlagRequired("checkpoint-dir") + + // state-commitment is optional. + // When provided, this program only gathers stats on trie with matching state commitment. + Cmd.Flags().StringVar(&flagStateCommitment, "state-commitment", "", + "Trie state commitment") + + Cmd.Flags().StringVar(&flagPayloads, "payload-filename", "", + "Payloads file name to load payloads from") Cmd.Flags().StringVar(&flagOutputDir, "output-dir", "", "Directory to write checkpoint stats to") _ = Cmd.MarkFlagRequired("output-dir") + Cmd.Flags().IntVar(&flagTopN, "top-n", 10, + "number of largest payloads or accounts to report") + + Cmd.Flags().StringVar(&flagChain, "chain", "", "Chain name") + _ = Cmd.MarkFlagRequired("chain") + Cmd.Flags().BoolVar(&flagMemProfile, "mem-profile", false, "Enable memory profiling") } type Stats struct { - LedgerStats *complete.LedgerStats + LedgerStats *complete.LedgerStats `json:",omitempty"` PayloadStats *PayloadStats } type PayloadStats struct { + TotalPayloadCount uint64 `json:"total_payload_count"` TotalPayloadSize uint64 `json:"total_payload_size"` TotalPayloadValueSize uint64 `json:"total_payload_value_size"` StatsByTypes []RegisterStatsByTypes `json:"stats_by_types"` + TopN []PayloadInfo `json:"largest_payloads"` } type RegisterStatsByTypes struct { - Type string `json:"type"` - Counts uint64 `json:"counts"` - ValueSizeTotal float64 `json:"value_size_total"` - ValueSizeMin float64 `json:"value_size_min"` - ValueSize25thPercentile float64 `json:"value_size_25th_percentile"` - ValueSizeMedian float64 `json:"value_size_median"` - ValueSize75thPercentile float64 `json:"value_size_75th_percentile"` - ValueSize95thPercentile float64 `json:"value_size_95th_percentile"` - ValueSizeMax float64 `json:"value_size_max"` + Type string `json:"type"` + Counts uint64 `json:"counts"` + ValueSizeTotal float64 `json:"value_size_total"` + ValueSizeMin float64 `json:"value_size_min"` + ValueSize25thPercentile float64 `json:"value_size_25th_percentile"` + ValueSizeMedian float64 `json:"value_size_median"` + ValueSize75thPercentile float64 `json:"value_size_75th_percentile"` + ValueSize95thPercentile float64 `json:"value_size_95th_percentile"` + ValueSize99thPercentile float64 `json:"value_size_99th_percentile"` + ValueSizeMax float64 `json:"value_size_max"` + SubTypes []RegisterStatsByTypes `json:"subtypes,omitempty"` +} + +type PayloadInfo struct { + Address string `json:"address"` + Key string `json:"key"` + Type string `json:"type"` + Size uint64 `json:"size"` +} + +type AccountStats struct { + AccountCount uint64 `json:"total_account_count"` + AccountSizeMin float64 `json:"account_size_min"` + AccountSize25thPercentile float64 `json:"account_size_25th_percentile"` + AccountSizeMedian float64 `json:"account_size_median"` + AccountSize75thPercentile float64 `json:"account_size_75th_percentile"` + AccountSize95thPercentile float64 `json:"account_size_95th_percentile"` + AccountSize99thPercentile float64 `json:"account_size_99th_percentile"` + AccountSizeMax float64 `json:"account_size_max"` + ServiceAccount *AccountInfo `json:"service_account,omitempty"` + EVMAccount *AccountInfo `json:"evm_account,omitempty"` + TopN []*AccountInfo `json:"largest_accounts"` +} + +type AccountInfo struct { + Address string `json:"address"` + PayloadCount uint64 `json:"payload_count"` + PayloadSize uint64 `json:"payload_size"` } type sizesByType map[string][]float64 func run(*cobra.Command, []string) { + if flagPayloads == "" && flagCheckpointDir == "" { + log.Fatal().Msg("Either --payload-filename or --checkpoint-dir must be provided") + } + if flagPayloads != "" && flagCheckpointDir != "" { + log.Fatal().Msg("Only one of --payload-filename or --checkpoint-dir must be provided") + } + if flagCheckpointDir == "" && flagStateCommitment != "" { + log.Fatal().Msg("--checkpont-dir must be provided when --state-commitment is provided") + } + + chainID := flow.ChainID(flagChain) + // Validate chain ID + _ = chainID.Chain() + if flagMemProfile { defer profile.Start(profile.MemProfile).Stop() } + payloadChannel := make(chan *ledger.Payload, payloadChannelBufferSize) + + ledgerStatsChannel := make(chan *complete.LedgerStats, 1) + + // Load execution state and retrieve payloads async + go getPayloadsAsync(payloadChannel, ledgerStatsChannel) + + var totalPayloadCount, totalPayloadSize, totalPayloadValueSize uint64 + + largestPayloads := util.NewTopN[PayloadInfo]( + flagTopN, + func(a, b PayloadInfo) bool { + return a.Size < b.Size + }, + ) + + valueSizesByType := make(sizesByType, 0) + + accounts := make(map[string]*AccountInfo, initialAccountMapSize) + + // Process payloads until payloadChannel is closed + for p := range payloadChannel { + key, err := p.Key() + if err != nil { + log.Fatal().Err(err).Msg("cannot load a key") + } + + address := key.KeyParts[0].Value + + size := p.Size() + value := p.Value() + valueSize := value.Size() + + // Update total payload size and count + totalPayloadSize += uint64(size) + totalPayloadValueSize += uint64(valueSize) + totalPayloadCount++ + + // Update payload sizes by type + typ := getType(key) + valueSizesByType[typ] = append(valueSizesByType[typ], float64(valueSize)) + + // Update top N largest payloads + _, _ = largestPayloads.Add( + PayloadInfo{ + Address: hex.EncodeToString(address), + Key: hex.EncodeToString(key.KeyParts[1].Value), + Type: typ, + Size: uint64(valueSize), + }) + + // Update accounts + account, exist := accounts[string(address)] + if !exist { + account = &AccountInfo{ + Address: hex.EncodeToString(address), + } + accounts[string(address)] = account + } + account.PayloadCount++ + account.PayloadSize += uint64(size) + } + + // At this point, all payload are processed. + + ledgerStats := <-ledgerStatsChannel + + var wg sync.WaitGroup + wg.Add(2) + + // Collect and write ledger stats + go func() { + defer wg.Done() + + statsByTypes := getStats(valueSizesByType) + + // Sort top N largest payloads by payload size in descending order + slices.SortFunc(largestPayloads.Tree, func(a, b PayloadInfo) int { + return cmp.Compare(b.Size, a.Size) + }) + + stats := &Stats{ + LedgerStats: ledgerStats, + PayloadStats: &PayloadStats{ + TotalPayloadCount: totalPayloadCount, + TotalPayloadSize: totalPayloadSize, + TotalPayloadValueSize: totalPayloadValueSize, + StatsByTypes: statsByTypes, + TopN: largestPayloads.Tree, + }, + } + + writeStats(ledgerStatsReportName, stats) + }() + + // Collect and write account stats + go func() { + defer wg.Done() + + accountsSlice := make([]*AccountInfo, 0, len(accounts)) + accountSizesSlice := make([]float64, 0, len(accounts)) + + for _, acct := range accounts { + accountsSlice = append(accountsSlice, acct) + accountSizesSlice = append(accountSizesSlice, float64(acct.PayloadSize)) + } + + // Sort accounts by payload size in descending order + slices.SortFunc(accountsSlice, func(a, b *AccountInfo) int { + return cmp.Compare(b.PayloadSize, a.PayloadSize) + }) + + stats := getTypeStats("", accountSizesSlice) + + evmAccountAddress := systemcontracts.SystemContractsForChain(chainID).EVMStorage.Address + + serviceAccountAddress := serviceAccountAddressForChain(chainID) + + acctStats := &AccountStats{ + AccountCount: uint64(len(accountsSlice)), + ServiceAccount: accounts[string(serviceAccountAddress[:])], + EVMAccount: accounts[string(evmAccountAddress[:])], + TopN: accountsSlice[:flagTopN], + AccountSizeMin: stats.ValueSizeMin, + AccountSize25thPercentile: stats.ValueSize25thPercentile, + AccountSizeMedian: stats.ValueSizeMedian, + AccountSize75thPercentile: stats.ValueSize75thPercentile, + AccountSize95thPercentile: stats.ValueSize95thPercentile, + AccountSize99thPercentile: stats.ValueSize99thPercentile, + AccountSizeMax: stats.ValueSizeMax, + } + + writeStats(accountStatsReportName, acctStats) + }() + + wg.Wait() +} + +func getPayloadsAsync( + payloadChannel chan<- *ledger.Payload, + ledgerStatsChannel chan<- *complete.LedgerStats, +) { + defer close(payloadChannel) + defer close(ledgerStatsChannel) + + payloadCallback := func(payload *ledger.Payload) { + payloadChannel <- payload + } + + useCheckpointFile := flagPayloads == "" + + if useCheckpointFile { + ledgerStatsChannel <- getPayloadStatsFromCheckpoint(payloadCallback) + } else { + getPayloadStatsFromPayloadFile(payloadCallback) + } +} + +func getPayloadStatsFromPayloadFile(payloadCallBack func(payload *ledger.Payload)) { + memAllocBefore := debug.GetHeapAllocsBytes() + log.Info().Msgf("loading payloads from %v", flagPayloads) + + _, payloads, err := util.ReadPayloadFile(log.Logger, flagPayloads) + if err != nil { + log.Fatal().Err(err).Msg("failed to read payloads") + } + + memAllocAfter := debug.GetHeapAllocsBytes() + log.Info().Msgf("%d payloads are loaded, mem usage: %d", len(payloads), memAllocAfter-memAllocBefore) + + for _, p := range payloads { + payloadCallBack(p) + } +} + +func getPayloadStatsFromCheckpoint(payloadCallBack func(payload *ledger.Payload)) *complete.LedgerStats { memAllocBefore := debug.GetHeapAllocsBytes() log.Info().Msgf("loading checkpoint(s) from %v", flagCheckpointDir) @@ -106,108 +371,139 @@ func run(*cobra.Command, []string) { memAllocAfter := debug.GetHeapAllocsBytes() log.Info().Msgf("the checkpoint is loaded, mem usage: %d", memAllocAfter-memAllocBefore) - var totalPayloadSize, totalPayloadValueSize uint64 - var value ledger.Value - var key ledger.Key - var size, valueSize int + var tries []*trie.MTrie - valueSizesByType := make(sizesByType, 0) - ledgerStats, err := led.CollectStats(func(p *ledger.Payload) { - key, err = p.Key() - if err != nil { - log.Fatal().Err(err).Msg("cannot load a key") - } + if flagStateCommitment != "" { + stateCommitment := util.ParseStateCommitment(flagStateCommitment) - size = p.Size() - value = p.Value() - valueSize = value.Size() - totalPayloadSize += uint64(size) - totalPayloadValueSize += uint64(valueSize) - valueSizesByType[getType(key)] = append(valueSizesByType[getType(key)], float64(valueSize)) - }) - - statsByTypes := make([]RegisterStatsByTypes, 0) - for t, values := range valueSizesByType { - - sum, err := stats.Sum(values) + t, err := led.FindTrieByStateCommit(stateCommitment) if err != nil { - log.Fatal().Err(err).Msg("cannot compute the sum of values") + log.Fatal().Err(err).Msgf("failed to find trie with state commitment %x", stateCommitment) } - - min, err := stats.Min(values) - if err != nil { - log.Fatal().Err(err).Msg("cannot compute the min of values") + if t == nil { + log.Fatal().Msgf("no trie with state commitment %x", stateCommitment) } - percentile25, err := stats.Percentile(values, 25) + tries = append(tries, t) + } else { + ts, err := led.Tries() if err != nil { - log.Fatal().Err(err).Msg("cannot compute the 25th percentile of values") + log.Fatal().Err(err).Msg("failed to get tries") } - median, err := stats.Median(values) - if err != nil { - log.Fatal().Err(err).Msg("cannot compute the median of values") - } + tries = append(tries, ts...) + } - percentile75, err := stats.Percentile(values, 75) - if err != nil { - log.Fatal().Err(err).Msg("cannot compute the 75th percentile of values") - } + log.Info().Msgf("collecting stats on %d tries", len(tries)) - percentile95, err := stats.Percentile(values, 95) - if err != nil { - log.Fatal().Err(err).Msg("cannot compute the 95th percentile of values") - } + ledgerStats, err := complete.CollectStats(tries, payloadCallBack) + if err != nil { + log.Fatal().Err(err).Msg("failed to collect stats") + } - max, err := stats.Max(values) - if err != nil { - log.Fatal().Err(err).Msg("cannot compute the max of values") - } + return ledgerStats +} - statsByTypes = append(statsByTypes, - RegisterStatsByTypes{ - Type: t, - Counts: uint64(len(values)), - ValueSizeTotal: sum, - ValueSizeMin: min, - ValueSize25thPercentile: percentile25, - ValueSizeMedian: median, - ValueSize75thPercentile: percentile75, - ValueSize95thPercentile: percentile95, - ValueSizeMax: max, - }) +func getTypeStats(t string, values []float64) RegisterStatsByTypes { + sum, err := stats.Sum(values) + if err != nil { + log.Fatal().Err(err).Msg("cannot compute the sum of values") } + min, err := stats.Min(values) if err != nil { - log.Fatal().Err(err).Msg("failed to collect stats") + log.Fatal().Err(err).Msg("cannot compute the min of values") } - stats := &Stats{ - LedgerStats: ledgerStats, - PayloadStats: &PayloadStats{ - TotalPayloadSize: totalPayloadSize, - TotalPayloadValueSize: totalPayloadValueSize, - StatsByTypes: statsByTypes, - }, + percentile25, err := stats.Percentile(values, 25) + if err != nil { + log.Fatal().Err(err).Msg("cannot compute the 25th percentile of values") } - path := filepath.Join(flagOutputDir, "ledger.stats.json") + median, err := stats.Median(values) + if err != nil { + log.Fatal().Err(err).Msg("cannot compute the median of values") + } - fi, err := os.Create(path) + percentile75, err := stats.Percentile(values, 75) if err != nil { - log.Fatal().Err(err).Msg("failed to create path") + log.Fatal().Err(err).Msg("cannot compute the 75th percentile of values") } - defer fi.Close() - writer := bufio.NewWriter(fi) - defer writer.Flush() + percentile95, err := stats.Percentile(values, 95) + if err != nil { + log.Fatal().Err(err).Msg("cannot compute the 95th percentile of values") + } - encoder := json.NewEncoder(writer) + percentile99, err := stats.Percentile(values, 99) + if err != nil { + log.Fatal().Err(err).Msg("cannot compute the 99th percentile of values") + } - err = encoder.Encode(stats) + max, err := stats.Max(values) if err != nil { - log.Fatal().Err(err).Msg("could not json encode ledger stats") + log.Fatal().Err(err).Msg("cannot compute the max of values") } + + return RegisterStatsByTypes{ + Type: t, + Counts: uint64(len(values)), + ValueSizeTotal: sum, + ValueSizeMin: min, + ValueSize25thPercentile: percentile25, + ValueSizeMedian: median, + ValueSize75thPercentile: percentile75, + ValueSize95thPercentile: percentile95, + ValueSize99thPercentile: percentile99, + ValueSizeMax: max, + } +} + +func getStats(valueSizesByType sizesByType) []RegisterStatsByTypes { + domainStats := make([]RegisterStatsByTypes, 0, len(util.StorageMapDomains)) + var allDomainSizes []float64 + + statsByTypes := make([]RegisterStatsByTypes, 0, len(valueSizesByType)) + for t, values := range valueSizesByType { + + stats := getTypeStats(t, values) + + if isDomainType(t) { + domainStats = append(domainStats, stats) + allDomainSizes = append(allDomainSizes, values...) + } else { + statsByTypes = append(statsByTypes, stats) + } + } + + allDomainStats := getTypeStats("domain", allDomainSizes) + allDomainStats.SubTypes = domainStats + + statsByTypes = append(statsByTypes, allDomainStats) + + // Sort domain stats by payload count in descending order + slices.SortFunc(allDomainStats.SubTypes, func(a, b RegisterStatsByTypes) int { + return cmp.Compare(b.Counts, a.Counts) + }) + + // Sort stats by payload count in descending order + slices.SortFunc(statsByTypes, func(a, b RegisterStatsByTypes) int { + return cmp.Compare(b.Counts, a.Counts) + }) + + return statsByTypes +} + +func writeStats(reportName string, stats interface{}) { + rw := reporters.NewReportFileWriterFactory(flagOutputDir, log.Logger). + ReportWriter(reportName) + defer rw.Close() + + rw.Write(stats) +} + +func isDomainType(typ string) bool { + return strings.HasPrefix(typ, domainTypePrefix) } func getType(key ledger.Key) string { @@ -215,33 +511,56 @@ func getType(key ledger.Key) string { kstr := string(k) if atree.LedgerKeyIsSlabKey(kstr) { - return "slab" + return "atree slab" + } + + isDomain := slices.Contains(util.StorageMapDomains, kstr) + if isDomain { + return domainTypePrefix + kstr } switch kstr { - case "storage": - return "account's cadence storage domain map" - case "private": - return "account's cadence private domain map" - case "public": - return "account's cadence public domain map" - case "contract": - return "account's cadence contract domain map" case flow.ContractNamesKey: return "contract names" case flow.AccountStatusKey: return "account status" - case "uuid": - return "uuid generator state" - case "account_address_state": + case flow.AddressStateKey: return "address generator state" + case state.AccountsStorageIDKey: + return "account storage ID" + case state.CodesStorageIDKey: + return "code storage ID" + case handler.BlockStoreLatestBlockKey: + return "latest block" + case handler.BlockStoreLatestBlockProposalKey: + return "latest block proposal" } + // other fvm registers + if kstr == "uuid" || strings.HasPrefix(kstr, "uuid_") { + return "uuid generator state" + } if strings.HasPrefix(kstr, "public_key_") { return "public key" } if strings.HasPrefix(kstr, flow.CodeKeyPrefix) { return "contract content" } + + // other evm registers + if strings.HasPrefix(kstr, blockHashListBucketKeyPrefix) { + return "block hash list bucket" + } + if strings.HasPrefix(kstr, blockHashListMetaKey) { + return "block hash list meta" + } + + log.Warn().Msgf("unknown payload key: %s", kstr) + return "others" } + +func serviceAccountAddressForChain(chainID flow.ChainID) flow.Address { + sc := systemcontracts.SystemContractsForChain(chainID) + return sc.FlowServiceAccount.Address +} diff --git a/cmd/util/cmd/common/clusters.go b/cmd/util/cmd/common/clusters.go index 6c77db7fbb3..1e8e4899858 100644 --- a/cmd/util/cmd/common/clusters.go +++ b/cmd/util/cmd/common/clusters.go @@ -7,7 +7,7 @@ import ( "github.com/rs/zerolog" "github.com/onflow/cadence" - cdcCommon "github.com/onflow/cadence/runtime/common" + cdcCommon "github.com/onflow/cadence/common" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/model/flow/assignment" @@ -38,9 +38,9 @@ import ( // - error: if any error occurs. Any error returned from this function is irrecoverable. func ConstructClusterAssignment(log zerolog.Logger, partnerNodes, internalNodes flow.IdentityList, numCollectionClusters int) (flow.AssignmentList, flow.ClusterList, error) { - partners := partnerNodes.Filter(filter.HasRole[flow.Identity](flow.RoleCollection)) - internals := internalNodes.Filter(filter.HasRole[flow.Identity](flow.RoleCollection)) - nCollectors := len(partners) + len(internals) + partnerCollectors := partnerNodes.Filter(filter.HasRole[flow.Identity](flow.RoleCollection)) + internalCollectors := internalNodes.Filter(filter.HasRole[flow.Identity](flow.RoleCollection)) + nCollectors := len(partnerCollectors) + len(internalCollectors) // ensure we have at least as many collection nodes as clusters if nCollectors < int(numCollectionClusters) { @@ -49,32 +49,24 @@ func ConstructClusterAssignment(log zerolog.Logger, partnerNodes, internalNodes } // shuffle both collector lists based on a non-deterministic algorithm - partners, err := partners.Shuffle() + partnerCollectors, err := partnerCollectors.Shuffle() if err != nil { log.Fatal().Err(err).Msg("could not shuffle partners") } - internals, err = internals.Shuffle() + internalCollectors, err = internalCollectors.Shuffle() if err != nil { log.Fatal().Err(err).Msg("could not shuffle internals") } - // The following is a heuristic for distributing the internal collector nodes (private staking key available - // to generate QC for cluster root block) and partner nodes (private staking unknown). We need internal nodes - // to control strictly more than 2/3 of the cluster's total weight. - // The following is a heuristic that distributes collectors round-robbin across the specified number of clusters. - // This heuristic only works when all collectors have equal weight! The following sanity check enforces this: - if len(partnerNodes) > 0 && len(partnerNodes) > 2*len(internalNodes) { - return nil, nil, fmt.Errorf("requiring at least x>0 number of partner nodes and y > 2x number of internal nodes, but got x,y=%d,%d", len(partnerNodes), len(internalNodes)) - } - // sanity check ^ enforces that there is at least one internal node, hence `internalNodes[0].InitialWeight` is always a valid reference weight - refWeight := internalNodes[0].InitialWeight + // capture first reference weight to validate that all collectors have equal weight + refWeight := internalCollectors[0].InitialWeight identifierLists := make([]flow.IdentifierList, numCollectionClusters) // array to track the 2/3 internal-nodes constraint (internal_nodes > 2 * partner_nodes) constraint := make([]int, numCollectionClusters) // first, round-robin internal nodes into each cluster - for i, node := range internals { + for i, node := range internalCollectors { if node.InitialWeight != refWeight { return nil, nil, fmt.Errorf("current implementation requires all collectors (partner & interal nodes) to have equal weight") } @@ -84,7 +76,7 @@ func ConstructClusterAssignment(log zerolog.Logger, partnerNodes, internalNodes } // next, round-robin partner nodes into each cluster - for i, node := range partners { + for i, node := range partnerCollectors { if node.InitialWeight != refWeight { return nil, nil, fmt.Errorf("current implementation requires all collectors (partner & interal nodes) to have equal weight") } @@ -102,7 +94,7 @@ func ConstructClusterAssignment(log zerolog.Logger, partnerNodes, internalNodes assignments := assignment.FromIdentifierLists(identifierLists) - collectors := append(partners, internals...) + collectors := append(partnerCollectors, internalCollectors...) clusters, err := factory.NewClusterList(assignments, collectors.ToSkeleton()) if err != nil { log.Fatal().Err(err).Msg("could not create cluster list") diff --git a/cmd/util/cmd/common/node_info.go b/cmd/util/cmd/common/node_info.go index 061741d0955..9263e5c96a3 100644 --- a/cmd/util/cmd/common/node_info.go +++ b/cmd/util/cmd/common/node_info.go @@ -50,7 +50,7 @@ func ReadFullPartnerNodeInfos(log zerolog.Logger, partnerWeightsPath, partnerNod weight := weights[partner.NodeID] if valid := ValidateWeight(weight); !valid { - return nil, fmt.Errorf(fmt.Sprintf("invalid partner weight: %d", weight)) + return nil, fmt.Errorf(fmt.Sprintf("invalid partner weight %v: %d", partner.NodeID, weight)) } if weight != flow.DefaultInitialWeight { @@ -148,7 +148,7 @@ func ReadFullInternalNodeInfos(log zerolog.Logger, internalNodePrivInfoDir, inte weight := weights[internal.Address] if valid := ValidateWeight(weight); !valid { - return nil, fmt.Errorf(fmt.Sprintf("invalid partner weight: %d", weight)) + return nil, fmt.Errorf(fmt.Sprintf("invalid partner weight %v: %d", internal.NodeID, weight)) } if weight != flow.DefaultInitialWeight { log.Warn().Msgf("internal node (id=%x) has non-default weight (%d != %d)", internal.NodeID, weight, flow.DefaultInitialWeight) @@ -224,3 +224,20 @@ func internalWeightsByAddress(log zerolog.Logger, config string) map[string]uint return weights } + +// FilterInternalPartners returns the `partners`, dropping any entries that are also in `internal` +// Formally, this function implements the set difference `partners \ internal`. +func FilterInternalPartners(partners []bootstrap.NodeInfo, internal []bootstrap.NodeInfo) []bootstrap.NodeInfo { + lookup := make(map[flow.Identifier]struct{}) + for _, node := range internal { + lookup[node.NodeID] = struct{}{} + } + + var filtered []bootstrap.NodeInfo + for _, node := range partners { + if _, ok := lookup[node.NodeID]; !ok { + filtered = append(filtered, node) + } + } + return filtered +} diff --git a/cmd/util/cmd/common/snapshot.go b/cmd/util/cmd/common/snapshot.go index 6e940237ea1..83b568b71b8 100644 --- a/cmd/util/cmd/common/snapshot.go +++ b/cmd/util/cmd/common/snapshot.go @@ -9,9 +9,10 @@ import ( "github.com/rs/zerolog" "github.com/sethvargo/go-retry" + "github.com/onflow/flow-go-sdk/access/grpc" + "github.com/onflow/flow-go/utils/logging" - "github.com/onflow/flow-go-sdk/access/grpc" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/state/protocol" "github.com/onflow/flow-go/state/protocol/inmem" diff --git a/cmd/util/cmd/diff-states/cmd.go b/cmd/util/cmd/diff-states/cmd.go index 78160bab8f0..ec92cf591e9 100644 --- a/cmd/util/cmd/diff-states/cmd.go +++ b/cmd/util/cmd/diff-states/cmd.go @@ -10,7 +10,7 @@ import ( "slices" "github.com/dustin/go-humanize/english" - "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/common" "github.com/rs/zerolog/log" "github.com/spf13/cobra" "golang.org/x/sync/errgroup" diff --git a/cmd/util/cmd/execution-state-extract/cmd.go b/cmd/util/cmd/execution-state-extract/cmd.go index 2196b554c93..96ca319e575 100644 --- a/cmd/util/cmd/execution-state-extract/cmd.go +++ b/cmd/util/cmd/execution-state-extract/cmd.go @@ -16,6 +16,7 @@ import ( "github.com/onflow/flow-go/cmd/util/ledger/migrations" "github.com/onflow/flow-go/cmd/util/ledger/reporters" "github.com/onflow/flow-go/cmd/util/ledger/util" + "github.com/onflow/flow-go/ledger/complete/wal" "github.com/onflow/flow-go/model/bootstrap" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module/metrics" @@ -31,6 +32,8 @@ var ( flagChain string flagNWorker int flagNoMigration bool + flagMigration string + flagAuthorizationFixes string flagNoReport bool flagValidateMigration bool flagAllowPartialStateFromPayloads bool @@ -86,6 +89,12 @@ func init() { Cmd.Flags().BoolVar(&flagNoMigration, "no-migration", false, "don't migrate the state") + Cmd.Flags().StringVar(&flagMigration, "migration", "cadence-1.0", + "migration name. 'cadence-1.0' (default) or 'fix-authorizations'") + + Cmd.Flags().StringVar(&flagAuthorizationFixes, "authorization-fixes", "", + "authorization fixes to apply. requires '--migration=fix-authorizations'") + Cmd.Flags().BoolVar(&flagNoReport, "no-report", false, "don't report the state") @@ -194,7 +203,10 @@ func run(*cobra.Command, []string) { defer pprof.StopCPUProfile() } - var stateCommitment flow.StateCommitment + err := os.MkdirAll(flagOutputDir, 0755) + if err != nil { + log.Fatal().Err(err).Msgf("cannot create output directory %s", flagOutputDir) + } if len(flagBlockHash) > 0 && len(flagStateCommitment) > 0 { log.Fatal().Msg("cannot run the command with both block hash and state commitment as inputs, only one of them should be provided") @@ -218,6 +230,21 @@ func run(*cobra.Command, []string) { log.Fatal().Msg("Both --validate and --diff are enabled, please specify only one (or none) of these") } + switch flagMigration { + case "cadence-1.0": + // valid, no-op + + case "fix-authorizations": + if flagAuthorizationFixes == "" { + log.Fatal().Msg("--migration=fix-authorizations requires --authorization-fixes") + } + + default: + log.Fatal().Msg("Invalid --migration: got %s, expected 'cadence-1.0' or 'fix-authorizations'") + } + + var stateCommitment flow.StateCommitment + if len(flagBlockHash) > 0 { blockID, err := flow.HexStringToIdentifier(flagBlockHash) if err != nil { @@ -297,12 +324,8 @@ func run(*cobra.Command, []string) { } } - // err := ensureCheckpointFileExist(flagExecutionStateDir) - // if err != nil { - // log.Fatal().Err(err).Msgf("cannot ensure checkpoint file exist in folder %v", flagExecutionStateDir) - // } - - chain := flow.ChainID(flagChain).Chain() + // Validate chain ID + _ = flow.ChainID(flagChain).Chain() if flagNoReport { log.Warn().Msgf("--no-report flag is deprecated") @@ -346,6 +369,12 @@ func run(*cobra.Command, []string) { hex.EncodeToString(stateCommitment[:]), flagExecutionStateDir, ) + + err := ensureCheckpointFileExist(flagExecutionStateDir) + if err != nil { + log.Error().Err(err).Msgf("cannot ensure checkpoint file exist in folder %v", flagExecutionStateDir) + } + } var outputMsg string @@ -371,43 +400,6 @@ func run(*cobra.Command, []string) { log.Info().Msgf("state extraction plan: %s, %s", inputMsg, outputMsg) - chainID := chain.ChainID() - - burnerContractChange := migrations.BurnerContractChangeNone - evmContractChange := migrations.EVMContractChangeNone - switch chainID { - case flow.Emulator: - burnerContractChange = migrations.BurnerContractChangeDeploy - evmContractChange = migrations.EVMContractChangeDeployMinimalAndUpdateFull - case flow.Testnet, flow.Mainnet: - burnerContractChange = migrations.BurnerContractChangeUpdate - evmContractChange = migrations.EVMContractChangeUpdateFull - } - - stagedContracts, err := migrations.StagedContractsFromCSV(flagStagedContractsFile) - if err != nil { - log.Fatal().Err(err).Msgf("error loading staged contracts: %s", err.Error()) - } - - opts := migrations.Options{ - NWorker: flagNWorker, - DiffMigrations: flagDiffMigration, - LogVerboseDiff: flagLogVerboseDiff, - CheckStorageHealthBeforeMigration: flagCheckStorageHealthBeforeMigration, - ChainID: chainID, - EVMContractChange: evmContractChange, - BurnerContractChange: burnerContractChange, - StagedContracts: stagedContracts, - Prune: flagPrune, - MaxAccountSize: flagMaxAccountSize, - VerboseErrorOutput: flagVerboseErrorOutput, - FixSlabsWithBrokenReferences: chainID == flow.Testnet && flagFixSlabsWithBrokenReferences, - FilterUnreferencedSlabs: flagFilterUnreferencedSlabs, - ReportMetrics: flagReportMetrics, - CacheStaticTypeMigrationResults: flagCacheStaticTypeMigrationResults, - CacheEntitlementsMigrationResults: flagCacheEntitlementsMigrationResults, - } - var extractor extractor if len(flagInputPayloadFileName) > 0 { extractor = newPayloadFileExtractor(log.Logger, flagInputPayloadFileName) @@ -427,9 +419,14 @@ func run(*cobra.Command, []string) { // Migrate payloads. if !flagNoMigration { - migrations := newMigrations(log.Logger, flagOutputDir, opts) + var migs []migrations.NamedMigration - migration := newMigration(log.Logger, migrations, flagNWorker) + switch flagMigration { + default: + log.Fatal().Msgf("unknown migration: %s", flagMigration) + } + + migration := newMigration(log.Logger, migs, flagNWorker) payloads, err = migration(payloads) if err != nil { @@ -484,26 +481,26 @@ func run(*cobra.Command, []string) { ) } -// func ensureCheckpointFileExist(dir string) error { -// checkpoints, err := wal.Checkpoints(dir) -// if err != nil { -// return fmt.Errorf("could not find checkpoint files: %v", err) -// } -// -// if len(checkpoints) != 0 { -// log.Info().Msgf("found checkpoint %v files: %v", len(checkpoints), checkpoints) -// return nil -// } -// -// has, err := wal.HasRootCheckpoint(dir) -// if err != nil { -// return fmt.Errorf("could not check has root checkpoint: %w", err) -// } -// -// if has { -// log.Info().Msg("found root checkpoint file") -// return nil -// } -// -// return fmt.Errorf("no checkpoint file was found, no root checkpoint file was found") -// } +func ensureCheckpointFileExist(dir string) error { + checkpoints, err := wal.Checkpoints(dir) + if err != nil { + return fmt.Errorf("could not find checkpoint files: %v", err) + } + + if len(checkpoints) != 0 { + log.Info().Msgf("found checkpoint %v files: %v", len(checkpoints), checkpoints) + return nil + } + + has, err := wal.HasRootCheckpoint(dir) + if err != nil { + return fmt.Errorf("could not check has root checkpoint: %w", err) + } + + if has { + log.Info().Msg("found root checkpoint file") + return nil + } + + return fmt.Errorf("no checkpoint file was found, no root checkpoint file was found in %v, check the --execution-state-dir flag", dir) +} diff --git a/cmd/util/cmd/execution-state-extract/execution_state_extract.go b/cmd/util/cmd/execution-state-extract/execution_state_extract.go index f9055f2f2d0..1ddbc721276 100644 --- a/cmd/util/cmd/execution-state-extract/execution_state_extract.go +++ b/cmd/util/cmd/execution-state-extract/execution_state_extract.go @@ -11,7 +11,6 @@ import ( "golang.org/x/sync/errgroup" migrators "github.com/onflow/flow-go/cmd/util/ledger/migrations" - "github.com/onflow/flow-go/cmd/util/ledger/reporters" "github.com/onflow/flow-go/cmd/util/ledger/util" "github.com/onflow/flow-go/ledger" "github.com/onflow/flow-go/ledger/common/hash" @@ -357,40 +356,3 @@ func createTrieFromPayloads(logger zerolog.Logger, payloads []*ledger.Payload) ( return newTrie, nil } - -func newMigrations( - log zerolog.Logger, - outputDir string, - opts migrators.Options, -) []migrators.NamedMigration { - - log.Info().Msg("initializing migrations") - - rwf := reporters.NewReportFileWriterFactory(outputDir, log) - - namedMigrations := migrators.NewCadence1Migrations( - log, - outputDir, - rwf, - opts, - ) - - // At the end, fix up storage-used discrepancies - namedMigrations = append( - namedMigrations, - migrators.NamedMigration{ - Name: "account-usage-migration", - Migrate: migrators.NewAccountBasedMigration( - log, - opts.NWorker, - []migrators.AccountBasedMigration{ - migrators.NewAccountUsageMigration(rwf), - }, - ), - }, - ) - - log.Info().Msg("initialized migrations") - - return namedMigrations -} diff --git a/cmd/util/cmd/execution-state-extract/execution_state_extract_test.go b/cmd/util/cmd/execution-state-extract/execution_state_extract_test.go index 272fd370a80..e630b925789 100644 --- a/cmd/util/cmd/execution-state-extract/execution_state_extract_test.go +++ b/cmd/util/cmd/execution-state-extract/execution_state_extract_test.go @@ -12,7 +12,7 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/atomic" - runtimeCommon "github.com/onflow/cadence/runtime/common" + runtimeCommon "github.com/onflow/cadence/common" "github.com/onflow/flow-go/cmd/util/cmd/common" "github.com/onflow/flow-go/cmd/util/ledger/util" diff --git a/cmd/util/cmd/export-evm-state/cmd.go b/cmd/util/cmd/export-evm-state/cmd.go new file mode 100644 index 00000000000..2927b9a313a --- /dev/null +++ b/cmd/util/cmd/export-evm-state/cmd.go @@ -0,0 +1,109 @@ +package evm_exporter + +import ( + "fmt" + "os" + + "github.com/rs/zerolog/log" + "github.com/spf13/cobra" + + "github.com/onflow/flow-go/cmd/util/ledger/util" + "github.com/onflow/flow-go/fvm/evm" + "github.com/onflow/flow-go/fvm/evm/emulator/state" + "github.com/onflow/flow-go/ledger" + "github.com/onflow/flow-go/ledger/common/convert" + "github.com/onflow/flow-go/model/flow" +) + +var ( + flagChain string + flagExecutionStateDir string + flagOutputDir string + flagStateCommitment string +) + +var Cmd = &cobra.Command{ + Use: "export-evm-state", + Short: "exports evm state into a several binary files", + Run: run, +} + +func init() { + Cmd.Flags().StringVar(&flagChain, "chain", "", "Chain name") + _ = Cmd.MarkFlagRequired("chain") + + Cmd.Flags().StringVar(&flagExecutionStateDir, "execution-state-dir", "", + "Execution Node state dir (where WAL logs are written") + _ = Cmd.MarkFlagRequired("execution-state-dir") + + Cmd.Flags().StringVar(&flagOutputDir, "output-dir", "", + "Directory to write new Execution State to") + _ = Cmd.MarkFlagRequired("output-dir") + + Cmd.Flags().StringVar(&flagStateCommitment, "state-commitment", "", + "State commitment (hex-encoded, 64 characters)") +} + +func run(*cobra.Command, []string) { + log.Info().Msg("start exporting evm state") + err := ExportEVMState(flagChain, flagExecutionStateDir, flagStateCommitment, flagOutputDir) + if err != nil { + log.Fatal().Err(err).Msg("cannot get export evm state") + } +} + +// ExportEVMState evm state +func ExportEVMState( + chainName string, + ledgerPath string, + targetState string, + outputPath string) error { + + chainID := flow.ChainID(chainName) + + storageRoot := evm.StorageAccountAddress(chainID) + rootOwner := string(storageRoot.Bytes()) + + payloads, err := util.ReadTrie(ledgerPath, util.ParseStateCommitment(targetState)) + if err != nil { + return err + } + + // filter payloads of evm storage + filteredPayloads := make(map[flow.RegisterID]*ledger.Payload, 0) + for _, payload := range payloads { + registerID, _, err := convert.PayloadToRegister(payload) + if err != nil { + return fmt.Errorf("failed to convert payload to register: %w", err) + } + if registerID.Owner == rootOwner { + filteredPayloads[registerID] = payload + } + } + + payloadsLedger := util.NewPayloadsLedger(filteredPayloads) + + exporter, err := state.NewExporter(payloadsLedger, storageRoot) + if err != nil { + return fmt.Errorf("failed to create exporter: %w", err) + } + + if _, err := os.Stat(outputPath); os.IsNotExist(err) { + err := os.Mkdir(outputPath, os.ModePerm) + if err != nil { + return fmt.Errorf("failed to create path: %w", err) + } + } + + fi, err := os.Create(outputPath) + if err != nil { + return err + } + defer fi.Close() + + err = exporter.Export(outputPath) + if err != nil { + return fmt.Errorf("failed to export: %w", err) + } + return nil +} diff --git a/cmd/util/cmd/extract-payloads-by-address/extract_payloads_test.go b/cmd/util/cmd/extract-payloads-by-address/extract_payloads_test.go index 3c1bb267cc1..697d983d1f9 100644 --- a/cmd/util/cmd/extract-payloads-by-address/extract_payloads_test.go +++ b/cmd/util/cmd/extract-payloads-by-address/extract_payloads_test.go @@ -9,7 +9,7 @@ import ( "github.com/rs/zerolog" "github.com/stretchr/testify/require" - "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/common" "github.com/onflow/flow-go/cmd/util/ledger/util" "github.com/onflow/flow-go/ledger" diff --git a/cmd/util/cmd/generate-authorization-fixes/cmd.go b/cmd/util/cmd/generate-authorization-fixes/cmd.go new file mode 100644 index 00000000000..ac2e5825aea --- /dev/null +++ b/cmd/util/cmd/generate-authorization-fixes/cmd.go @@ -0,0 +1,443 @@ +package generate_authorization_fixes + +import ( + "compress/gzip" + "encoding/json" + "io" + "os" + "strings" + "sync" + + "github.com/onflow/cadence/common" + "github.com/onflow/cadence/interpreter" + "github.com/onflow/cadence/sema" + "github.com/onflow/cadence/stdlib" + "github.com/rs/zerolog/log" + "github.com/schollz/progressbar/v3" + "github.com/spf13/cobra" + + common2 "github.com/onflow/flow-go/cmd/util/common" + "github.com/onflow/flow-go/cmd/util/ledger/migrations" + "github.com/onflow/flow-go/cmd/util/ledger/reporters" + "github.com/onflow/flow-go/cmd/util/ledger/util" + "github.com/onflow/flow-go/cmd/util/ledger/util/registers" + "github.com/onflow/flow-go/ledger" + "github.com/onflow/flow-go/model/flow" +) + +var ( + flagPayloads string + flagState string + flagStateCommitment string + flagOutputDirectory string + flagChain string + flagLinkMigrationReport string + flagAddresses string +) + +var Cmd = &cobra.Command{ + Use: "generate-authorization-fixes", + Short: "generate authorization fixes for capability controllers", + Run: run, +} + +func init() { + + Cmd.Flags().StringVar( + &flagPayloads, + "payloads", + "", + "Input payload file name", + ) + + Cmd.Flags().StringVar( + &flagState, + "state", + "", + "Input state file name", + ) + + Cmd.Flags().StringVar( + &flagStateCommitment, + "state-commitment", + "", + "Input state commitment", + ) + + Cmd.Flags().StringVar( + &flagOutputDirectory, + "output-directory", + "", + "Output directory", + ) + + Cmd.Flags().StringVar( + &flagChain, + "chain", + "", + "Chain name", + ) + _ = Cmd.MarkFlagRequired("chain") + + Cmd.Flags().StringVar( + &flagLinkMigrationReport, + "link-migration-report", + "", + "Input link migration report file name", + ) + _ = Cmd.MarkFlagRequired("link-migration-report") + + Cmd.Flags().StringVar( + &flagAddresses, + "addresses", + "", + "only generate fixes for given accounts (comma-separated hex-encoded addresses)", + ) +} + +func run(*cobra.Command, []string) { + + var addressFilter map[common.Address]struct{} + + if len(flagAddresses) > 0 { + for _, hexAddr := range strings.Split(flagAddresses, ",") { + + hexAddr = strings.TrimSpace(hexAddr) + + if len(hexAddr) == 0 { + continue + } + + addr, err := common2.ParseAddress(hexAddr) + if err != nil { + log.Fatal().Err(err).Msgf("failed to parse address: %s", hexAddr) + } + + if addressFilter == nil { + addressFilter = make(map[common.Address]struct{}) + } + addressFilter[common.Address(addr)] = struct{}{} + } + + addresses := make([]string, 0, len(addressFilter)) + for addr := range addressFilter { + addresses = append(addresses, addr.HexWithPrefix()) + } + log.Info().Msgf( + "Only generating fixes for %d accounts: %s", + len(addressFilter), + addresses, + ) + } + + if flagPayloads == "" && flagState == "" { + log.Fatal().Msg("Either --payloads or --state must be provided") + } else if flagPayloads != "" && flagState != "" { + log.Fatal().Msg("Only one of --payloads or --state must be provided") + } + if flagState != "" && flagStateCommitment == "" { + log.Fatal().Msg("--state-commitment must be provided when --state is provided") + } + + rwf := reporters.NewReportFileWriterFactory(flagOutputDirectory, log.Logger) + + chainID := flow.ChainID(flagChain) + // Validate chain ID + _ = chainID.Chain() + + migratedPublicLinkSetChan := make(chan MigratedPublicLinkSet, 1) + go func() { + migratedPublicLinkSetChan <- readMigratedPublicLinkSet( + flagLinkMigrationReport, + addressFilter, + ) + }() + + registersByAccountChan := make(chan *registers.ByAccount, 1) + go func() { + registersByAccountChan <- loadRegistersByAccount() + }() + + migratedPublicLinkSet := <-migratedPublicLinkSetChan + registersByAccount := <-registersByAccountChan + + fixReporter := rwf.ReportWriter("authorization-fixes") + defer fixReporter.Close() + + authorizationFixGenerator := &AuthorizationFixGenerator{ + registersByAccount: registersByAccount, + chainID: chainID, + migratedPublicLinkSet: migratedPublicLinkSet, + reporter: fixReporter, + } + + log.Info().Msg("Generating authorization fixes ...") + + if len(addressFilter) > 0 { + authorizationFixGenerator.generateFixesForAccounts(addressFilter) + } else { + authorizationFixGenerator.generateFixesForAllAccounts() + } +} + +func loadRegistersByAccount() *registers.ByAccount { + // Read payloads from payload file or checkpoint file + + var payloads []*ledger.Payload + var err error + + if flagPayloads != "" { + log.Info().Msgf("Reading payloads from %s", flagPayloads) + + _, payloads, err = util.ReadPayloadFile(log.Logger, flagPayloads) + if err != nil { + log.Fatal().Err(err).Msg("failed to read payloads") + } + } else { + log.Info().Msgf("Reading trie %s", flagStateCommitment) + + stateCommitment := util.ParseStateCommitment(flagStateCommitment) + payloads, err = util.ReadTrie(flagState, stateCommitment) + if err != nil { + log.Fatal().Err(err).Msg("failed to read state") + } + } + + log.Info().Msgf("creating registers from payloads (%d)", len(payloads)) + + registersByAccount, err := registers.NewByAccountFromPayloads(payloads) + if err != nil { + log.Fatal().Err(err) + } + log.Info().Msgf( + "created %d registers from payloads (%d accounts)", + registersByAccount.Count(), + registersByAccount.AccountCount(), + ) + + return registersByAccount +} + +func readMigratedPublicLinkSet(path string, addressFilter map[common.Address]struct{}) MigratedPublicLinkSet { + + file, err := os.Open(path) + if err != nil { + log.Fatal().Err(err).Msgf("can't open link migration report: %s", path) + } + defer file.Close() + + var reader io.Reader = file + if isGzip(file) { + reader, err = gzip.NewReader(file) + if err != nil { + log.Fatal().Err(err).Msgf("failed to create gzip reader for %s", path) + } + } + + log.Info().Msgf("Reading link migration report from %s ...", path) + + migratedPublicLinkSet, err := ReadMigratedPublicLinkSet(reader, addressFilter) + if err != nil { + log.Fatal().Err(err).Msgf("failed to read public link report: %s", path) + } + + log.Info().Msgf("Read %d public link migration entries", len(migratedPublicLinkSet)) + + return migratedPublicLinkSet +} + +func jsonEncodeAuthorization(authorization interpreter.Authorization) string { + switch authorization { + case interpreter.UnauthorizedAccess, interpreter.InaccessibleAccess: + return "" + default: + return string(authorization.ID()) + } +} + +type fixEntitlementsEntry struct { + CapabilityAddress common.Address + CapabilityID uint64 + ReferencedType interpreter.StaticType + Authorization interpreter.Authorization +} + +var _ json.Marshaler = fixEntitlementsEntry{} + +func (e fixEntitlementsEntry) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + CapabilityAddress string `json:"capability_address"` + CapabilityID uint64 `json:"capability_id"` + ReferencedType string `json:"referenced_type"` + Authorization string `json:"authorization"` + }{ + CapabilityAddress: e.CapabilityAddress.String(), + CapabilityID: e.CapabilityID, + ReferencedType: string(e.ReferencedType.ID()), + Authorization: jsonEncodeAuthorization(e.Authorization), + }) +} + +type AuthorizationFixGenerator struct { + registersByAccount *registers.ByAccount + chainID flow.ChainID + migratedPublicLinkSet MigratedPublicLinkSet + reporter reporters.ReportWriter +} + +func (g *AuthorizationFixGenerator) generateFixesForAllAccounts() { + var wg sync.WaitGroup + progress := progressbar.Default(int64(g.registersByAccount.AccountCount()), "Processing:") + + err := g.registersByAccount.ForEachAccount(func(accountRegisters *registers.AccountRegisters) error { + address := common.MustBytesToAddress([]byte(accountRegisters.Owner())) + wg.Add(1) + go func(address common.Address) { + defer wg.Done() + g.generateFixesForAccount(address) + _ = progress.Add(1) + }(address) + return nil + }) + if err != nil { + log.Fatal().Err(err) + } + + wg.Wait() + _ = progress.Finish() +} + +func (g *AuthorizationFixGenerator) generateFixesForAccounts(addresses map[common.Address]struct{}) { + var wg sync.WaitGroup + progress := progressbar.Default(int64(len(addresses)), "Processing:") + + for address := range addresses { + wg.Add(1) + go func(address common.Address) { + defer wg.Done() + g.generateFixesForAccount(address) + _ = progress.Add(1) + }(address) + } + + wg.Wait() + _ = progress.Finish() +} + +func (g *AuthorizationFixGenerator) generateFixesForAccount(address common.Address) { + mr, err := migrations.NewInterpreterMigrationRuntime( + g.registersByAccount, + g.chainID, + migrations.InterpreterMigrationRuntimeConfig{}, + ) + if err != nil { + log.Fatal().Err(err) + } + + capabilityControllerStorage := mr.Storage.GetStorageMap( + address, + stdlib.CapabilityControllerStorageDomain, + false, + ) + if capabilityControllerStorage == nil { + return + } + + iterator := capabilityControllerStorage.Iterator(nil) + for { + k, v := iterator.Next() + + if k == nil || v == nil { + break + } + + key, ok := k.(interpreter.Uint64AtreeValue) + if !ok { + log.Fatal().Msgf("unexpected key type: %T", k) + } + + capabilityID := uint64(key) + + value := interpreter.MustConvertUnmeteredStoredValue(v) + + capabilityController, ok := value.(*interpreter.StorageCapabilityControllerValue) + if !ok { + continue + } + + borrowType := capabilityController.BorrowType + + switch borrowType.Authorization.(type) { + case interpreter.EntitlementSetAuthorization: + g.maybeGenerateFixForEntitledCapabilityController( + address, + capabilityID, + borrowType, + ) + + case interpreter.Unauthorized: + // Already unauthorized, nothing to do + + case interpreter.Inaccessible: + log.Warn().Msgf( + "capability controller %d in account %s has borrow type with inaccessible authorization", + capabilityID, + address.HexWithPrefix(), + ) + + case interpreter.EntitlementMapAuthorization: + log.Warn().Msgf( + "capability controller %d in account %s has borrow type with entitlement map authorization", + capabilityID, + address.HexWithPrefix(), + ) + + default: + log.Warn().Msgf( + "capability controller %d in account %s has borrow type with entitlement map authorization", + capabilityID, + address.HexWithPrefix(), + ) + } + } +} + +func newEntitlementSetAuthorizationFromTypeIDs( + typeIDs []common.TypeID, + setKind sema.EntitlementSetKind, +) interpreter.EntitlementSetAuthorization { + return interpreter.NewEntitlementSetAuthorization( + nil, + func() []common.TypeID { + return typeIDs + }, + len(typeIDs), + setKind, + ) +} + +func (g *AuthorizationFixGenerator) maybeGenerateFixForEntitledCapabilityController( + capabilityAddress common.Address, + capabilityID uint64, + borrowType *interpreter.ReferenceStaticType, +) { + // Only remove the authorization if the capability controller was migrated from a public link + _, ok := g.migratedPublicLinkSet[AccountCapabilityID{ + Address: capabilityAddress, + CapabilityID: capabilityID, + }] + if !ok { + return + } + + g.reporter.Write(fixEntitlementsEntry{ + CapabilityAddress: capabilityAddress, + CapabilityID: capabilityID, + ReferencedType: borrowType.ReferencedType, + Authorization: borrowType.Authorization, + }) +} + +func isGzip(file *os.File) bool { + return strings.HasSuffix(file.Name(), ".gz") +} diff --git a/cmd/util/cmd/generate-authorization-fixes/cmd_test.go b/cmd/util/cmd/generate-authorization-fixes/cmd_test.go new file mode 100644 index 00000000000..1d579ee8d33 --- /dev/null +++ b/cmd/util/cmd/generate-authorization-fixes/cmd_test.go @@ -0,0 +1,263 @@ +package generate_authorization_fixes + +import ( + "fmt" + "testing" + + "github.com/onflow/cadence" + "github.com/onflow/cadence/common" + jsoncdc "github.com/onflow/cadence/encoding/json" + "github.com/onflow/cadence/interpreter" + "github.com/onflow/cadence/sema" + "github.com/rs/zerolog" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/onflow/flow-go/cmd/util/ledger/migrations" + "github.com/onflow/flow-go/cmd/util/ledger/reporters" + "github.com/onflow/flow-go/cmd/util/ledger/util/registers" + "github.com/onflow/flow-go/fvm" + "github.com/onflow/flow-go/fvm/storage/snapshot" + "github.com/onflow/flow-go/ledger" + "github.com/onflow/flow-go/ledger/common/convert" + "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/utils/unittest" +) + +func newBootstrapPayloads( + chainID flow.ChainID, + bootstrapProcedureOptions ...fvm.BootstrapProcedureOption, +) ([]*ledger.Payload, error) { + + ctx := fvm.NewContext( + fvm.WithChain(chainID.Chain()), + ) + + vm := fvm.NewVirtualMachine() + + storageSnapshot := snapshot.MapStorageSnapshot{} + + bootstrapProcedure := fvm.Bootstrap( + unittest.ServiceAccountPublicKey, + bootstrapProcedureOptions..., + ) + + executionSnapshot, _, err := vm.Run( + ctx, + bootstrapProcedure, + storageSnapshot, + ) + if err != nil { + return nil, err + } + + payloads := make([]*ledger.Payload, 0, len(executionSnapshot.WriteSet)) + + for registerID, registerValue := range executionSnapshot.WriteSet { + payloadKey := convert.RegisterIDToLedgerKey(registerID) + payload := ledger.NewPayload(payloadKey, registerValue) + payloads = append(payloads, payload) + } + + return payloads, nil +} + +type testReportWriter struct { + entries []any +} + +func (t *testReportWriter) Write(entry interface{}) { + t.entries = append(t.entries, entry) +} + +func (*testReportWriter) Close() { + // NO-OP +} + +var _ reporters.ReportWriter = &testReportWriter{} + +func TestGenerateAuthorizationFixes(t *testing.T) { + t.Parallel() + + // This test no longer works because publishing authorized capabilities is no longer allowed. + // The migration and test are kept for historical reasons. + + t.Skip() + + const chainID = flow.Emulator + chain := chainID.Chain() + + address, err := chain.AddressAtIndex(1000) + require.NoError(t, err) + + require.Equal(t, "bf519681cdb888b1", address.Hex()) + + log := zerolog.New(zerolog.NewTestWriter(t)) + + bootstrapPayloads, err := newBootstrapPayloads(chainID) + require.NoError(t, err) + + registersByAccount, err := registers.NewByAccountFromPayloads(bootstrapPayloads) + require.NoError(t, err) + + mr := migrations.NewBasicMigrationRuntime(registersByAccount) + + err = mr.Accounts.Create(nil, address) + require.NoError(t, err) + + expectedWriteAddresses := map[flow.Address]struct{}{ + address: {}, + } + + err = mr.Commit(expectedWriteAddresses, log) + require.NoError(t, err) + + const contractCode = ` + access(all) contract Test { + + access(all) entitlement E + + access(all) struct S {} + } + ` + + deployTX := flow.NewTransactionBody(). + SetScript([]byte(` + transaction(code: String) { + prepare(signer: auth(Contracts) &Account) { + signer.contracts.add(name: "Test", code: code.utf8) + } + } + `)). + AddAuthorizer(address). + AddArgument(jsoncdc.MustEncode(cadence.String(contractCode))) + + runDeployTx := migrations.NewTransactionBasedMigration( + deployTX, + chainID, + log, + expectedWriteAddresses, + ) + err = runDeployTx(registersByAccount) + require.NoError(t, err) + + setupTx := flow.NewTransactionBody(). + SetScript([]byte(fmt.Sprintf(` + import Test from %s + + transaction { + prepare(signer: auth(Storage, Capabilities) &Account) { + // Capability 1 was a public, unauthorized capability, which is now authorized. + // It should lose its entitlement + let cap1 = signer.capabilities.storage.issue(/storage/s) + signer.capabilities.publish(cap1, at: /public/s1) + + // Capability 2 was a public, unauthorized capability, which is now authorized. + // It is currently only stored, nested, in storage, and is not published. + // It should lose its entitlement + let cap2 = signer.capabilities.storage.issue(/storage/s) + signer.storage.save([cap2], to: /storage/caps2) + + // Capability 3 was a private, authorized capability. + // It is currently only stored, nested, in storage, and is not published. + // It should keep its entitlement + let cap3 = signer.capabilities.storage.issue(/storage/s) + signer.storage.save([cap3], to: /storage/caps3) + + // Capability 4 was a private, authorized capability. + // It is currently both stored, nested, in storage, and is published. + // It should keep its entitlement + let cap4 = signer.capabilities.storage.issue(/storage/s) + signer.storage.save([cap4], to: /storage/caps4) + signer.capabilities.publish(cap4, at: /public/s4) + + // Capability 5 was a public, unauthorized capability, which is still unauthorized. + // It is currently both stored, nested, in storage, and is published. + // There is no need to fix it. + let cap5 = signer.capabilities.storage.issue<&Test.S>(/storage/s) + signer.storage.save([cap5], to: /storage/caps5) + signer.capabilities.publish(cap5, at: /public/s5) + } + } + `, + address.HexWithPrefix(), + ))). + AddAuthorizer(address) + + runSetupTx := migrations.NewTransactionBasedMigration( + setupTx, + chainID, + log, + expectedWriteAddresses, + ) + err = runSetupTx(registersByAccount) + require.NoError(t, err) + + testContractLocation := common.AddressLocation{ + Address: common.Address(address), + Name: "Test", + } + + migratedPublicLinkSet := MigratedPublicLinkSet{ + { + Address: common.Address(address), + CapabilityID: 1, + }: {}, + { + Address: common.Address(address), + CapabilityID: 2, + }: {}, + { + Address: common.Address(address), + CapabilityID: 5, + }: {}, + } + + reporter := &testReportWriter{} + + generator := &AuthorizationFixGenerator{ + registersByAccount: registersByAccount, + chainID: chainID, + migratedPublicLinkSet: migratedPublicLinkSet, + reporter: reporter, + } + generator.generateFixesForAllAccounts() + + eTypeID := testContractLocation.TypeID(nil, "Test.E") + + assert.Equal(t, + []any{ + fixEntitlementsEntry{ + CapabilityAddress: common.Address(address), + CapabilityID: 1, + ReferencedType: interpreter.NewCompositeStaticTypeComputeTypeID( + nil, + testContractLocation, + "Test.S", + ), + Authorization: newEntitlementSetAuthorizationFromTypeIDs( + []common.TypeID{ + eTypeID, + }, + sema.Conjunction, + ), + }, + fixEntitlementsEntry{ + CapabilityAddress: common.Address(address), + CapabilityID: 2, + ReferencedType: interpreter.NewCompositeStaticTypeComputeTypeID( + nil, + testContractLocation, + "Test.S", + ), + Authorization: newEntitlementSetAuthorizationFromTypeIDs( + []common.TypeID{ + eTypeID, + }, + sema.Conjunction, + ), + }, + }, + reporter.entries, + ) +} diff --git a/cmd/util/cmd/generate-authorization-fixes/link_migration_report.go b/cmd/util/cmd/generate-authorization-fixes/link_migration_report.go new file mode 100644 index 00000000000..5a41dfc12e9 --- /dev/null +++ b/cmd/util/cmd/generate-authorization-fixes/link_migration_report.go @@ -0,0 +1,93 @@ +package generate_authorization_fixes + +import ( + "encoding/json" + "fmt" + "io" + "strings" + + "github.com/onflow/cadence/common" +) + +// AccountCapabilityID is a capability ID in an account. +type AccountCapabilityID struct { + Address common.Address + CapabilityID uint64 +} + +// MigratedPublicLinkSet is a set of capability controller IDs which were migrated from public links. +type MigratedPublicLinkSet map[AccountCapabilityID]struct{} + +// ReadMigratedPublicLinkSet reads a link migration report from the given reader, +// and returns a set of all capability controller IDs which were migrated from public links. +// +// The report is expected to be a JSON array of objects with the following structure: +// +// [ +// {"kind":"link-migration-success","account_address":"0x1","path":"/public/foo","capability_id":1}, +// ] +func ReadMigratedPublicLinkSet( + reader io.Reader, + filter map[common.Address]struct{}, +) (MigratedPublicLinkSet, error) { + + set := MigratedPublicLinkSet{} + + dec := json.NewDecoder(reader) + + token, err := dec.Token() + if err != nil { + return nil, fmt.Errorf("failed to read token: %w", err) + } + if token != json.Delim('[') { + return nil, fmt.Errorf("expected start of array, got %s", token) + } + + for dec.More() { + var entry struct { + Kind string `json:"kind"` + Address string `json:"account_address"` + Path string `json:"path"` + CapabilityID uint64 `json:"capability_id"` + } + err := dec.Decode(&entry) + if err != nil { + return nil, fmt.Errorf("failed to decode entry: %w", err) + } + + if entry.Kind != "link-migration-success" { + continue + } + + if !strings.HasPrefix(entry.Path, "/public/") { + continue + } + + address, err := common.HexToAddress(entry.Address) + if err != nil { + return nil, fmt.Errorf("failed to parse address: %w", err) + } + + if filter != nil { + if _, ok := filter[address]; !ok { + continue + } + } + + accountCapabilityID := AccountCapabilityID{ + Address: address, + CapabilityID: entry.CapabilityID, + } + set[accountCapabilityID] = struct{}{} + } + + token, err = dec.Token() + if err != nil { + return nil, fmt.Errorf("failed to read token: %w", err) + } + if token != json.Delim(']') { + return nil, fmt.Errorf("expected end of array, got %s", token) + } + + return set, nil +} diff --git a/cmd/util/cmd/generate-authorization-fixes/link_migration_report_test.go b/cmd/util/cmd/generate-authorization-fixes/link_migration_report_test.go new file mode 100644 index 00000000000..872babc0fec --- /dev/null +++ b/cmd/util/cmd/generate-authorization-fixes/link_migration_report_test.go @@ -0,0 +1,70 @@ +package generate_authorization_fixes + +import ( + "strings" + "testing" + + "github.com/onflow/cadence/common" + "github.com/stretchr/testify/require" +) + +func TestReadPublicLinkMigrationReport(t *testing.T) { + t.Parallel() + + contents := ` + [ + {"kind":"link-migration-success","account_address":"0x1","path":"/public/foo","capability_id":1}, + {"kind":"link-migration-success","account_address":"0x2","path":"/private/bar","capability_id":2}, + {"kind":"link-migration-success","account_address":"0x3","path":"/public/baz","capability_id":3} + ] + ` + + t.Run("unfiltered", func(t *testing.T) { + t.Parallel() + + reader := strings.NewReader(contents) + + mapping, err := ReadMigratedPublicLinkSet(reader, nil) + require.NoError(t, err) + + require.Equal(t, + MigratedPublicLinkSet{ + { + Address: common.MustBytesToAddress([]byte{0x1}), + CapabilityID: 1, + }: struct{}{}, + { + Address: common.MustBytesToAddress([]byte{0x3}), + CapabilityID: 3, + }: struct{}{}, + }, + mapping, + ) + }) + + t.Run("filtered", func(t *testing.T) { + t.Parallel() + + address1 := common.MustBytesToAddress([]byte{0x1}) + + reader := strings.NewReader(contents) + + mapping, err := ReadMigratedPublicLinkSet( + reader, + map[common.Address]struct{}{ + address1: {}, + }, + ) + require.NoError(t, err) + + require.Equal(t, + MigratedPublicLinkSet{ + { + Address: address1, + CapabilityID: 1, + }: struct{}{}, + }, + mapping, + ) + }) +} diff --git a/cmd/util/cmd/read-badger/cmd/find-block-by-commits/main.go b/cmd/util/cmd/read-badger/cmd/find-block-by-commits/main.go index 8eec5ddee0b..b1fb1cf3106 100644 --- a/cmd/util/cmd/read-badger/cmd/find-block-by-commits/main.go +++ b/cmd/util/cmd/read-badger/cmd/find-block-by-commits/main.go @@ -80,7 +80,7 @@ func FindBlockIDByCommits( func toStateCommitments(commitsStr string) ([]flow.StateCommitment, error) { commitSlice := strings.Split(commitsStr, ",") - commits := make([]flow.StateCommitment, len(commitSlice)) + commits := make([]flow.StateCommitment, 0, len(commitSlice)) for _, c := range commitSlice { commit, err := toStateCommitment(c) if err != nil { diff --git a/cmd/util/cmd/root.go b/cmd/util/cmd/root.go index dd11c40b14b..cefd8db691d 100644 --- a/cmd/util/cmd/root.go +++ b/cmd/util/cmd/root.go @@ -24,11 +24,13 @@ import ( export "github.com/onflow/flow-go/cmd/util/cmd/exec-data-json-export" edbs "github.com/onflow/flow-go/cmd/util/cmd/execution-data-blobstore/cmd" extract "github.com/onflow/flow-go/cmd/util/cmd/execution-state-extract" + evm_state_exporter "github.com/onflow/flow-go/cmd/util/cmd/export-evm-state" ledger_json_exporter "github.com/onflow/flow-go/cmd/util/cmd/export-json-execution-state" export_json_transactions "github.com/onflow/flow-go/cmd/util/cmd/export-json-transactions" extractpayloads "github.com/onflow/flow-go/cmd/util/cmd/extract-payloads-by-address" find_inconsistent_result "github.com/onflow/flow-go/cmd/util/cmd/find-inconsistent-result" find_trie_root "github.com/onflow/flow-go/cmd/util/cmd/find-trie-root" + generate_authorization_fixes "github.com/onflow/flow-go/cmd/util/cmd/generate-authorization-fixes" read_badger "github.com/onflow/flow-go/cmd/util/cmd/read-badger/cmd" read_execution_state "github.com/onflow/flow-go/cmd/util/cmd/read-execution-state" read_hotstuff "github.com/onflow/flow-go/cmd/util/cmd/read-hotstuff/cmd" @@ -122,6 +124,8 @@ func addCommands() { rootCmd.AddCommand(check_storage.Cmd) rootCmd.AddCommand(debug_tx.Cmd) rootCmd.AddCommand(debug_script.Cmd) + rootCmd.AddCommand(generate_authorization_fixes.Cmd) + rootCmd.AddCommand(evm_state_exporter.Cmd) } func initConfig() { diff --git a/cmd/util/cmd/run-script/cmd.go b/cmd/util/cmd/run-script/cmd.go index 7f12cef5b35..1f24d2599c2 100644 --- a/cmd/util/cmd/run-script/cmd.go +++ b/cmd/util/cmd/run-script/cmd.go @@ -1,19 +1,29 @@ package run_script import ( + "context" + "errors" + "fmt" "io" "os" jsoncdc "github.com/onflow/cadence/encoding/json" + "github.com/onflow/flow/protobuf/go/flow/entities" "github.com/rs/zerolog/log" "github.com/spf13/cobra" + "github.com/onflow/flow-go/access" "github.com/onflow/flow-go/cmd/util/ledger/util" "github.com/onflow/flow-go/cmd/util/ledger/util/registers" + "github.com/onflow/flow-go/engine/access/rest" + "github.com/onflow/flow-go/engine/access/state_stream/backend" + "github.com/onflow/flow-go/engine/access/subscription" "github.com/onflow/flow-go/engine/execution/computation" "github.com/onflow/flow-go/fvm" + "github.com/onflow/flow-go/fvm/storage/snapshot" "github.com/onflow/flow-go/ledger" "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/module/metrics" ) var ( @@ -21,6 +31,8 @@ var ( flagState string flagStateCommitment string flagChain string + flagServe bool + flagPort int ) var Cmd = &cobra.Command{ @@ -60,6 +72,20 @@ func init() { "Chain name", ) _ = Cmd.MarkFlagRequired("chain") + + Cmd.Flags().BoolVar( + &flagServe, + "serve", + false, + "serve with an HTTP server", + ) + + Cmd.Flags().IntVar( + &flagPort, + "port", + 8000, + "port for HTTP server", + ) } func run(*cobra.Command, []string) { @@ -75,15 +101,14 @@ func run(*cobra.Command, []string) { chainID := flow.ChainID(flagChain) // Validate chain ID - _ = chainID.Chain() + chain := chainID.Chain() - code, err := io.ReadAll(os.Stdin) - if err != nil { - log.Fatal().Msgf("failed to read script: %s", err) - } - - var payloads []*ledger.Payload + log.Info().Msg("loading state ...") + var ( + err error + payloads []*ledger.Payload + ) if flagPayloads != "" { _, payloads, err = util.ReadPayloadFile(log.Logger, flagPayloads) } else { @@ -125,23 +150,390 @@ func run(*cobra.Command, []string) { vm := fvm.NewVirtualMachine() + if flagServe { + + api := &api{ + chainID: chainID, + vm: vm, + ctx: ctx, + storageSnapshot: storageSnapshot, + } + + server, err := rest.NewServer( + api, + rest.Config{ + ListenAddress: fmt.Sprintf(":%d", flagPort), + }, + log.Logger, + chain, + metrics.NewNoopCollector(), + nil, + backend.Config{}, + ) + if err != nil { + log.Fatal().Err(err).Msg("failed to create server") + } + + log.Info().Msgf("serving on port %d", flagPort) + + err = server.ListenAndServe() + if err != nil { + log.Info().Msg("server stopped") + } + } else { + code, err := io.ReadAll(os.Stdin) + if err != nil { + log.Fatal().Msgf("failed to read script: %s", err) + } + + encodedResult, err := runScript(vm, ctx, storageSnapshot, code, nil) + if err != nil { + log.Fatal().Err(err).Msg("failed to run script") + } + + _, _ = os.Stdout.Write(encodedResult) + } +} + +func runScript( + vm *fvm.VirtualMachine, + ctx fvm.Context, + storageSnapshot snapshot.StorageSnapshot, + code []byte, + arguments [][]byte, +) ( + encodedResult []byte, + err error, +) { _, res, err := vm.Run( ctx, - fvm.Script(code), + fvm.Script(code).WithArguments(arguments...), storageSnapshot, ) if err != nil { - log.Fatal().Msgf("failed to run script: %s", err) + return nil, err } if res.Err != nil { - log.Fatal().Msgf("script failed: %s", res.Err) + return nil, res.Err } encoded, err := jsoncdc.Encode(res.Value) if err != nil { - log.Fatal().Msgf("failed to encode result: %s", err) + return nil, err } - _, _ = os.Stdout.Write(encoded) + return encoded, nil +} + +type api struct { + chainID flow.ChainID + vm *fvm.VirtualMachine + ctx fvm.Context + storageSnapshot registers.StorageSnapshot +} + +var _ access.API = &api{} + +func (*api) Ping(_ context.Context) error { + return nil +} + +func (a *api) GetNetworkParameters(_ context.Context) access.NetworkParameters { + return access.NetworkParameters{ + ChainID: a.chainID, + } +} + +func (*api) GetNodeVersionInfo(_ context.Context) (*access.NodeVersionInfo, error) { + return nil, errors.New("unimplemented") +} + +func (*api) GetLatestBlockHeader(_ context.Context, _ bool) (*flow.Header, flow.BlockStatus, error) { + return nil, flow.BlockStatusUnknown, errors.New("unimplemented") +} + +func (*api) GetBlockHeaderByHeight(_ context.Context, _ uint64) (*flow.Header, flow.BlockStatus, error) { + return nil, flow.BlockStatusUnknown, errors.New("unimplemented") +} + +func (*api) GetBlockHeaderByID(_ context.Context, _ flow.Identifier) (*flow.Header, flow.BlockStatus, error) { + return nil, flow.BlockStatusUnknown, errors.New("unimplemented") +} + +func (*api) GetLatestBlock(_ context.Context, _ bool) (*flow.Block, flow.BlockStatus, error) { + return nil, flow.BlockStatusUnknown, errors.New("unimplemented") +} + +func (*api) GetBlockByHeight(_ context.Context, _ uint64) (*flow.Block, flow.BlockStatus, error) { + return nil, flow.BlockStatusUnknown, errors.New("unimplemented") +} + +func (*api) GetBlockByID(_ context.Context, _ flow.Identifier) (*flow.Block, flow.BlockStatus, error) { + return nil, flow.BlockStatusUnknown, errors.New("unimplemented") +} + +func (*api) GetCollectionByID(_ context.Context, _ flow.Identifier) (*flow.LightCollection, error) { + return nil, errors.New("unimplemented") +} + +func (*api) GetFullCollectionByID(_ context.Context, _ flow.Identifier) (*flow.Collection, error) { + return nil, errors.New("unimplemented") +} + +func (*api) SendTransaction(_ context.Context, _ *flow.TransactionBody) error { + return errors.New("unimplemented") +} + +func (*api) GetTransaction(_ context.Context, _ flow.Identifier) (*flow.TransactionBody, error) { + return nil, errors.New("unimplemented") +} + +func (*api) GetTransactionsByBlockID(_ context.Context, _ flow.Identifier) ([]*flow.TransactionBody, error) { + return nil, errors.New("unimplemented") +} + +func (*api) GetTransactionResult( + _ context.Context, + _ flow.Identifier, + _ flow.Identifier, + _ flow.Identifier, + _ entities.EventEncodingVersion, +) (*access.TransactionResult, error) { + return nil, errors.New("unimplemented") +} + +func (*api) GetTransactionResultByIndex( + _ context.Context, + _ flow.Identifier, + _ uint32, + _ entities.EventEncodingVersion, +) (*access.TransactionResult, error) { + return nil, errors.New("unimplemented") +} + +func (*api) GetTransactionResultsByBlockID( + _ context.Context, + _ flow.Identifier, + _ entities.EventEncodingVersion, +) ([]*access.TransactionResult, error) { + return nil, errors.New("unimplemented") +} + +func (*api) GetSystemTransaction( + _ context.Context, + _ flow.Identifier, +) (*flow.TransactionBody, error) { + return nil, errors.New("unimplemented") +} + +func (*api) GetSystemTransactionResult( + _ context.Context, + _ flow.Identifier, + _ entities.EventEncodingVersion, +) (*access.TransactionResult, error) { + return nil, errors.New("unimplemented") +} + +func (*api) GetAccount(_ context.Context, _ flow.Address) (*flow.Account, error) { + return nil, errors.New("unimplemented") +} + +func (*api) GetAccountAtLatestBlock(_ context.Context, _ flow.Address) (*flow.Account, error) { + return nil, errors.New("unimplemented") +} + +func (*api) GetAccountAtBlockHeight(_ context.Context, _ flow.Address, _ uint64) (*flow.Account, error) { + return nil, errors.New("unimplemented") +} + +func (*api) GetAccountBalanceAtLatestBlock(_ context.Context, _ flow.Address) (uint64, error) { + return 0, errors.New("unimplemented") +} + +func (*api) GetAccountBalanceAtBlockHeight( + _ context.Context, + _ flow.Address, + _ uint64, +) (uint64, error) { + return 0, errors.New("unimplemented") +} + +func (*api) GetAccountKeyAtLatestBlock( + _ context.Context, + _ flow.Address, + _ uint32, +) (*flow.AccountPublicKey, error) { + return nil, errors.New("unimplemented") +} + +func (*api) GetAccountKeyAtBlockHeight( + _ context.Context, + _ flow.Address, + _ uint32, + _ uint64, +) (*flow.AccountPublicKey, error) { + return nil, errors.New("unimplemented") +} + +func (*api) GetAccountKeysAtLatestBlock( + _ context.Context, + _ flow.Address, +) ([]flow.AccountPublicKey, error) { + return nil, errors.New("unimplemented") +} + +func (*api) GetAccountKeysAtBlockHeight( + _ context.Context, + _ flow.Address, + _ uint64, +) ([]flow.AccountPublicKey, error) { + return nil, errors.New("unimplemented") +} + +func (a *api) ExecuteScriptAtLatestBlock( + _ context.Context, + script []byte, + arguments [][]byte, +) ([]byte, error) { + return runScript( + a.vm, + a.ctx, + a.storageSnapshot, + script, + arguments, + ) +} + +func (*api) ExecuteScriptAtBlockHeight( + _ context.Context, + _ uint64, + _ []byte, + _ [][]byte, +) ([]byte, error) { + return nil, errors.New("unimplemented") +} + +func (*api) ExecuteScriptAtBlockID( + _ context.Context, + _ flow.Identifier, + _ []byte, + _ [][]byte, +) ([]byte, error) { + return nil, errors.New("unimplemented") +} + +func (a *api) GetEventsForHeightRange( + _ context.Context, + _ string, + _, _ uint64, + _ entities.EventEncodingVersion, +) ([]flow.BlockEvents, error) { + return nil, errors.New("unimplemented") +} + +func (a *api) GetEventsForBlockIDs( + _ context.Context, + _ string, + _ []flow.Identifier, + _ entities.EventEncodingVersion, +) ([]flow.BlockEvents, error) { + return nil, errors.New("unimplemented") +} + +func (*api) GetLatestProtocolStateSnapshot(_ context.Context) ([]byte, error) { + return nil, errors.New("unimplemented") +} + +func (*api) GetProtocolStateSnapshotByBlockID(_ context.Context, _ flow.Identifier) ([]byte, error) { + return nil, errors.New("unimplemented") +} + +func (*api) GetProtocolStateSnapshotByHeight(_ context.Context, _ uint64) ([]byte, error) { + return nil, errors.New("unimplemented") +} + +func (*api) GetExecutionResultForBlockID(_ context.Context, _ flow.Identifier) (*flow.ExecutionResult, error) { + return nil, errors.New("unimplemented") +} + +func (*api) GetExecutionResultByID(_ context.Context, _ flow.Identifier) (*flow.ExecutionResult, error) { + return nil, errors.New("unimplemented") +} + +func (*api) SubscribeBlocksFromStartBlockID( + _ context.Context, + _ flow.Identifier, + _ flow.BlockStatus, +) subscription.Subscription { + return nil +} + +func (*api) SubscribeBlocksFromStartHeight( + _ context.Context, + _ uint64, + _ flow.BlockStatus, +) subscription.Subscription { + return nil +} + +func (*api) SubscribeBlocksFromLatest( + _ context.Context, + _ flow.BlockStatus, +) subscription.Subscription { + return nil +} + +func (*api) SubscribeBlockHeadersFromStartBlockID( + _ context.Context, + _ flow.Identifier, + _ flow.BlockStatus, +) subscription.Subscription { + return nil +} + +func (*api) SubscribeBlockHeadersFromStartHeight( + _ context.Context, + _ uint64, + _ flow.BlockStatus, +) subscription.Subscription { + return nil +} + +func (*api) SubscribeBlockHeadersFromLatest( + _ context.Context, + _ flow.BlockStatus, +) subscription.Subscription { + return nil +} + +func (*api) SubscribeBlockDigestsFromStartBlockID( + _ context.Context, + _ flow.Identifier, + _ flow.BlockStatus, +) subscription.Subscription { + return nil +} + +func (*api) SubscribeBlockDigestsFromStartHeight( + _ context.Context, + _ uint64, + _ flow.BlockStatus, +) subscription.Subscription { + return nil +} + +func (*api) SubscribeBlockDigestsFromLatest( + _ context.Context, + _ flow.BlockStatus, +) subscription.Subscription { + return nil +} + +func (*api) SubscribeTransactionStatuses( + _ context.Context, + _ *flow.TransactionBody, + _ entities.EventEncodingVersion, +) subscription.Subscription { + return nil } diff --git a/cmd/util/ledger/migrations/account_based_migration.go b/cmd/util/ledger/migrations/account_based_migration.go index 22179c53b8f..b6b4f15b369 100644 --- a/cmd/util/ledger/migrations/account_based_migration.go +++ b/cmd/util/ledger/migrations/account_based_migration.go @@ -8,7 +8,7 @@ import ( syncAtomic "sync/atomic" "time" - "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/common" "github.com/rs/zerolog" "golang.org/x/sync/errgroup" diff --git a/cmd/util/ledger/migrations/account_based_migration_test.go b/cmd/util/ledger/migrations/account_based_migration_test.go index f0ceaaac6b2..c06a6a7f090 100644 --- a/cmd/util/ledger/migrations/account_based_migration_test.go +++ b/cmd/util/ledger/migrations/account_based_migration_test.go @@ -3,10 +3,9 @@ package migrations import ( "context" "fmt" - "testing" - "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/common" "github.com/rs/zerolog" "github.com/stretchr/testify/require" diff --git a/cmd/util/ledger/migrations/account_storage_migration.go b/cmd/util/ledger/migrations/account_storage_migration.go index 444819ffac6..7b80f7560d6 100644 --- a/cmd/util/ledger/migrations/account_storage_migration.go +++ b/cmd/util/ledger/migrations/account_storage_migration.go @@ -3,9 +3,9 @@ package migrations import ( "fmt" + "github.com/onflow/cadence/common" + "github.com/onflow/cadence/interpreter" "github.com/onflow/cadence/runtime" - "github.com/onflow/cadence/runtime/common" - "github.com/onflow/cadence/runtime/interpreter" "github.com/rs/zerolog" "github.com/onflow/flow-go/cmd/util/ledger/util/registers" diff --git a/cmd/util/ledger/migrations/burner.go b/cmd/util/ledger/migrations/burner.go deleted file mode 100644 index 726da8f4ea4..00000000000 --- a/cmd/util/ledger/migrations/burner.go +++ /dev/null @@ -1,27 +0,0 @@ -package migrations - -import ( - coreContracts "github.com/onflow/flow-core-contracts/lib/go/contracts" - "github.com/rs/zerolog" - - "github.com/onflow/flow-go/model/flow" -) - -func NewBurnerDeploymentMigration( - chainID flow.ChainID, - logger zerolog.Logger, -) RegistersMigration { - address := BurnerAddressForChain(chainID) - return NewDeploymentMigration( - chainID, - Contract{ - Name: "Burner", - Code: coreContracts.Burner(), - }, - address, - map[flow.Address]struct{}{ - address: {}, - }, - logger, - ) -} diff --git a/cmd/util/ledger/migrations/cadence.go b/cmd/util/ledger/migrations/cadence.go deleted file mode 100644 index 2cc5c9154ed..00000000000 --- a/cmd/util/ledger/migrations/cadence.go +++ /dev/null @@ -1,855 +0,0 @@ -package migrations - -import ( - "context" - _ "embed" - "fmt" - - "github.com/rs/zerolog" - - "github.com/onflow/cadence/migrations/capcons" - "github.com/onflow/cadence/migrations/statictypes" - "github.com/onflow/cadence/runtime" - "github.com/onflow/cadence/runtime/common" - "github.com/onflow/cadence/runtime/interpreter" - - "github.com/onflow/flow-go/cmd/util/ledger/reporters" - "github.com/onflow/flow-go/cmd/util/ledger/util" - "github.com/onflow/flow-go/cmd/util/ledger/util/registers" - "github.com/onflow/flow-go/fvm/environment" - "github.com/onflow/flow-go/fvm/systemcontracts" - "github.com/onflow/flow-go/fvm/tracing" - "github.com/onflow/flow-go/model/flow" -) - -func NewInterfaceTypeConversionRules(chainID flow.ChainID) StaticTypeMigrationRules { - systemContracts := systemcontracts.SystemContractsForChain(chainID) - - oldFungibleTokenResolverType, newFungibleTokenResolverType := - newFungibleTokenMetadataViewsToViewResolverRule(systemContracts, "Resolver") - - oldFungibleTokenResolverCollectionType, newFungibleTokenResolverCollectionType := - newFungibleTokenMetadataViewsToViewResolverRule(systemContracts, "ResolverCollection") - - oldNonFungibleTokenINFTType, newNonFungibleTokenNFTType := - nonFungibleTokenInterfaceToInterfaceRule(systemContracts, "INFT", "NFT") - - return StaticTypeMigrationRules{ - oldFungibleTokenResolverType.ID(): newFungibleTokenResolverType, - oldFungibleTokenResolverCollectionType.ID(): newFungibleTokenResolverCollectionType, - oldNonFungibleTokenINFTType.ID(): newNonFungibleTokenNFTType, - } -} - -func NewCompositeTypeConversionRules( - chainID flow.ChainID, - legacyTypeRequirements *LegacyTypeRequirements, -) StaticTypeMigrationRules { - systemContracts := systemcontracts.SystemContractsForChain(chainID) - - oldFungibleTokenVaultCompositeType, newFungibleTokenVaultType := - fungibleTokenRule(systemContracts, "Vault") - oldNonFungibleTokenNFTCompositeType, newNonFungibleTokenNFTType := - nonFungibleTokenCompositeToInterfaceRule(systemContracts, "NFT") - oldNonFungibleTokenCollectionCompositeType, newNonFungibleTokenCollectionType := - nonFungibleTokenCompositeToInterfaceRule(systemContracts, "Collection") - - rules := StaticTypeMigrationRules{ - oldFungibleTokenVaultCompositeType.ID(): newFungibleTokenVaultType, - oldNonFungibleTokenNFTCompositeType.ID(): newNonFungibleTokenNFTType, - oldNonFungibleTokenCollectionCompositeType.ID(): newNonFungibleTokenCollectionType, - } - - for _, typeRequirement := range legacyTypeRequirements.typeRequirements { - oldType, newType := compositeToInterfaceRule( - typeRequirement.Address, - typeRequirement.ContractName, - typeRequirement.TypeName, - ) - - rules[oldType.ID()] = newType - } - - return rules -} - -func NewCadence1InterfaceStaticTypeConverter(chainID flow.ChainID) statictypes.InterfaceTypeConverterFunc { - return NewStaticTypeMigration[*interpreter.InterfaceStaticType]( - func() StaticTypeMigrationRules { - return NewInterfaceTypeConversionRules(chainID) - }, - ) -} - -func NewCadence1CompositeStaticTypeConverter( - chainID flow.ChainID, - legacyTypeRequirements *LegacyTypeRequirements, -) statictypes.CompositeTypeConverterFunc { - return NewStaticTypeMigration[*interpreter.CompositeStaticType]( - func() StaticTypeMigrationRules { - return NewCompositeTypeConversionRules(chainID, legacyTypeRequirements) - }, - ) -} - -func nonFungibleTokenCompositeToInterfaceRule( - systemContracts *systemcontracts.SystemContracts, - identifier string, -) ( - *interpreter.CompositeStaticType, - *interpreter.IntersectionStaticType, -) { - contract := systemContracts.NonFungibleToken - - return compositeToInterfaceRule( - common.Address(contract.Address), - contract.Name, - identifier, - ) -} - -func compositeToInterfaceRule( - address common.Address, - contractName string, - typeName string, -) ( - *interpreter.CompositeStaticType, - *interpreter.IntersectionStaticType, -) { - qualifiedIdentifier := fmt.Sprintf("%s.%s", contractName, typeName) - - location := common.AddressLocation{ - Address: address, - Name: contractName, - } - - nftTypeID := location.TypeID(nil, qualifiedIdentifier) - - oldType := &interpreter.CompositeStaticType{ - Location: location, - QualifiedIdentifier: qualifiedIdentifier, - TypeID: nftTypeID, - } - - newType := &interpreter.IntersectionStaticType{ - Types: []*interpreter.InterfaceStaticType{ - { - Location: location, - QualifiedIdentifier: qualifiedIdentifier, - TypeID: nftTypeID, - }, - }, - } - - return oldType, newType -} - -func nonFungibleTokenInterfaceToInterfaceRule( - systemContracts *systemcontracts.SystemContracts, - oldIdentifier string, - newIdentifier string, -) ( - *interpreter.InterfaceStaticType, - *interpreter.InterfaceStaticType, -) { - contract := systemContracts.NonFungibleToken - - oldQualifiedIdentifier := fmt.Sprintf("%s.%s", contract.Name, oldIdentifier) - newQualifiedIdentifier := fmt.Sprintf("%s.%s", contract.Name, newIdentifier) - - location := common.AddressLocation{ - Address: common.Address(contract.Address), - Name: contract.Name, - } - - oldTypeID := location.TypeID(nil, oldQualifiedIdentifier) - newTypeID := location.TypeID(nil, newQualifiedIdentifier) - - oldType := &interpreter.InterfaceStaticType{ - Location: location, - QualifiedIdentifier: oldQualifiedIdentifier, - TypeID: oldTypeID, - } - - newType := &interpreter.InterfaceStaticType{ - Location: location, - QualifiedIdentifier: newQualifiedIdentifier, - TypeID: newTypeID, - } - - return oldType, newType -} - -func fungibleTokenRule( - systemContracts *systemcontracts.SystemContracts, - identifier string, -) ( - *interpreter.CompositeStaticType, - *interpreter.IntersectionStaticType, -) { - contract := systemContracts.FungibleToken - - qualifiedIdentifier := fmt.Sprintf("%s.%s", contract.Name, identifier) - - location := common.AddressLocation{ - Address: common.Address(contract.Address), - Name: contract.Name, - } - - vaultTypeID := location.TypeID(nil, qualifiedIdentifier) - - oldType := &interpreter.CompositeStaticType{ - Location: location, - QualifiedIdentifier: qualifiedIdentifier, - TypeID: vaultTypeID, - } - - newType := &interpreter.IntersectionStaticType{ - Types: []*interpreter.InterfaceStaticType{ - { - Location: location, - QualifiedIdentifier: qualifiedIdentifier, - TypeID: vaultTypeID, - }, - }, - } - - return oldType, newType -} - -func newFungibleTokenMetadataViewsToViewResolverRule( - systemContracts *systemcontracts.SystemContracts, - typeName string, -) ( - *interpreter.InterfaceStaticType, - *interpreter.InterfaceStaticType, -) { - oldContract := systemContracts.MetadataViews - newContract := systemContracts.ViewResolver - - oldLocation := common.AddressLocation{ - Address: common.Address(oldContract.Address), - Name: oldContract.Name, - } - - newLocation := common.AddressLocation{ - Address: common.Address(newContract.Address), - Name: newContract.Name, - } - - oldQualifiedIdentifier := fmt.Sprintf("%s.%s", oldContract.Name, typeName) - newQualifiedIdentifier := fmt.Sprintf("%s.%s", newContract.Name, typeName) - - oldType := &interpreter.InterfaceStaticType{ - Location: oldLocation, - QualifiedIdentifier: oldQualifiedIdentifier, - TypeID: oldLocation.TypeID(nil, oldQualifiedIdentifier), - } - - newType := &interpreter.InterfaceStaticType{ - Location: newLocation, - QualifiedIdentifier: newQualifiedIdentifier, - TypeID: newLocation.TypeID(nil, newQualifiedIdentifier), - } - - return oldType, newType -} - -type NamedMigration struct { - Name string - Migrate RegistersMigration -} - -type IssueStorageCapConMigration struct { - name string - chainID flow.ChainID - accountsCapabilities *capcons.AccountsCapabilities - interpreterMigrationRuntimeConfig InterpreterMigrationRuntimeConfig - programs map[runtime.Location]*interpreter.Program - typedCapabilityMapping *capcons.PathTypeCapabilityMapping - untypedCapabilityMapping *capcons.PathCapabilityMapping - reporter reporters.ReportWriter - logVerboseDiff bool - verboseErrorOutput bool - errorMessageHandler *errorMessageHandler - log zerolog.Logger -} - -const issueStorageCapConMigrationReporterName = "cadence-storage-capcon-issue-migration" - -func NewIssueStorageCapConMigration( - rwf reporters.ReportWriterFactory, - errorMessageHandler *errorMessageHandler, - chainID flow.ChainID, - storageDomainCapabilities *capcons.AccountsCapabilities, - programs map[runtime.Location]*interpreter.Program, - typedStorageCapabilityMapping *capcons.PathTypeCapabilityMapping, - untypedStorageCapabilityMapping *capcons.PathCapabilityMapping, - opts Options, -) *IssueStorageCapConMigration { - return &IssueStorageCapConMigration{ - name: "cadence_storage_cap_con_issue_migration", - reporter: rwf.ReportWriter(issueStorageCapConMigrationReporterName), - chainID: chainID, - accountsCapabilities: storageDomainCapabilities, - programs: programs, - typedCapabilityMapping: typedStorageCapabilityMapping, - untypedCapabilityMapping: untypedStorageCapabilityMapping, - logVerboseDiff: opts.LogVerboseDiff, - verboseErrorOutput: opts.VerboseErrorOutput, - errorMessageHandler: errorMessageHandler, - } -} - -func (m *IssueStorageCapConMigration) InitMigration( - log zerolog.Logger, - _ *registers.ByAccount, - _ int, -) error { - m.log = log.With().Str("migration", m.name).Logger() - - // During the migration, we only provide already checked programs, - // no parsing/checking of contracts is expected. - - m.interpreterMigrationRuntimeConfig = InterpreterMigrationRuntimeConfig{ - GetOrLoadProgram: func( - location runtime.Location, - _ func() (*interpreter.Program, error), - ) (*interpreter.Program, error) { - program, ok := m.programs[location] - if !ok { - return nil, fmt.Errorf("program not found: %s", location) - } - return program, nil - }, - GetCode: func(_ common.AddressLocation) ([]byte, error) { - return nil, fmt.Errorf("unexpected call to GetCode") - }, - GetContractNames: func(address flow.Address) ([]string, error) { - return nil, fmt.Errorf("unexpected call to GetContractNames") - }, - } - - return nil -} - -func (m *IssueStorageCapConMigration) MigrateAccount( - _ context.Context, - address common.Address, - accountRegisters *registers.AccountRegisters, -) error { - accountCapabilities := m.accountsCapabilities.Get(address) - if accountCapabilities == nil { - return nil - } - - // Create all the runtime components we need for the migration - migrationRuntime, err := NewInterpreterMigrationRuntime( - accountRegisters, - m.chainID, - m.interpreterMigrationRuntimeConfig, - ) - if err != nil { - return fmt.Errorf("failed to create interpreter migration runtime: %w", err) - } - - idGenerator := environment.NewAccountLocalIDGenerator( - tracing.NewMockTracerSpan(), - util.NopMeter{}, - migrationRuntime.Accounts, - ) - - handler := capabilityControllerHandler{ - idGenerator: idGenerator, - } - - reporter := newValueMigrationReporter( - m.reporter, - m.log, - m.errorMessageHandler, - m.verboseErrorOutput, - ) - - inter := migrationRuntime.Interpreter - - capcons.IssueAccountCapabilities( - inter, - migrationRuntime.Storage, - reporter, - address, - accountCapabilities, - handler, - m.typedCapabilityMapping, - m.untypedCapabilityMapping, - func(valueType interpreter.StaticType) interpreter.Authorization { - // TODO: - return interpreter.UnauthorizedAccess - }, - ) - - err = migrationRuntime.Storage.NondeterministicCommit(inter, false) - if err != nil { - return fmt.Errorf("failed to commit changes: %w", err) - } - - // Commit/finalize the transaction - - expectedAddresses := map[flow.Address]struct{}{ - flow.Address(address): {}, - } - - err = migrationRuntime.Commit(expectedAddresses, m.log) - if err != nil { - return fmt.Errorf("failed to commit: %w", err) - } - - return nil -} - -func (m *IssueStorageCapConMigration) Close() error { - m.reporter.Close() - return nil -} - -var _ AccountBasedMigration = &IssueStorageCapConMigration{} - -func NewCadence1ValueMigrations( - log zerolog.Logger, - rwf reporters.ReportWriterFactory, - importantLocations map[common.AddressLocation]struct{}, - legacyTypeRequirements *LegacyTypeRequirements, - opts Options, -) (migs []NamedMigration) { - - // Populated by CadenceLinkValueMigration, - // used by CadenceCapabilityValueMigration - privatePublicCapabilityMapping := &capcons.PathCapabilityMapping{} - // Populated by IssueStorageCapConMigration - // used by CadenceCapabilityValueMigration - typedStorageCapabilityMapping := &capcons.PathTypeCapabilityMapping{} - untypedStorageCapabilityMapping := &capcons.PathCapabilityMapping{} - - // Populated by StorageCapMigration, - // used by IssueStorageCapConMigration - storageDomainCapabilities := &capcons.AccountsCapabilities{} - - errorMessageHandler := &errorMessageHandler{} - - // The value migrations are run as account-based migrations, - // i.e. the migrations are only given the payloads for the account to be migrated. - // However, the migrations need to be able to get the code for contracts of any account. - // - // To achieve this, the contracts are extracted from the payloads once, - // before the value migrations are run. - - programs := make(map[common.Location]*interpreter.Program, 1000) - - migs = []NamedMigration{ - { - Name: "cleanup-contracts", - Migrate: NewAccountBasedMigration( - log, - opts.NWorker, - []AccountBasedMigration{ - NewContractCleanupMigration(rwf), - }, - ), - }, - { - Name: "check-contracts", - Migrate: NewContractCheckingMigration( - log, - rwf, - opts.ChainID, - opts.VerboseErrorOutput, - importantLocations, - programs, - ), - }, - } - - for index, migrationConstructor := range []func(opts Options) (string, AccountBasedMigration){ - func(opts Options) (string, AccountBasedMigration) { - migration := NewCadence1ValueMigration( - rwf, - errorMessageHandler, - programs, - NewCadence1CompositeStaticTypeConverter(opts.ChainID, legacyTypeRequirements), - NewCadence1InterfaceStaticTypeConverter(opts.ChainID), - storageDomainCapabilities, - opts, - ) - return migration.name, migration - }, - func(opts Options) (string, AccountBasedMigration) { - migration := NewIssueStorageCapConMigration( - rwf, - errorMessageHandler, - opts.ChainID, - storageDomainCapabilities, - programs, - typedStorageCapabilityMapping, - untypedStorageCapabilityMapping, - opts, - ) - return migration.name, migration - - }, - func(opts Options) (string, AccountBasedMigration) { - migration := NewCadence1LinkValueMigration( - rwf, - errorMessageHandler, - programs, - privatePublicCapabilityMapping, - opts, - ) - return migration.name, migration - }, - func(opts Options) (string, AccountBasedMigration) { - migration := NewCadence1CapabilityValueMigration( - rwf, - errorMessageHandler, - programs, - privatePublicCapabilityMapping, - typedStorageCapabilityMapping, - untypedStorageCapabilityMapping, - opts, - ) - return migration.name, migration - }, - } { - opts := opts - // Only check storage health before the first migration - opts.CheckStorageHealthBeforeMigration = opts.CheckStorageHealthBeforeMigration && index == 0 - - name, accountBasedMigration := migrationConstructor(opts) - - migs = append( - migs, - NamedMigration{ - Name: name, - Migrate: NewAccountBasedMigration( - log, - opts.NWorker, - []AccountBasedMigration{ - accountBasedMigration, - }, - ), - }, - ) - } - - if opts.ReportMetrics { - migs = append(migs, NamedMigration{ - Name: metricsCollectingMigrationName, - Migrate: NewAccountBasedMigration( - log, - opts.NWorker, - []AccountBasedMigration{ - NewMetricsCollectingMigration( - log, - opts.ChainID, - rwf, - programs, - ), - }, - ), - }) - } - - return -} - -const stagedContractUpdateMigrationName = "staged-contracts-update-migration" - -func NewCadence1ContractsMigrations( - log zerolog.Logger, - rwf reporters.ReportWriterFactory, - importantLocations map[common.AddressLocation]struct{}, - legacyTypeRequirements *LegacyTypeRequirements, - opts Options, -) ( - migs []NamedMigration, -) { - - stagedContractsMigrationOptions := StagedContractsMigrationOptions{ - ChainID: opts.ChainID, - VerboseErrorOutput: opts.VerboseErrorOutput, - } - - systemContractsMigrationOptions := SystemContractsMigrationOptions{ - StagedContractsMigrationOptions: stagedContractsMigrationOptions, - EVM: opts.EVMContractChange, - Burner: opts.BurnerContractChange, - } - - systemContractsMigration := NewSystemContractsMigration( - log, - rwf, - importantLocations, - systemContractsMigrationOptions, - ) - - stagedContractsMigration := NewStagedContractsMigration( - "StagedContractsMigration", - "staged-contracts-migration", - log, - rwf, - legacyTypeRequirements, - stagedContractsMigrationOptions, - ).WithContractUpdateValidation(). - WithStagedContractUpdates(opts.StagedContracts) - - toAccountBasedMigration := func(migration AccountBasedMigration) RegistersMigration { - return NewAccountBasedMigration( - log, - opts.NWorker, - []AccountBasedMigration{ - migration, - }, - ) - } - - switch opts.EVMContractChange { - case EVMContractChangeNone: - // NO-OP - - case EVMContractChangeUpdateFull: - // handled in system contract updates (SystemContractChanges) - - case EVMContractChangeDeployFull, - EVMContractChangeDeployMinimalAndUpdateFull: - - full := opts.EVMContractChange == EVMContractChangeDeployFull - - migs = append( - migs, - NamedMigration{ - Name: "evm-deployment-migration", - Migrate: NewEVMDeploymentMigration(opts.ChainID, log, full), - }, - ) - } - - if opts.BurnerContractChange == BurnerContractChangeDeploy { - migs = append( - migs, - NamedMigration{ - Name: "burner-deployment-migration", - Migrate: NewBurnerDeploymentMigration(opts.ChainID, log), - }, - ) - } - - migs = append( - migs, - NamedMigration{ - Name: "system-contracts-update-migration", - Migrate: toAccountBasedMigration(systemContractsMigration), - }, - NamedMigration{ - Name: stagedContractUpdateMigrationName, - Migrate: toAccountBasedMigration(stagedContractsMigration), - }, - ) - - return migs -} - -var testnetAccountsWithBrokenSlabReferences = func() map[common.Address]struct{} { - testnetAddresses := map[common.Address]struct{}{ - mustHexToAddress("434a1f199a7ae3ba"): {}, - mustHexToAddress("454c9991c2b8d947"): {}, - mustHexToAddress("48602d8056ff9d93"): {}, - mustHexToAddress("5d63c34d7f05e5a4"): {}, - mustHexToAddress("5e3448b3cffb97f2"): {}, - mustHexToAddress("7d8c7e050c694eaa"): {}, - mustHexToAddress("ba53f16ede01972d"): {}, - mustHexToAddress("c843c1f5a4805c3a"): {}, - mustHexToAddress("48d3be92e6e4a973"): {}, - } - - for address := range testnetAddresses { - if !flow.Testnet.Chain().IsValid(flow.Address(address)) { - panic(fmt.Sprintf("invalid testnet address: %s", address.Hex())) - } - } - - return testnetAddresses -}() - -func mustHexToAddress(hex string) common.Address { - address, err := common.HexToAddress(hex) - if err != nil { - panic(err) - } - return address -} - -type Options struct { - NWorker int - DiffMigrations bool - LogVerboseDiff bool - CheckStorageHealthBeforeMigration bool - VerboseErrorOutput bool - ChainID flow.ChainID - EVMContractChange EVMContractChange - BurnerContractChange BurnerContractChange - StagedContracts []StagedContract - Prune bool - MaxAccountSize uint64 - FixSlabsWithBrokenReferences bool - FilterUnreferencedSlabs bool - ReportMetrics bool - CacheStaticTypeMigrationResults bool - CacheEntitlementsMigrationResults bool -} - -func NewCadence1Migrations( - log zerolog.Logger, - outputDir string, - rwf reporters.ReportWriterFactory, - opts Options, -) (migs []NamedMigration) { - - if opts.MaxAccountSize > 0 { - - maxSizeExceptions := map[string]struct{}{} - - systemContracts := systemcontracts.SystemContractsForChain(opts.ChainID) - for _, systemContract := range systemContracts.All() { - maxSizeExceptions[string(systemContract.Address.Bytes())] = struct{}{} - } - - migs = append( - migs, - NamedMigration{ - Name: "account-size-filter-migration", - Migrate: NewAccountSizeFilterMigration( - opts.MaxAccountSize, - maxSizeExceptions, - log, - ), - }, - ) - } - - if opts.FixSlabsWithBrokenReferences || opts.FilterUnreferencedSlabs { - - var accountBasedMigrations []AccountBasedMigration - - if opts.FixSlabsWithBrokenReferences { - accountBasedMigrations = append( - accountBasedMigrations, - NewFixBrokenReferencesInSlabsMigration(outputDir, rwf, testnetAccountsWithBrokenSlabReferences), - ) - } - - if opts.FilterUnreferencedSlabs { - accountBasedMigrations = append( - accountBasedMigrations, - // NOTE: migration to filter unreferenced slabs should happen - // after migration to fix slabs with references to nonexistent slabs. - NewFilterUnreferencedSlabsMigration(outputDir, rwf), - ) - } - - migs = append( - migs, - NamedMigration{ - Name: "fix-slabs-migration", - Migrate: NewAccountBasedMigration( - log, - opts.NWorker, - accountBasedMigrations, - ), - }, - ) - } - - if opts.Prune { - migration := NewCadence1PruneMigration(opts.ChainID, log, opts.NWorker) - if migration != nil { - migs = append( - migs, - NamedMigration{ - Name: "prune-migration", - Migrate: migration, - }, - ) - } - } - - importantLocations := make(map[common.AddressLocation]struct{}) - legacyTypeRequirements := &LegacyTypeRequirements{} - - cadenceTypeRequirementsExtractor := NewTypeRequirementsExtractingMigration( - log, - rwf, - importantLocations, - legacyTypeRequirements, - ) - - migs = append( - migs, - NamedMigration{ - Name: "extract-type-requirements", - Migrate: cadenceTypeRequirementsExtractor, - }, - ) - - cadence1ContractsMigrations := NewCadence1ContractsMigrations( - log, - rwf, - importantLocations, - legacyTypeRequirements, - opts, - ) - - migs = append( - migs, - cadence1ContractsMigrations..., - ) - - migs = append( - migs, - NewCadence1ValueMigrations( - log, - rwf, - importantLocations, - legacyTypeRequirements, - opts, - )..., - ) - - switch opts.EVMContractChange { - case EVMContractChangeNone, - EVMContractChangeDeployFull: - // NO-OP - case EVMContractChangeUpdateFull, EVMContractChangeDeployMinimalAndUpdateFull: - migs = append( - migs, - NamedMigration{ - Name: "evm-setup-migration", - Migrate: NewEVMSetupMigration(opts.ChainID, log), - }, - ) - if opts.ChainID == flow.Emulator { - - // In the Emulator the EVM storage account needs to be created - - systemContracts := systemcontracts.SystemContractsForChain(opts.ChainID) - evmStorageAddress := systemContracts.EVMStorage.Address - - migs = append( - migs, - NamedMigration{ - Name: "evm-storage-account-creation-migration", - Migrate: NewAccountCreationMigration(evmStorageAddress, log), - }, - ) - } - } - - return migs -} diff --git a/cmd/util/ledger/migrations/cadence_test.go b/cmd/util/ledger/migrations/cadence_test.go deleted file mode 100644 index bc2d02a0d6a..00000000000 --- a/cmd/util/ledger/migrations/cadence_test.go +++ /dev/null @@ -1,69 +0,0 @@ -package migrations - -import ( - "testing" - - "github.com/onflow/cadence/runtime/common" - "github.com/onflow/cadence/runtime/interpreter" - "github.com/rs/zerolog" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/onflow/flow-go/cmd/util/ledger/util/registers" - "github.com/onflow/flow-go/fvm/environment" - "github.com/onflow/flow-go/model/flow" -) - -func TestMigrateCadence1EmptyContract(t *testing.T) { - - t.Parallel() - - const chainID = flow.Testnet - - address, err := common.HexToAddress("0x4184b8bdf78db9eb") - require.NoError(t, err) - - const contractName = "FungibleToken" - - registersByAccount := registers.NewByAccount() - - err = registersByAccount.Set( - string(address[:]), - flow.ContractKey(contractName), - // some whitespace for testing purposes - []byte(" \t \n "), - ) - require.NoError(t, err) - - encodedContractNames, err := environment.EncodeContractNames([]string{contractName}) - require.NoError(t, err) - - err = registersByAccount.Set( - string(address[:]), - flow.ContractNamesKey, - encodedContractNames, - ) - require.NoError(t, err) - - programs := map[common.Location]*interpreter.Program{} - - rwf := &testReportWriterFactory{} - - // Run contract checking migration - - log := zerolog.Nop() - checkingMigration := NewContractCheckingMigration(log, rwf, chainID, false, nil, programs) - - err = checkingMigration(registersByAccount) - require.NoError(t, err) - - reporter := rwf.reportWriters[contractCheckingReporterName] - assert.Empty(t, reporter.entries) - - // Initialize metrics collecting migration (used to run into unexpected error) - - metricsCollectingMigration := NewMetricsCollectingMigration(log, chainID, rwf, programs) - - err = metricsCollectingMigration.InitMigration(log, registersByAccount, 1) - require.NoError(t, err) -} diff --git a/cmd/util/ledger/migrations/cadence_value_diff.go b/cmd/util/ledger/migrations/cadence_value_diff.go index b8ea338c6c1..1887fad1264 100644 --- a/cmd/util/ledger/migrations/cadence_value_diff.go +++ b/cmd/util/ledger/migrations/cadence_value_diff.go @@ -4,9 +4,9 @@ import ( "fmt" "time" + "github.com/onflow/cadence/common" + "github.com/onflow/cadence/interpreter" "github.com/onflow/cadence/runtime" - "github.com/onflow/cadence/runtime/common" - "github.com/onflow/cadence/runtime/interpreter" "github.com/rs/zerolog/log" "golang.org/x/sync/errgroup" diff --git a/cmd/util/ledger/migrations/cadence_value_diff_test.go b/cmd/util/ledger/migrations/cadence_value_diff_test.go index be714e88dbf..c125859a24d 100644 --- a/cmd/util/ledger/migrations/cadence_value_diff_test.go +++ b/cmd/util/ledger/migrations/cadence_value_diff_test.go @@ -6,8 +6,8 @@ import ( "strconv" "testing" - "github.com/onflow/cadence/runtime/common" - "github.com/onflow/cadence/runtime/interpreter" + "github.com/onflow/cadence/common" + "github.com/onflow/cadence/interpreter" "github.com/onflow/flow-go/cmd/util/ledger/util/registers" "github.com/onflow/flow-go/fvm/environment" diff --git a/cmd/util/ledger/migrations/cadence_values_migration.go b/cmd/util/ledger/migrations/cadence_values_migration.go deleted file mode 100644 index 4bd2c756cde..00000000000 --- a/cmd/util/ledger/migrations/cadence_values_migration.go +++ /dev/null @@ -1,932 +0,0 @@ -package migrations - -import ( - "context" - "encoding/json" - "fmt" - "io" - "sync" - - "errors" - - "github.com/onflow/cadence/migrations" - "github.com/onflow/cadence/migrations/capcons" - "github.com/onflow/cadence/migrations/entitlements" - "github.com/onflow/cadence/migrations/statictypes" - "github.com/onflow/cadence/migrations/string_normalization" - "github.com/onflow/cadence/migrations/type_keys" - "github.com/onflow/cadence/runtime" - "github.com/onflow/cadence/runtime/common" - cadenceErrors "github.com/onflow/cadence/runtime/errors" - "github.com/onflow/cadence/runtime/interpreter" - "github.com/onflow/cadence/runtime/sema" - "github.com/onflow/cadence/runtime/stdlib" - "github.com/rs/zerolog" - - "github.com/onflow/flow-go/cmd/util/ledger/reporters" - "github.com/onflow/flow-go/cmd/util/ledger/util" - "github.com/onflow/flow-go/cmd/util/ledger/util/registers" - "github.com/onflow/flow-go/fvm/environment" - "github.com/onflow/flow-go/fvm/tracing" - "github.com/onflow/flow-go/ledger" - "github.com/onflow/flow-go/model/flow" -) - -type CadenceBaseMigration struct { - name string - log zerolog.Logger - reporter reporters.ReportWriter - diffReporter reporters.ReportWriter - logVerboseDiff bool - verboseErrorOutput bool - checkStorageHealthBeforeMigration bool - valueMigrations func( - inter *interpreter.Interpreter, - accounts environment.Accounts, - reporter *cadenceValueMigrationReporter, - ) []migrations.ValueMigration - interpreterMigrationRuntimeConfig InterpreterMigrationRuntimeConfig - errorMessageHandler *errorMessageHandler - programs map[runtime.Location]*interpreter.Program - chainID flow.ChainID - nWorkers int -} - -var _ AccountBasedMigration = (*CadenceBaseMigration)(nil) -var _ io.Closer = (*CadenceBaseMigration)(nil) - -func (m *CadenceBaseMigration) Close() error { - // Close the report writer so it flushes to file. - m.reporter.Close() - - if m.diffReporter != nil { - m.diffReporter.Close() - } - - return nil -} - -func (m *CadenceBaseMigration) InitMigration( - log zerolog.Logger, - _ *registers.ByAccount, - nWorkers int, -) error { - m.log = log.With().Str("migration", m.name).Logger() - m.nWorkers = nWorkers - - // During the migration, we only provide already checked programs, - // no parsing/checking of contracts is expected. - - m.interpreterMigrationRuntimeConfig = InterpreterMigrationRuntimeConfig{ - GetOrLoadProgram: func( - location runtime.Location, - _ func() (*interpreter.Program, error), - ) (*interpreter.Program, error) { - program, ok := m.programs[location] - if !ok { - return nil, fmt.Errorf("program not found: %s", location) - } - return program, nil - }, - GetCode: func(_ common.AddressLocation) ([]byte, error) { - return nil, fmt.Errorf("unexpected call to GetCode") - }, - GetContractNames: func(address flow.Address) ([]string, error) { - return nil, fmt.Errorf("unexpected call to GetContractNames") - }, - } - - return nil -} - -func (m *CadenceBaseMigration) MigrateAccount( - _ context.Context, - address common.Address, - accountRegisters *registers.AccountRegisters, -) error { - var oldPayloadsForDiff []*ledger.Payload - if m.diffReporter != nil { - oldPayloadsForDiff = accountRegisters.Payloads() - } - - // Create all the runtime components we need for the migration - migrationRuntime, err := NewInterpreterMigrationRuntime( - accountRegisters, - m.chainID, - m.interpreterMigrationRuntimeConfig, - ) - if err != nil { - return fmt.Errorf("failed to create interpreter migration runtime: %w", err) - } - - storage := migrationRuntime.Storage - - // Check storage health before migration, if enabled. - var storageHealthErrorBefore error - if m.checkStorageHealthBeforeMigration { - - storageHealthErrorBefore = util.CheckStorageHealth(address, storage, accountRegisters, AllStorageMapDomains, m.nWorkers) - if storageHealthErrorBefore != nil { - m.log.Warn(). - Err(storageHealthErrorBefore). - Str("account", address.HexWithPrefix()). - Msg("storage health check before migration failed") - } - } - - migration, err := migrations.NewStorageMigration( - migrationRuntime.Interpreter, - storage, - m.name, - address, - ) - if err != nil { - return fmt.Errorf("failed to create storage migration: %w", err) - } - - reporter := newValueMigrationReporter( - m.reporter, - m.log, - m.errorMessageHandler, - m.verboseErrorOutput, - ) - - valueMigrations := m.valueMigrations( - migrationRuntime.Interpreter, - migrationRuntime.Accounts, - reporter, - ) - - migration.Migrate( - migration.NewValueMigrationsPathMigrator( - reporter, - valueMigrations..., - ), - ) - - err = migration.Commit() - if err != nil { - return fmt.Errorf("failed to commit changes: %w", err) - } - - // Check storage health after migration. - // If the storage health check failed before the migration, we don't need to check it again. - if storageHealthErrorBefore == nil { - storageHealthErrorAfter := storage.CheckHealth() - if storageHealthErrorAfter != nil { - m.log.Err(storageHealthErrorAfter). - Str("account", address.HexWithPrefix()). - Msg("storage health check after migration failed") - } - } - - // Commit/finalize the transaction - - expectedAddresses := map[flow.Address]struct{}{ - flow.Address(address): {}, - } - - err = migrationRuntime.Commit(expectedAddresses, m.log) - if err != nil { - return fmt.Errorf("failed to commit: %w", err) - } - - if m.diffReporter != nil { - newPayloadsForDiff := accountRegisters.Payloads() - - accountDiffReporter := NewCadenceValueDiffReporter( - address, - m.chainID, - m.diffReporter, - m.logVerboseDiff, - m.nWorkers, - ) - - owner := flow.AddressToRegisterOwner(flow.Address(address)) - - oldRegistersForDiff, err := registers.NewAccountRegistersFromPayloads(owner, oldPayloadsForDiff) - if err != nil { - return fmt.Errorf("failed to create registers from old payloads: %w", err) - } - - newRegistersForDiff, err := registers.NewAccountRegistersFromPayloads(owner, newPayloadsForDiff) - if err != nil { - return fmt.Errorf("failed to create registers from new payloads: %w", err) - } - - accountDiffReporter.DiffStates( - oldRegistersForDiff, - newRegistersForDiff, - AllStorageMapDomains, - ) - } - - return nil -} - -const cadenceValueMigrationReporterName = "cadence-value-migration" - -// NewCadence1ValueMigration creates a new CadenceBaseMigration -// which runs some of the Cadence value migrations (static types, entitlements, strings) -func NewCadence1ValueMigration( - rwf reporters.ReportWriterFactory, - errorMessageHandler *errorMessageHandler, - programs map[runtime.Location]*interpreter.Program, - compositeTypeConverter statictypes.CompositeTypeConverterFunc, - interfaceTypeConverter statictypes.InterfaceTypeConverterFunc, - storageDomainCapabilities *capcons.AccountsCapabilities, - opts Options, -) *CadenceBaseMigration { - - var diffReporter reporters.ReportWriter - if opts.DiffMigrations { - diffReporter = rwf.ReportWriter("cadence-value-migration-diff") - } - - var staticTypeMigrationCache migrations.StaticTypeCache - if opts.CacheStaticTypeMigrationResults { - staticTypeMigrationCache = migrations.NewDefaultStaticTypeCache() - } - - var entitlementsMigrationCache migrations.StaticTypeCache - if opts.CacheEntitlementsMigrationResults { - entitlementsMigrationCache = migrations.NewDefaultStaticTypeCache() - } - - return &CadenceBaseMigration{ - name: "cadence_value_migration", - reporter: rwf.ReportWriter(cadenceValueMigrationReporterName), - diffReporter: diffReporter, - logVerboseDiff: opts.LogVerboseDiff, - verboseErrorOutput: opts.VerboseErrorOutput, - checkStorageHealthBeforeMigration: opts.CheckStorageHealthBeforeMigration, - valueMigrations: func( - inter *interpreter.Interpreter, - _ environment.Accounts, - reporter *cadenceValueMigrationReporter, - ) []migrations.ValueMigration { - return []migrations.ValueMigration{ - statictypes.NewStaticTypeMigrationWithCache(staticTypeMigrationCache). - WithCompositeTypeConverter(compositeTypeConverter). - WithInterfaceTypeConverter(interfaceTypeConverter), - entitlements.NewEntitlementsMigrationWithCache(inter, entitlementsMigrationCache), - // After the static type and entitlements migration, we run the type key migration, - // which ensures that even if the previous migrations failed to migrate `Type` values - // used as dictionary keys, they will get re-stored and are accessible by users - // and the mutating iterator of the inlined version of atree - type_keys.NewTypeKeyMigration(), - string_normalization.NewStringNormalizingMigration(), - &capcons.StorageCapMigration{ - StorageDomainCapabilities: storageDomainCapabilities, - }, - } - }, - errorMessageHandler: errorMessageHandler, - programs: programs, - chainID: opts.ChainID, - } -} - -type capabilityControllerHandler struct { - idGenerator environment.AccountLocalIDGenerator -} - -var _ stdlib.CapabilityControllerIssueHandler = capabilityControllerHandler{} -var _ stdlib.CapabilityControllerHandler = capabilityControllerHandler{} - -func (c capabilityControllerHandler) GenerateAccountID(address common.Address) (uint64, error) { - return c.idGenerator.GenerateAccountID(address) -} - -func (capabilityControllerHandler) EmitEvent( - _ *interpreter.Interpreter, - _ interpreter.LocationRange, - _ *sema.CompositeType, - _ []interpreter.Value, -) { - // NO-OP -} - -// NewCadence1LinkValueMigration creates a new CadenceBaseMigration -// which migrates links to capability controllers. -// It populates the given map with the IDs of the capability controller it issues. -func NewCadence1LinkValueMigration( - rwf reporters.ReportWriterFactory, - errorMessageHandler *errorMessageHandler, - programs map[runtime.Location]*interpreter.Program, - capabilityMapping *capcons.PathCapabilityMapping, - opts Options, -) *CadenceBaseMigration { - var diffReporter reporters.ReportWriter - if opts.DiffMigrations { - diffReporter = rwf.ReportWriter("cadence-link-value-migration-diff") - } - - return &CadenceBaseMigration{ - name: "cadence_link_value_migration", - reporter: rwf.ReportWriter("cadence-link-value-migration"), - diffReporter: diffReporter, - logVerboseDiff: opts.LogVerboseDiff, - verboseErrorOutput: opts.VerboseErrorOutput, - checkStorageHealthBeforeMigration: opts.CheckStorageHealthBeforeMigration, - valueMigrations: func( - _ *interpreter.Interpreter, - accounts environment.Accounts, - reporter *cadenceValueMigrationReporter, - ) []migrations.ValueMigration { - - idGenerator := environment.NewAccountLocalIDGenerator( - tracing.NewMockTracerSpan(), - util.NopMeter{}, - accounts, - ) - - handler := capabilityControllerHandler{ - idGenerator: idGenerator, - } - - return []migrations.ValueMigration{ - &capcons.LinkValueMigration{ - CapabilityMapping: capabilityMapping, - IssueHandler: handler, - Handler: handler, - Reporter: reporter, - }, - } - }, - errorMessageHandler: errorMessageHandler, - programs: programs, - chainID: opts.ChainID, - } -} - -const capabilityValueMigrationReporterName = "cadence-capability-value-migration" - -// NewCadence1CapabilityValueMigration creates a new CadenceBaseMigration -// which migrates path capability values to ID capability values. -// It requires a map the IDs of the capability controllers, -// generated by the link value migration. -func NewCadence1CapabilityValueMigration( - rwf reporters.ReportWriterFactory, - errorMessageHandler *errorMessageHandler, - programs map[runtime.Location]*interpreter.Program, - privatePublicCapabilityMapping *capcons.PathCapabilityMapping, - typedStorageCapabilityMapping *capcons.PathTypeCapabilityMapping, - untypedStorageCapabilityMapping *capcons.PathCapabilityMapping, - opts Options, -) *CadenceBaseMigration { - var diffReporter reporters.ReportWriter - if opts.DiffMigrations { - diffReporter = rwf.ReportWriter("cadence-capability-value-migration-diff") - } - - return &CadenceBaseMigration{ - name: "cadence_capability_value_migration", - reporter: rwf.ReportWriter(capabilityValueMigrationReporterName), - diffReporter: diffReporter, - logVerboseDiff: opts.LogVerboseDiff, - verboseErrorOutput: opts.VerboseErrorOutput, - checkStorageHealthBeforeMigration: opts.CheckStorageHealthBeforeMigration, - valueMigrations: func( - _ *interpreter.Interpreter, - accounts environment.Accounts, - reporter *cadenceValueMigrationReporter, - ) []migrations.ValueMigration { - return []migrations.ValueMigration{ - &capcons.CapabilityValueMigration{ - PrivatePublicCapabilityMapping: privatePublicCapabilityMapping, - TypedStorageCapabilityMapping: typedStorageCapabilityMapping, - UntypedStorageCapabilityMapping: untypedStorageCapabilityMapping, - Reporter: reporter, - }, - } - }, - errorMessageHandler: errorMessageHandler, - programs: programs, - chainID: opts.ChainID, - } -} - -// errorMessageHandler formats error messages from errors. -// It only reports program loading errors once. -type errorMessageHandler struct { - // common.Location -> struct{} - reportedProgramLoadingErrors sync.Map -} - -func (t *errorMessageHandler) FormatError(err error) (message string, showStack bool) { - - // Only report program loading errors once, - // omit full error message for subsequent occurrences - - var programLoadingError environment.ProgramLoadingError - if errors.As(err, &programLoadingError) { - location := programLoadingError.Location - _, ok := t.reportedProgramLoadingErrors.LoadOrStore(location, struct{}{}) - if ok { - return "error getting program", false - } - - return err.Error(), false - } - - return err.Error(), true -} - -// cadenceValueMigrationReporter is the reporter for cadence value migrations -type cadenceValueMigrationReporter struct { - reportWriter reporters.ReportWriter - log zerolog.Logger - errorMessageHandler *errorMessageHandler - verboseErrorOutput bool -} - -var _ capcons.LinkMigrationReporter = &cadenceValueMigrationReporter{} -var _ capcons.CapabilityMigrationReporter = &cadenceValueMigrationReporter{} -var _ capcons.StorageCapabilityMigrationReporter = &cadenceValueMigrationReporter{} -var _ migrations.Reporter = &cadenceValueMigrationReporter{} - -func newValueMigrationReporter( - reportWriter reporters.ReportWriter, - log zerolog.Logger, - errorMessageHandler *errorMessageHandler, - verboseErrorOutput bool, -) *cadenceValueMigrationReporter { - return &cadenceValueMigrationReporter{ - reportWriter: reportWriter, - log: log, - errorMessageHandler: errorMessageHandler, - verboseErrorOutput: verboseErrorOutput, - } -} - -func (t *cadenceValueMigrationReporter) Migrated( - storageKey interpreter.StorageKey, - storageMapKey interpreter.StorageMapKey, - migration string, -) { - t.reportWriter.Write(cadenceValueMigrationEntry{ - StorageKey: storageKey, - StorageMapKey: storageMapKey, - Migration: migration, - }) -} - -func (t *cadenceValueMigrationReporter) Error(err error) { - - var migrationErr migrations.StorageMigrationError - - if !errors.As(err, &migrationErr) { - panic(cadenceErrors.NewUnreachableError()) - } - - message, showStack := t.errorMessageHandler.FormatError(migrationErr.Err) - - storageKey := migrationErr.StorageKey - storageMapKey := migrationErr.StorageMapKey - migration := migrationErr.Migration - - if showStack && len(migrationErr.Stack) > 0 { - message = fmt.Sprintf("%s\n%s", message, migrationErr.Stack) - } - - if t.verboseErrorOutput { - t.reportWriter.Write(cadenceValueMigrationFailureEntry{ - StorageKey: storageKey, - StorageMapKey: storageMapKey, - Migration: migration, - Message: message, - }) - } -} - -func (t *cadenceValueMigrationReporter) MigratedPathCapability( - accountAddress common.Address, - addressPath interpreter.AddressPath, - borrowType *interpreter.ReferenceStaticType, - capabilityID interpreter.UInt64Value, -) { - t.reportWriter.Write(capabilityMigrationEntry{ - AccountAddress: accountAddress, - AddressPath: addressPath, - BorrowType: borrowType, - CapabilityID: capabilityID, - }) -} - -func (t *cadenceValueMigrationReporter) MissingCapabilityID( - accountAddress common.Address, - addressPath interpreter.AddressPath, -) { - t.reportWriter.Write(capabilityMissingCapabilityIDEntry{ - AccountAddress: accountAddress, - AddressPath: addressPath, - }) -} - -func (t *cadenceValueMigrationReporter) MissingBorrowType( - targetPath interpreter.AddressPath, - storedPath interpreter.AddressPath, -) { - t.reportWriter.Write(storageCapConsMissingBorrowTypeEntry{ - TargetPath: targetPath, - StoredPath: storedPath, - }) -} - -func (t *cadenceValueMigrationReporter) InferredMissingBorrowType( - targetPath interpreter.AddressPath, - borrowType *interpreter.ReferenceStaticType, - storedPath interpreter.AddressPath, -) { - t.reportWriter.Write(storageCapConsInferredBorrowTypeEntry{ - TargetPath: targetPath, - BorrowType: borrowType, - StoredPath: storedPath, - }) -} - -func (t *cadenceValueMigrationReporter) IssuedStorageCapabilityController( - accountAddress common.Address, - addressPath interpreter.AddressPath, - borrowType *interpreter.ReferenceStaticType, - capabilityID interpreter.UInt64Value, -) { - t.reportWriter.Write(storageCapConIssuedEntry{ - AccountAddress: accountAddress, - AddressPath: addressPath, - BorrowType: borrowType, - CapabilityID: capabilityID, - }) -} - -func (t *cadenceValueMigrationReporter) MigratedLink( - accountAddressPath interpreter.AddressPath, - capabilityID interpreter.UInt64Value, -) { - t.reportWriter.Write(linkMigrationEntry{ - AccountAddressPath: accountAddressPath, - CapabilityID: uint64(capabilityID), - }) -} - -func (t *cadenceValueMigrationReporter) CyclicLink(err capcons.CyclicLinkError) { - t.reportWriter.Write(linkCyclicEntry{ - Address: err.Address, - Paths: err.Paths, - }) -} - -func (t *cadenceValueMigrationReporter) MissingTarget(accountAddressPath interpreter.AddressPath) { - t.reportWriter.Write(linkMissingTargetEntry{ - AddressPath: accountAddressPath, - }) -} - -func (t *cadenceValueMigrationReporter) DictionaryKeyConflict(accountAddressPath interpreter.AddressPath) { - t.reportWriter.Write(dictionaryKeyConflictEntry{ - AddressPath: accountAddressPath, - }) -} - -type valueMigrationReportEntry interface { - accountAddress() common.Address -} - -// cadenceValueMigrationReportEntry - -type cadenceValueMigrationEntry struct { - StorageKey interpreter.StorageKey - StorageMapKey interpreter.StorageMapKey - Migration string -} - -var _ valueMigrationReportEntry = cadenceValueMigrationEntry{} - -func (e cadenceValueMigrationEntry) accountAddress() common.Address { - return e.StorageKey.Address -} - -var _ json.Marshaler = cadenceValueMigrationEntry{} - -func (e cadenceValueMigrationEntry) MarshalJSON() ([]byte, error) { - return json.Marshal(struct { - Kind string `json:"kind"` - AccountAddress string `json:"account_address"` - StorageDomain string `json:"domain"` - Key string `json:"key"` - Migration string `json:"migration"` - }{ - Kind: "cadence-value-migration-success", - AccountAddress: e.StorageKey.Address.HexWithPrefix(), - StorageDomain: e.StorageKey.Key, - Key: fmt.Sprintf("%s", e.StorageMapKey), - Migration: e.Migration, - }) -} - -// cadenceValueMigrationFailureEntry - -type cadenceValueMigrationFailureEntry struct { - StorageKey interpreter.StorageKey - StorageMapKey interpreter.StorageMapKey - Migration string - Message string -} - -var _ valueMigrationReportEntry = cadenceValueMigrationFailureEntry{} - -func (e cadenceValueMigrationFailureEntry) accountAddress() common.Address { - return e.StorageKey.Address -} - -var _ json.Marshaler = cadenceValueMigrationFailureEntry{} - -func (e cadenceValueMigrationFailureEntry) MarshalJSON() ([]byte, error) { - return json.Marshal(struct { - Kind string `json:"kind"` - AccountAddress string `json:"account_address"` - StorageDomain string `json:"domain"` - Key string `json:"key"` - Migration string `json:"migration"` - Message string `json:"message"` - }{ - Kind: "cadence-value-migration-failure", - AccountAddress: e.StorageKey.Address.HexWithPrefix(), - StorageDomain: e.StorageKey.Key, - Key: fmt.Sprintf("%s", e.StorageMapKey), - Migration: e.Migration, - Message: e.Message, - }) -} - -// linkMigrationEntry - -type linkMigrationEntry struct { - AccountAddressPath interpreter.AddressPath - CapabilityID uint64 -} - -var _ valueMigrationReportEntry = linkMigrationEntry{} - -func (e linkMigrationEntry) accountAddress() common.Address { - return e.AccountAddressPath.Address -} - -var _ json.Marshaler = linkMigrationEntry{} - -func (e linkMigrationEntry) MarshalJSON() ([]byte, error) { - return json.Marshal(struct { - Kind string `json:"kind"` - AccountAddress string `json:"account_address"` - Path string `json:"path"` - CapabilityID uint64 `json:"capability_id"` - }{ - Kind: "link-migration-success", - AccountAddress: e.AccountAddressPath.Address.HexWithPrefix(), - Path: e.AccountAddressPath.Path.String(), - CapabilityID: e.CapabilityID, - }) -} - -// capabilityMigrationEntry - -type capabilityMigrationEntry struct { - AccountAddress common.Address - AddressPath interpreter.AddressPath - BorrowType *interpreter.ReferenceStaticType - CapabilityID interpreter.UInt64Value -} - -var _ valueMigrationReportEntry = capabilityMigrationEntry{} - -func (e capabilityMigrationEntry) accountAddress() common.Address { - return e.AccountAddress -} - -var _ json.Marshaler = capabilityMigrationEntry{} - -func (e capabilityMigrationEntry) MarshalJSON() ([]byte, error) { - return json.Marshal(struct { - Kind string `json:"kind"` - AccountAddress string `json:"account_address"` - Address string `json:"address"` - Path string `json:"path"` - BorrowType string `json:"borrow_type"` - CapabilityID string `json:"capability_id"` - }{ - Kind: "capability-migration-success", - AccountAddress: e.AccountAddress.HexWithPrefix(), - Address: e.AddressPath.Address.HexWithPrefix(), - Path: e.AddressPath.Path.String(), - BorrowType: string(e.BorrowType.ID()), - CapabilityID: e.CapabilityID.String(), - }) -} - -// capabilityMissingCapabilityIDEntry - -type capabilityMissingCapabilityIDEntry struct { - AccountAddress common.Address - AddressPath interpreter.AddressPath -} - -var _ valueMigrationReportEntry = capabilityMissingCapabilityIDEntry{} - -func (e capabilityMissingCapabilityIDEntry) accountAddress() common.Address { - return e.AccountAddress -} - -var _ json.Marshaler = capabilityMissingCapabilityIDEntry{} - -func (e capabilityMissingCapabilityIDEntry) MarshalJSON() ([]byte, error) { - return json.Marshal(struct { - Kind string `json:"kind"` - AccountAddress string `json:"account_address"` - Address string `json:"address"` - Path string `json:"path"` - }{ - Kind: "capability-missing-capability-id", - AccountAddress: e.AccountAddress.HexWithPrefix(), - Address: e.AddressPath.Address.HexWithPrefix(), - Path: e.AddressPath.Path.String(), - }) -} - -// linkMissingTargetEntry - -type linkMissingTargetEntry struct { - AddressPath interpreter.AddressPath -} - -var _ valueMigrationReportEntry = linkMissingTargetEntry{} - -func (e linkMissingTargetEntry) accountAddress() common.Address { - return e.AddressPath.Address -} - -var _ json.Marshaler = linkMissingTargetEntry{} - -func (e linkMissingTargetEntry) MarshalJSON() ([]byte, error) { - return json.Marshal(struct { - Kind string `json:"kind"` - AccountAddress string `json:"account_address"` - Path string `json:"path"` - }{ - Kind: "link-missing-target", - AccountAddress: e.AddressPath.Address.HexWithPrefix(), - Path: e.AddressPath.Path.String(), - }) -} - -// linkCyclicEntry - -type linkCyclicEntry struct { - Address common.Address - Paths []interpreter.PathValue -} - -var _ valueMigrationReportEntry = linkCyclicEntry{} - -func (e linkCyclicEntry) accountAddress() common.Address { - return e.Address -} - -var _ json.Marshaler = linkCyclicEntry{} - -func (e linkCyclicEntry) MarshalJSON() ([]byte, error) { - - pathStrings := make([]string, 0, len(e.Paths)) - for _, path := range e.Paths { - pathStrings = append(pathStrings, path.String()) - } - - return json.Marshal(struct { - Kind string `json:"kind"` - AccountAddress string `json:"account_address"` - Paths []string `json:"paths"` - }{ - Kind: "link-cyclic", - AccountAddress: e.Address.HexWithPrefix(), - Paths: pathStrings, - }) -} - -// dictionaryKeyConflictEntry - -type dictionaryKeyConflictEntry struct { - AddressPath interpreter.AddressPath -} - -var _ json.Marshaler = dictionaryKeyConflictEntry{} - -func (e dictionaryKeyConflictEntry) MarshalJSON() ([]byte, error) { - return json.Marshal(struct { - Kind string `json:"kind"` - AccountAddress string `json:"account_address"` - Path string `json:"path"` - }{ - Kind: "dictionary-key-conflict", - AccountAddress: e.AddressPath.Address.HexWithPrefix(), - Path: e.AddressPath.Path.String(), - }) -} - -// storageCapConIssuedEntry - -type storageCapConIssuedEntry struct { - AccountAddress common.Address - AddressPath interpreter.AddressPath - BorrowType interpreter.StaticType - CapabilityID interpreter.UInt64Value -} - -var _ valueMigrationReportEntry = storageCapConIssuedEntry{} - -func (e storageCapConIssuedEntry) accountAddress() common.Address { - return e.AccountAddress -} - -var _ json.Marshaler = storageCapConIssuedEntry{} - -func (e storageCapConIssuedEntry) MarshalJSON() ([]byte, error) { - return json.Marshal(struct { - Kind string `json:"kind"` - AccountAddress string `json:"account_address"` - Address string `json:"address"` - Path string `json:"path"` - BorrowType string `json:"borrow_type"` - CapabilityID string `json:"capability_id"` - }{ - Kind: "storage-capcon-issued", - AccountAddress: e.AccountAddress.HexWithPrefix(), - Address: e.AddressPath.Address.HexWithPrefix(), - Path: e.AddressPath.Path.String(), - BorrowType: string(e.BorrowType.ID()), - CapabilityID: e.CapabilityID.String(), - }) -} - -// StorageCapConMissingBorrowType - -type storageCapConsMissingBorrowTypeEntry struct { - TargetPath interpreter.AddressPath - StoredPath interpreter.AddressPath -} - -var _ valueMigrationReportEntry = storageCapConsMissingBorrowTypeEntry{} - -func (e storageCapConsMissingBorrowTypeEntry) accountAddress() common.Address { - return e.StoredPath.Address -} - -var _ json.Marshaler = storageCapConsMissingBorrowTypeEntry{} - -func (e storageCapConsMissingBorrowTypeEntry) MarshalJSON() ([]byte, error) { - return json.Marshal(struct { - Kind string `json:"kind"` - AccountAddress string `json:"account_address"` - Address string `json:"address"` - TargetPath string `json:"target_path"` - StoredPath string `json:"stored_path"` - }{ - Kind: "storage-capcon-missing-borrow-type", - AccountAddress: e.StoredPath.Address.HexWithPrefix(), - Address: e.TargetPath.Address.HexWithPrefix(), - TargetPath: e.TargetPath.Path.String(), - StoredPath: e.StoredPath.Path.String(), - }) -} - -// StorageCapConMissingBorrowType - -type storageCapConsInferredBorrowTypeEntry struct { - TargetPath interpreter.AddressPath - BorrowType *interpreter.ReferenceStaticType - StoredPath interpreter.AddressPath -} - -var _ valueMigrationReportEntry = storageCapConsInferredBorrowTypeEntry{} -var _ json.Marshaler = storageCapConsInferredBorrowTypeEntry{} - -func (e storageCapConsInferredBorrowTypeEntry) accountAddress() common.Address { - return e.StoredPath.Address -} - -func (e storageCapConsInferredBorrowTypeEntry) MarshalJSON() ([]byte, error) { - return json.Marshal(struct { - Kind string `json:"kind"` - AccountAddress string `json:"account_address"` - Address string `json:"address"` - TargetPath string `json:"target_path"` - BorrowType string `json:"borrow_type"` - StoredPath string `json:"stored_path"` - }{ - Kind: "storage-capcon-inferred-borrow-type", - AccountAddress: e.StoredPath.Address.HexWithPrefix(), - Address: e.TargetPath.Address.HexWithPrefix(), - TargetPath: e.TargetPath.Path.String(), - BorrowType: string(e.BorrowType.ID()), - StoredPath: e.StoredPath.Path.String(), - }) -} diff --git a/cmd/util/ledger/migrations/cadence_values_migration_test.go b/cmd/util/ledger/migrations/cadence_values_migration_test.go deleted file mode 100644 index cad06ce594d..00000000000 --- a/cmd/util/ledger/migrations/cadence_values_migration_test.go +++ /dev/null @@ -1,3142 +0,0 @@ -package migrations - -import ( - _ "embed" - "fmt" - "io" - "sort" - "sync" - "testing" - - _ "github.com/glebarez/go-sqlite" - "github.com/onflow/cadence" - migrations2 "github.com/onflow/cadence/migrations" - "github.com/rs/zerolog" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/onflow/cadence/runtime" - "github.com/onflow/cadence/runtime/common" - "github.com/onflow/cadence/runtime/interpreter" - "github.com/onflow/cadence/runtime/sema" - - "github.com/onflow/flow-go/cmd/util/ledger/reporters" - "github.com/onflow/flow-go/cmd/util/ledger/util" - "github.com/onflow/flow-go/cmd/util/ledger/util/registers" - "github.com/onflow/flow-go/engine/execution/computation" - "github.com/onflow/flow-go/fvm" - "github.com/onflow/flow-go/fvm/environment" - "github.com/onflow/flow-go/fvm/systemcontracts" - "github.com/onflow/flow-go/model/flow" -) - -const snapshotPath = "test-data/cadence_values_migration/snapshot_cadence_v0.42.6" - -const testAccountAddress = "01cf0e2f2f715450" - -type writer struct { - logs []string -} - -var _ io.Writer = &writer{} - -func (w *writer) Write(p []byte) (n int, err error) { - w.logs = append(w.logs, string(p)) - return len(p), nil -} - -//go:embed test-data/cadence_values_migration/test_contract_upgraded.cdc -var testContractUpgraded []byte - -func TestCadenceValuesMigration(t *testing.T) { - - t.Parallel() - - address, err := common.HexToAddress(testAccountAddress) - require.NoError(t, err) - - // Get the old payloads - payloads, err := util.PayloadsFromEmulatorSnapshot(snapshotPath) - require.NoError(t, err) - - registersByAccount, err := registers.NewByAccountFromPayloads(payloads) - require.NoError(t, err) - - rwf := &testReportWriterFactory{} - - logWriter := &writer{} - logger := zerolog.New(logWriter).Level(zerolog.ErrorLevel) - - const nWorker = 2 - - const chainID = flow.Emulator - // TODO: EVM contract is not deployed in snapshot yet, so can't update it - const evmContractChange = EVMContractChangeNone - - const burnerContractChange = BurnerContractChangeDeploy - - stagedContracts := []StagedContract{ - { - Contract: Contract{ - Name: "Test", - Code: testContractUpgraded, - }, - Address: address, - }, - } - - migrations := NewCadence1Migrations( - logger, - t.TempDir(), - rwf, - Options{ - NWorker: nWorker, - ChainID: chainID, - EVMContractChange: evmContractChange, - BurnerContractChange: burnerContractChange, - StagedContracts: stagedContracts, - VerboseErrorOutput: true, - }, - ) - - for _, migration := range migrations { - err = migration.Migrate(registersByAccount) - require.NoError( - t, - err, - "migration `%s` failed, logs: %v", - migration.Name, - logWriter.logs, - ) - } - - // Assert the migrated payloads - checkMigratedPayloads(t, address, registersByAccount, chainID) - - // Check reporters - checkReporters(t, rwf, address) - - // Check error logs. - require.Empty(t, logWriter.logs) - - checkMigratedState(t, address, registersByAccount, chainID) -} - -type migrationVisit struct { - storageKey interpreter.StorageKey - storageMapKey interpreter.StorageMapKey - value string -} - -type visitMigration struct { - visits []migrationVisit -} - -var _ migrations2.ValueMigration = &visitMigration{} - -func (*visitMigration) Name() string { - return "visit" -} - -func (m *visitMigration) Migrate( - storageKey interpreter.StorageKey, - storageMapKey interpreter.StorageMapKey, - value interpreter.Value, - _ *interpreter.Interpreter, - _ migrations2.ValueMigrationPosition, -) (newValue interpreter.Value, err error) { - - m.visits = append( - m.visits, - migrationVisit{ - storageKey: storageKey, - storageMapKey: storageMapKey, - value: value.String(), - }, - ) - - return nil, nil -} - -func (*visitMigration) CanSkip(_ interpreter.StaticType) bool { - return false -} - -func (*visitMigration) Domains() map[string]struct{} { - return nil -} - -func checkMigratedState( - t *testing.T, - address common.Address, - registersByAccount *registers.ByAccount, - chainID flow.ChainID, -) { - - mr, err := NewInterpreterMigrationRuntime( - registersByAccount, - chainID, - InterpreterMigrationRuntimeConfig{}, - ) - require.NoError(t, err) - - validationMigration, err := migrations2.NewStorageMigration( - mr.Interpreter, - mr.Storage, - "validation", - address, - ) - require.NoError(t, err) - - visitMigration := &visitMigration{} - - validationMigration.Migrate( - validationMigration.NewValueMigrationsPathMigrator(nil, visitMigration), - ) - - require.Equal(t, - []migrationVisit{ - { - storageKey: interpreter.StorageKey{Key: "storage", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("dictionary_with_string_keys"), - value: `"H\u{e9}llo"`, - }, - { - storageKey: interpreter.StorageKey{Key: "storage", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("dictionary_with_string_keys"), - value: `"Caf\u{e9}"`, - }, - { - storageKey: interpreter.StorageKey{Key: "storage", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("dictionary_with_string_keys"), - value: `2`, - }, - { - storageKey: interpreter.StorageKey{Key: "storage", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("dictionary_with_string_keys"), - value: `1`, - }, - { - storageKey: interpreter.StorageKey{Key: "storage", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("dictionary_with_string_keys"), - value: `{"H\u{e9}llo": 2, "Caf\u{e9}": 1}`, - }, - { - storageKey: interpreter.StorageKey{Key: "storage", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("flowTokenVault"), - value: `0.00100000`, - }, - { - storageKey: interpreter.StorageKey{Key: "storage", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("flowTokenVault"), - value: `8791026472627208194`, - }, - { - storageKey: interpreter.StorageKey{Key: "storage", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("flowTokenVault"), - value: `A.0ae53cb6e3f42a79.FlowToken.Vault(balance: 0.00100000, uuid: 8791026472627208194)`, - }, - { - storageKey: interpreter.StorageKey{Key: "storage", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("dictionary_with_auth_reference_typed_key"), - value: `Type()`, - }, - { - storageKey: interpreter.StorageKey{Key: "storage", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("dictionary_with_auth_reference_typed_key"), - value: `"auth_ref"`, - }, - { - storageKey: interpreter.StorageKey{Key: "storage", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("dictionary_with_auth_reference_typed_key"), - value: `{Type(): "auth_ref"}`, - }, - { - storageKey: interpreter.StorageKey{Key: "storage", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("dictionary_with_reference_typed_key"), - value: `Type()`, - }, - { - storageKey: interpreter.StorageKey{Key: "storage", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("dictionary_with_reference_typed_key"), - value: `"non_auth_ref"`, - }, - { - storageKey: interpreter.StorageKey{Key: "storage", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("dictionary_with_reference_typed_key"), - value: `{Type(): "non_auth_ref"}`, - }, - { - storageKey: interpreter.StorageKey{Key: "storage", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("type_value"), - value: "Type()", - }, - { - storageKey: interpreter.StorageKey{Key: "storage", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("dictionary_with_account_type_keys"), - value: "Type()", - }, - { - storageKey: interpreter.StorageKey{Key: "storage", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("dictionary_with_account_type_keys"), - value: "Type()", - }, - { - storageKey: interpreter.StorageKey{Key: "storage", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("dictionary_with_account_type_keys"), - value: "Type()", - }, - { - storageKey: interpreter.StorageKey{Key: "storage", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("dictionary_with_account_type_keys"), - value: "Type()", - }, - { - storageKey: interpreter.StorageKey{Key: "storage", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("dictionary_with_account_type_keys"), - value: "Type<&Account>()", - }, - { - storageKey: interpreter.StorageKey{Key: "storage", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("dictionary_with_account_type_keys"), - value: `Type()`, - }, - { - storageKey: interpreter.StorageKey{Key: "storage", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("dictionary_with_account_type_keys"), - value: `Type()`, - }, - { - storageKey: interpreter.StorageKey{Key: "storage", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("dictionary_with_account_type_keys"), - value: `Type()`, - }, - { - storageKey: interpreter.StorageKey{Key: "storage", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("dictionary_with_account_type_keys"), - value: `Type()`, - }, - { - storageKey: interpreter.StorageKey{Key: "storage", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("dictionary_with_account_type_keys"), - value: "4", - }, - { - storageKey: interpreter.StorageKey{Key: "storage", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("dictionary_with_account_type_keys"), - value: "6", - }, - { - storageKey: interpreter.StorageKey{Key: "storage", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("dictionary_with_account_type_keys"), - value: "5", - }, - { - storageKey: interpreter.StorageKey{Key: "storage", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("dictionary_with_account_type_keys"), - value: "7", - }, - { - storageKey: interpreter.StorageKey{Key: "storage", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("dictionary_with_account_type_keys"), - value: "8", - }, - { - storageKey: interpreter.StorageKey{Key: "storage", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("dictionary_with_account_type_keys"), - value: "2", - }, - { - storageKey: interpreter.StorageKey{Key: "storage", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("dictionary_with_account_type_keys"), - value: "3", - }, - { - storageKey: interpreter.StorageKey{Key: "storage", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("dictionary_with_account_type_keys"), - value: "9", - }, - { - storageKey: interpreter.StorageKey{Key: "storage", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("dictionary_with_account_type_keys"), - value: "1", - }, - { - storageKey: interpreter.StorageKey{Key: "storage", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("dictionary_with_account_type_keys"), - value: `{Type(): 4, Type(): 6, Type(): 5, Type(): 7, Type<&Account>(): 8, Type(): 2, Type(): 3, Type(): 9, Type(): 1}`, - }, - { - storageKey: interpreter.StorageKey{Key: "storage", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("capability"), - value: `Capability(address: 0x01cf0e2f2f715450, id: 2)`, - }, - { - storageKey: interpreter.StorageKey{Key: "storage", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("capability"), - value: `Capability(address: 0x01cf0e2f2f715450, id: 2)`, - }, - { - storageKey: interpreter.StorageKey{Key: "storage", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("string_value_1"), - value: `"Caf\u{e9}"`, - }, - { - storageKey: interpreter.StorageKey{Key: "storage", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("untyped_capability"), - value: `Capability(address: 0x01cf0e2f2f715450, id: 2)`, - }, - { - storageKey: interpreter.StorageKey{Key: "storage", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("r"), - value: `11457157452030541824`, - }, - { - storageKey: interpreter.StorageKey{Key: "storage", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("r"), - value: "A.01cf0e2f2f715450.Test.R(uuid: 11457157452030541824)", - }, - { - storageKey: interpreter.StorageKey{Key: "storage", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("string_value_2"), - value: `"Caf\u{e9}"`, - }, - { - storageKey: interpreter.StorageKey{Key: "storage", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("dictionary_with_restricted_typed_keys"), - value: `Type<{A.01cf0e2f2f715450.Test.Bar, A.01cf0e2f2f715450.Test.Foo}>()`, - }, - { - storageKey: interpreter.StorageKey{Key: "storage", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("dictionary_with_restricted_typed_keys"), - value: `Type<{A.01cf0e2f2f715450.Test.Foo, A.01cf0e2f2f715450.Test.Bar, A.01cf0e2f2f715450.Test.Baz}>()`, - }, - { - storageKey: interpreter.StorageKey{Key: "storage", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("dictionary_with_restricted_typed_keys"), - value: `1`, - }, - { - storageKey: interpreter.StorageKey{Key: "storage", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("dictionary_with_restricted_typed_keys"), - value: `2`, - }, - { - storageKey: interpreter.StorageKey{Key: "storage", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("dictionary_with_restricted_typed_keys"), - value: `{Type<{A.01cf0e2f2f715450.Test.Bar, A.01cf0e2f2f715450.Test.Foo}>(): 1, Type<{A.01cf0e2f2f715450.Test.Foo, A.01cf0e2f2f715450.Test.Bar, A.01cf0e2f2f715450.Test.Baz}>(): 2}`, - }, - { - storageKey: interpreter.StorageKey{Key: "public", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("flowTokenReceiver"), - value: `Capability<&A.0ae53cb6e3f42a79.FlowToken.Vault>(address: 0x01cf0e2f2f715450, id: 1)`, - }, - { - storageKey: interpreter.StorageKey{Key: "public", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("linkR"), - value: `Capability(address: 0x01cf0e2f2f715450, id: 2)`, - }, - { - storageKey: interpreter.StorageKey{Key: "public", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("flowTokenBalance"), - value: `Capability<&A.0ae53cb6e3f42a79.FlowToken.Vault>(address: 0x01cf0e2f2f715450, id: 3)`, - }, - { - storageKey: interpreter.StorageKey{Key: "contract", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("Test"), - value: `A.01cf0e2f2f715450.Test()`, - }, - { - storageKey: interpreter.StorageKey{Key: "cap_con", Address: address}, - storageMapKey: interpreter.Uint64StorageMapKey(0x2), - value: "StorageCapabilityController(borrowType: Type(), capabilityID: 2, target: /storage/r)", - }, - { - storageKey: interpreter.StorageKey{Key: "cap_con", Address: address}, - storageMapKey: interpreter.Uint64StorageMapKey(0x1), - value: "StorageCapabilityController(borrowType: Type<&A.0ae53cb6e3f42a79.FlowToken.Vault>(), capabilityID: 1, target: /storage/flowTokenVault)", - }, - { - storageKey: interpreter.StorageKey{Key: "cap_con", Address: address}, - storageMapKey: interpreter.Uint64StorageMapKey(0x3), - value: "StorageCapabilityController(borrowType: Type<&A.0ae53cb6e3f42a79.FlowToken.Vault>(), capabilityID: 3, target: /storage/flowTokenVault)", - }, - { - storageKey: interpreter.StorageKey{Key: "path_cap", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("flowTokenVault"), - value: "3", - }, - { - storageKey: interpreter.StorageKey{Key: "path_cap", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("flowTokenVault"), - value: "1", - }, - { - storageKey: interpreter.StorageKey{Key: "path_cap", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("flowTokenVault"), - value: "nil", - }, - { - storageKey: interpreter.StorageKey{Key: "path_cap", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("flowTokenVault"), - value: "nil", - }, - { - storageKey: interpreter.StorageKey{Key: "path_cap", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("flowTokenVault"), - value: "{3: nil, 1: nil}", - }, - { - storageKey: interpreter.StorageKey{Key: "path_cap", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("r"), - value: "2", - }, - { - storageKey: interpreter.StorageKey{Key: "path_cap", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("r"), - value: "nil", - }, - { - storageKey: interpreter.StorageKey{Key: "path_cap", Address: address}, - storageMapKey: interpreter.StringStorageMapKey("r"), - value: "{2: nil}", - }, - }, - visitMigration.visits, - ) - -} - -var flowTokenAddress = func() common.Address { - address, _ := common.HexToAddress("0ae53cb6e3f42a79") - return address -}() - -func checkMigratedPayloads( - t *testing.T, - address common.Address, - registersByAccount *registers.ByAccount, - chainID flow.ChainID, -) { - mr, err := NewInterpreterMigrationRuntime( - registersByAccount, - chainID, - InterpreterMigrationRuntimeConfig{}, - ) - require.NoError(t, err) - - storageMap := mr.Storage.GetStorageMap(address, common.PathDomainStorage.Identifier(), false) - require.NotNil(t, storageMap) - require.Equal(t, 12, int(storageMap.Count())) - - iterator := storageMap.Iterator(mr.Interpreter) - - // Check whether the account ID is properly incremented. - checkAccountID(t, mr, address) - - fullyEntitledAccountReferenceType := interpreter.ConvertSemaToStaticType(nil, sema.FullyEntitledAccountReferenceType) - accountReferenceType := interpreter.ConvertSemaToStaticType(nil, sema.AccountReferenceType) - - var values []interpreter.Value - for key, value := iterator.Next(); key != nil; key, value = iterator.Next() { - values = append(values, value) - } - - testContractLocation := common.NewAddressLocation( - nil, - address, - "Test", - ) - - flowTokenLocation := common.NewAddressLocation( - nil, - flowTokenAddress, - "FlowToken", - ) - - fooInterfaceType := interpreter.NewInterfaceStaticTypeComputeTypeID( - nil, - testContractLocation, - "Test.Foo", - ) - - barInterfaceType := interpreter.NewInterfaceStaticTypeComputeTypeID( - nil, - testContractLocation, - "Test.Bar", - ) - - bazInterfaceType := interpreter.NewInterfaceStaticTypeComputeTypeID( - nil, - testContractLocation, - "Test.Baz", - ) - - rResourceType := interpreter.NewCompositeStaticTypeComputeTypeID( - nil, - testContractLocation, - "Test.R", - ) - - entitlementAuthorization := func() interpreter.EntitlementSetAuthorization { - return interpreter.NewEntitlementSetAuthorization( - nil, - func() (entitlements []common.TypeID) { - return []common.TypeID{ - testContractLocation.TypeID(nil, "Test.E"), - } - }, - 1, - sema.Conjunction, - ) - } - - expectedValues := []interpreter.Value{ - interpreter.NewDictionaryValue( - mr.Interpreter, - interpreter.EmptyLocationRange, - interpreter.NewDictionaryStaticType( - nil, - interpreter.PrimitiveStaticTypeString, - interpreter.PrimitiveStaticTypeInt, - ), - interpreter.NewUnmeteredStringValue("Caf\u00E9"), - interpreter.NewUnmeteredIntValueFromInt64(1), - interpreter.NewUnmeteredStringValue("H\u00E9llo"), - interpreter.NewUnmeteredIntValueFromInt64(2), - ), - - interpreter.NewCompositeValue( - mr.Interpreter, - interpreter.EmptyLocationRange, - flowTokenLocation, - "FlowToken.Vault", - common.CompositeKindResource, - []interpreter.CompositeField{ - { - Value: interpreter.NewUnmeteredUFix64Value(0.001 * sema.Fix64Factor), - Name: "balance", - }, - { - Value: interpreter.NewUnmeteredUInt64Value(8791026472627208194), - Name: "uuid", - }, - }, - address, - ), - interpreter.NewDictionaryValue( - mr.Interpreter, - interpreter.EmptyLocationRange, - interpreter.NewDictionaryStaticType( - nil, - interpreter.PrimitiveStaticTypeMetaType, - interpreter.PrimitiveStaticTypeString, - ), - interpreter.NewUnmeteredTypeValue( - interpreter.NewReferenceStaticType( - nil, - entitlementAuthorization(), - rResourceType, - ), - ), - interpreter.NewUnmeteredStringValue("auth_ref"), - ), - interpreter.NewDictionaryValue( - mr.Interpreter, - interpreter.EmptyLocationRange, - interpreter.NewDictionaryStaticType( - nil, - interpreter.PrimitiveStaticTypeMetaType, - interpreter.PrimitiveStaticTypeString, - ), - interpreter.NewUnmeteredTypeValue( - interpreter.NewReferenceStaticType( - nil, - entitlementAuthorization(), - rResourceType, - ), - ), - interpreter.NewUnmeteredStringValue("non_auth_ref"), - ), - interpreter.NewUnmeteredTypeValue(fullyEntitledAccountReferenceType), - interpreter.NewDictionaryValue( - mr.Interpreter, - interpreter.EmptyLocationRange, - interpreter.NewDictionaryStaticType( - nil, - interpreter.PrimitiveStaticTypeMetaType, - interpreter.PrimitiveStaticTypeInt, - ), - interpreter.NewUnmeteredTypeValue(fullyEntitledAccountReferenceType), - interpreter.NewUnmeteredIntValueFromInt64(1), - interpreter.NewUnmeteredTypeValue(interpreter.PrimitiveStaticTypeAccount_Capabilities), - interpreter.NewUnmeteredIntValueFromInt64(2), - interpreter.NewUnmeteredTypeValue(interpreter.PrimitiveStaticTypeAccount_AccountCapabilities), - interpreter.NewUnmeteredIntValueFromInt64(3), - interpreter.NewUnmeteredTypeValue(interpreter.PrimitiveStaticTypeAccount_StorageCapabilities), - interpreter.NewUnmeteredIntValueFromInt64(4), - interpreter.NewUnmeteredTypeValue(interpreter.PrimitiveStaticTypeAccount_Contracts), - interpreter.NewUnmeteredIntValueFromInt64(5), - interpreter.NewUnmeteredTypeValue(interpreter.PrimitiveStaticTypeAccount_Keys), - interpreter.NewUnmeteredIntValueFromInt64(6), - interpreter.NewUnmeteredTypeValue(interpreter.PrimitiveStaticTypeAccount_Inbox), - interpreter.NewUnmeteredIntValueFromInt64(7), - interpreter.NewUnmeteredTypeValue(accountReferenceType), - interpreter.NewUnmeteredIntValueFromInt64(8), - interpreter.NewUnmeteredTypeValue(interpreter.AccountKeyStaticType), - interpreter.NewUnmeteredIntValueFromInt64(9), - ), - interpreter.NewUnmeteredSomeValueNonCopying( - interpreter.NewUnmeteredCapabilityValue( - 2, - interpreter.NewAddressValue(nil, address), - interpreter.NewReferenceStaticType(nil, entitlementAuthorization(), rResourceType), - ), - ), - - // String value should be in the normalized form. - interpreter.NewUnmeteredStringValue("Caf\u00E9"), - - interpreter.NewUnmeteredCapabilityValue( - 2, - interpreter.NewAddressValue(nil, address), - interpreter.NewReferenceStaticType(nil, entitlementAuthorization(), rResourceType), - ), - - interpreter.NewCompositeValue( - mr.Interpreter, - interpreter.EmptyLocationRange, - testContractLocation, - "Test.R", - common.CompositeKindResource, - []interpreter.CompositeField{ - { - Value: interpreter.NewUnmeteredUInt64Value(11457157452030541824), - Name: "uuid", - }, - }, - address, - ), - - // String value should be in the normalized form. - interpreter.NewUnmeteredStringValue("Caf\u00E9"), - - interpreter.NewDictionaryValue( - mr.Interpreter, - interpreter.EmptyLocationRange, - interpreter.NewDictionaryStaticType( - nil, - interpreter.PrimitiveStaticTypeMetaType, - interpreter.PrimitiveStaticTypeInt, - ), - interpreter.NewUnmeteredTypeValue( - &interpreter.IntersectionStaticType{ - Types: []*interpreter.InterfaceStaticType{ - fooInterfaceType, - barInterfaceType, - }, - LegacyType: interpreter.PrimitiveStaticTypeAnyStruct, - }, - ), - interpreter.NewUnmeteredIntValueFromInt64(1), - interpreter.NewUnmeteredTypeValue( - &interpreter.IntersectionStaticType{ - Types: []*interpreter.InterfaceStaticType{ - fooInterfaceType, - barInterfaceType, - bazInterfaceType, - }, - LegacyType: interpreter.PrimitiveStaticTypeAnyStruct, - }, - ), - interpreter.NewUnmeteredIntValueFromInt64(2), - ), - } - - require.Equal(t, len(expectedValues), len(values)) - - for index, value := range values { - actualValue := value.(interpreter.EquatableValue) - expectedValue := expectedValues[index] - - assert.True(t, - actualValue.Equal(mr.Interpreter, interpreter.EmptyLocationRange, expectedValue), - "values at index %d are not equal: %s != %s", - index, - actualValue, - expectedValue, - ) - } -} - -func checkAccountID(t *testing.T, mr *InterpreterMigrationRuntime, address common.Address) { - id := flow.AccountStatusRegisterID(flow.Address(address)) - statusBytes, err := mr.Accounts.GetValue(id) - require.NoError(t, err) - - accountStatus, err := environment.AccountStatusFromBytes(statusBytes) - require.NoError(t, err) - - assert.Equal(t, uint64(3), accountStatus.AccountIdCounter()) -} - -func checkReporters( - t *testing.T, - rwf *testReportWriterFactory, - address common.Address, -) { - - testContractLocation := common.NewAddressLocation( - nil, - address, - "Test", - ) - - rResourceType := interpreter.NewCompositeStaticTypeComputeTypeID( - nil, - testContractLocation, - "Test.R", - ) - - entitlementAuthorization := func() interpreter.EntitlementSetAuthorization { - return interpreter.NewEntitlementSetAuthorization( - nil, - func() (entitlements []common.TypeID) { - return []common.TypeID{ - testContractLocation.TypeID(nil, "Test.E"), - } - }, - 1, - sema.Conjunction, - ) - } - - var reporterNames []string - for reporterName := range rwf.reportWriters { - reporterNames = append(reporterNames, reporterName) - } - sort.Strings(reporterNames) - - var accountReportEntries []valueMigrationReportEntry - - for _, reporterName := range reporterNames { - reportWriter := rwf.reportWriters[reporterName] - - for _, entry := range reportWriter.entries { - - e, ok := entry.(valueMigrationReportEntry) - if !ok || e.accountAddress() != address { - continue - } - - accountReportEntries = append(accountReportEntries, e) - } - } - - assert.Equal( - t, - []valueMigrationReportEntry{ - capabilityMigrationEntry{ - AccountAddress: address, - AddressPath: interpreter.AddressPath{ - Address: address, - Path: interpreter.PathValue{ - Identifier: "linkR", - Domain: common.PathDomainPublic, - }, - }, - BorrowType: interpreter.NewReferenceStaticType( - nil, - entitlementAuthorization(), - rResourceType, - ), - CapabilityID: 2, - }, - cadenceValueMigrationEntry{ - StorageKey: interpreter.StorageKey{Key: "storage", Address: address}, - StorageMapKey: interpreter.StringStorageMapKey("capability"), - Migration: "CapabilityValueMigration", - }, - capabilityMigrationEntry{ - AccountAddress: address, - AddressPath: interpreter.AddressPath{ - Address: address, Path: interpreter.PathValue{ - Identifier: "linkR", - Domain: common.PathDomainPublic, - }, - }, - BorrowType: interpreter.NewReferenceStaticType( - nil, - entitlementAuthorization(), - rResourceType, - ), - CapabilityID: 2, - }, - cadenceValueMigrationEntry{ - StorageKey: interpreter.StorageKey{Key: "storage", Address: address}, - StorageMapKey: interpreter.StringStorageMapKey("untyped_capability"), - Migration: "CapabilityValueMigration", - }, - linkMigrationEntry{ - AccountAddressPath: interpreter.AddressPath{ - Address: address, - Path: interpreter.PathValue{ - Identifier: "flowTokenReceiver", - Domain: common.PathDomainPublic, - }, - }, - CapabilityID: 1, - }, - cadenceValueMigrationEntry{ - StorageKey: interpreter.StorageKey{Key: "public", Address: address}, - StorageMapKey: interpreter.StringStorageMapKey("flowTokenReceiver"), - Migration: "LinkValueMigration", - }, - linkMigrationEntry{ - AccountAddressPath: interpreter.AddressPath{ - Address: address, - Path: interpreter.PathValue{ - Identifier: "linkR", - Domain: common.PathDomainPublic, - }, - }, - CapabilityID: 2, - }, - cadenceValueMigrationEntry{ - StorageKey: interpreter.StorageKey{Key: "public", Address: address}, - StorageMapKey: interpreter.StringStorageMapKey("linkR"), - Migration: "LinkValueMigration", - }, - linkMigrationEntry{ - AccountAddressPath: interpreter.AddressPath{ - Address: address, - Path: interpreter.PathValue{ - Identifier: "flowTokenBalance", - Domain: common.PathDomainPublic, - }, - }, - CapabilityID: 3, - }, - cadenceValueMigrationEntry{ - StorageKey: interpreter.StorageKey{Key: "public", Address: address}, - StorageMapKey: interpreter.StringStorageMapKey("flowTokenBalance"), - Migration: "LinkValueMigration", - }, - cadenceValueMigrationEntry{ - StorageKey: interpreter.StorageKey{Key: "storage", Address: address}, - StorageMapKey: interpreter.StringStorageMapKey("dictionary_with_string_keys"), - Migration: "StringNormalizingMigration", - }, - cadenceValueMigrationEntry{ - StorageKey: interpreter.StorageKey{Key: "storage", Address: address}, - StorageMapKey: interpreter.StringStorageMapKey("dictionary_with_auth_reference_typed_key"), - Migration: "EntitlementsMigration", - }, - cadenceValueMigrationEntry{ - StorageKey: interpreter.StorageKey{Key: "storage", Address: address}, - StorageMapKey: interpreter.StringStorageMapKey("dictionary_with_auth_reference_typed_key"), - Migration: "TypeKeyMigration", - }, - cadenceValueMigrationEntry{ - StorageKey: interpreter.StorageKey{Key: "storage", Address: address}, - StorageMapKey: interpreter.StringStorageMapKey("dictionary_with_reference_typed_key"), - Migration: "EntitlementsMigration", - }, - cadenceValueMigrationEntry{ - StorageKey: interpreter.StorageKey{Key: "storage", Address: address}, - StorageMapKey: interpreter.StringStorageMapKey("dictionary_with_reference_typed_key"), - Migration: "TypeKeyMigration", - }, - cadenceValueMigrationEntry{ - StorageKey: interpreter.StorageKey{Key: "storage", Address: address}, - StorageMapKey: interpreter.StringStorageMapKey("type_value"), - Migration: "StaticTypeMigration", - }, - cadenceValueMigrationEntry{ - StorageKey: interpreter.StorageKey{Key: "storage", Address: address}, - StorageMapKey: interpreter.StringStorageMapKey("dictionary_with_account_type_keys"), - Migration: "StaticTypeMigration", - }, - cadenceValueMigrationEntry{ - StorageKey: interpreter.StorageKey{Key: "storage", Address: address}, - StorageMapKey: interpreter.StringStorageMapKey("dictionary_with_account_type_keys"), - Migration: "TypeKeyMigration", - }, - cadenceValueMigrationEntry{ - StorageKey: interpreter.StorageKey{Key: "storage", Address: address}, - StorageMapKey: interpreter.StringStorageMapKey("dictionary_with_account_type_keys"), - Migration: "StaticTypeMigration", - }, - cadenceValueMigrationEntry{ - StorageKey: interpreter.StorageKey{Key: "storage", Address: address}, - StorageMapKey: interpreter.StringStorageMapKey("dictionary_with_account_type_keys"), - Migration: "TypeKeyMigration", - }, - cadenceValueMigrationEntry{ - StorageKey: interpreter.StorageKey{Key: "storage", Address: address}, - StorageMapKey: interpreter.StringStorageMapKey("dictionary_with_account_type_keys"), - Migration: "StaticTypeMigration", - }, - cadenceValueMigrationEntry{ - StorageKey: interpreter.StorageKey{Key: "storage", Address: address}, - StorageMapKey: interpreter.StringStorageMapKey("dictionary_with_account_type_keys"), - Migration: "TypeKeyMigration", - }, - cadenceValueMigrationEntry{ - StorageKey: interpreter.StorageKey{Key: "storage", Address: address}, - StorageMapKey: interpreter.StringStorageMapKey("dictionary_with_account_type_keys"), - Migration: "StaticTypeMigration", - }, - cadenceValueMigrationEntry{ - StorageKey: interpreter.StorageKey{Key: "storage", Address: address}, - StorageMapKey: interpreter.StringStorageMapKey("dictionary_with_account_type_keys"), - Migration: "TypeKeyMigration", - }, - cadenceValueMigrationEntry{ - StorageKey: interpreter.StorageKey{Key: "storage", Address: address}, - StorageMapKey: interpreter.StringStorageMapKey("dictionary_with_account_type_keys"), - Migration: "StaticTypeMigration", - }, - cadenceValueMigrationEntry{ - StorageKey: interpreter.StorageKey{Key: "storage", Address: address}, - StorageMapKey: interpreter.StringStorageMapKey("dictionary_with_account_type_keys"), - Migration: "TypeKeyMigration", - }, - cadenceValueMigrationEntry{ - StorageKey: interpreter.StorageKey{Key: "storage", Address: address}, - StorageMapKey: interpreter.StringStorageMapKey("dictionary_with_account_type_keys"), - Migration: "StaticTypeMigration", - }, - cadenceValueMigrationEntry{ - StorageKey: interpreter.StorageKey{Key: "storage", Address: address}, - StorageMapKey: interpreter.StringStorageMapKey("dictionary_with_account_type_keys"), - Migration: "TypeKeyMigration", - }, - cadenceValueMigrationEntry{ - StorageKey: interpreter.StorageKey{Key: "storage", Address: address}, - StorageMapKey: interpreter.StringStorageMapKey("dictionary_with_account_type_keys"), - Migration: "StaticTypeMigration", - }, - cadenceValueMigrationEntry{ - StorageKey: interpreter.StorageKey{Key: "storage", Address: address}, - StorageMapKey: interpreter.StringStorageMapKey("dictionary_with_account_type_keys"), - Migration: "TypeKeyMigration", - }, - cadenceValueMigrationEntry{ - StorageKey: interpreter.StorageKey{Key: "storage", Address: address}, - StorageMapKey: interpreter.StringStorageMapKey("dictionary_with_account_type_keys"), - Migration: "StaticTypeMigration", - }, - cadenceValueMigrationEntry{ - StorageKey: interpreter.StorageKey{Key: "storage", Address: address}, - StorageMapKey: interpreter.StringStorageMapKey("dictionary_with_account_type_keys"), - Migration: "TypeKeyMigration", - }, - cadenceValueMigrationEntry{ - StorageKey: interpreter.StorageKey{Key: "storage", Address: address}, - StorageMapKey: interpreter.StringStorageMapKey("dictionary_with_account_type_keys"), - Migration: "StaticTypeMigration", - }, - cadenceValueMigrationEntry{ - StorageKey: interpreter.StorageKey{Key: "storage", Address: address}, - StorageMapKey: interpreter.StringStorageMapKey("dictionary_with_account_type_keys"), - Migration: "TypeKeyMigration", - }, - cadenceValueMigrationEntry{ - StorageKey: interpreter.StorageKey{Key: "storage", Address: address}, - StorageMapKey: interpreter.StringStorageMapKey("capability"), - Migration: "EntitlementsMigration", - }, - cadenceValueMigrationEntry{ - StorageKey: interpreter.StorageKey{Key: "storage", Address: address}, - StorageMapKey: interpreter.StringStorageMapKey("string_value_1"), - Migration: "StringNormalizingMigration", - }, - cadenceValueMigrationEntry{ - StorageKey: interpreter.StorageKey{Key: "storage", Address: address}, - StorageMapKey: interpreter.StringStorageMapKey("dictionary_with_restricted_typed_keys"), - Migration: "StaticTypeMigration", - }, - cadenceValueMigrationEntry{ - StorageKey: interpreter.StorageKey{Key: "storage", Address: address}, - StorageMapKey: interpreter.StringStorageMapKey("dictionary_with_restricted_typed_keys"), - Migration: "TypeKeyMigration", - }, - cadenceValueMigrationEntry{ - StorageKey: interpreter.StorageKey{Key: "storage", Address: address}, - StorageMapKey: interpreter.StringStorageMapKey("dictionary_with_restricted_typed_keys"), - Migration: "StaticTypeMigration", - }, - cadenceValueMigrationEntry{ - StorageKey: interpreter.StorageKey{Key: "storage", Address: address}, - StorageMapKey: interpreter.StringStorageMapKey("dictionary_with_restricted_typed_keys"), - Migration: "TypeKeyMigration", - }, - cadenceValueMigrationEntry{ - StorageKey: interpreter.StorageKey{Key: "public", Address: address}, - StorageMapKey: interpreter.StringStorageMapKey("flowTokenReceiver"), - Migration: "EntitlementsMigration", - }, - cadenceValueMigrationEntry{ - StorageKey: interpreter.StorageKey{Key: "public", Address: address}, - StorageMapKey: interpreter.StringStorageMapKey("linkR"), - Migration: "EntitlementsMigration", - }, - cadenceValueMigrationEntry{ - StorageKey: interpreter.StorageKey{Key: "public", Address: address}, - StorageMapKey: interpreter.StringStorageMapKey("flowTokenBalance"), - Migration: "EntitlementsMigration", - }, - }, - - accountReportEntries, - ) -} - -type testReportWriterFactory struct { - lock sync.Mutex - reportWriters map[string]*testReportWriter -} - -func (f *testReportWriterFactory) ReportWriter(dataNamespace string) reporters.ReportWriter { - f.lock.Lock() - defer f.lock.Unlock() - - if f.reportWriters == nil { - f.reportWriters = make(map[string]*testReportWriter) - } - reportWriter := &testReportWriter{} - if _, ok := f.reportWriters[dataNamespace]; ok { - panic(fmt.Sprintf("report writer already exists for namespace %s", dataNamespace)) - } - f.reportWriters[dataNamespace] = reportWriter - return reportWriter -} - -type testReportWriter struct { - lock sync.Mutex - entries []any -} - -var _ reporters.ReportWriter = &testReportWriter{} - -func (r *testReportWriter) Write(entry any) { - r.lock.Lock() - defer r.lock.Unlock() - - r.entries = append(r.entries, entry) -} - -func (r *testReportWriter) Close() {} - -func TestBootstrappedStateMigration(t *testing.T) { - t.Parallel() - - rwf := &testReportWriterFactory{} - - logWriter := &writer{} - logger := zerolog.New(logWriter).Level(zerolog.ErrorLevel) - - const nWorker = 2 - - const chainID = flow.Emulator - // TODO: EVM contract is not deployed in snapshot yet, so can't update it - const evmContractChange = EVMContractChangeNone - - const burnerContractChange = BurnerContractChangeUpdate - - payloads, err := newBootstrapPayloads(chainID) - require.NoError(t, err) - - registersByAccount, err := registers.NewByAccountFromPayloads(payloads) - require.NoError(t, err) - - migrations := NewCadence1Migrations( - logger, - t.TempDir(), - rwf, - Options{ - NWorker: nWorker, - ChainID: chainID, - EVMContractChange: evmContractChange, - BurnerContractChange: burnerContractChange, - VerboseErrorOutput: true, - }, - ) - - for _, migration := range migrations { - err = migration.Migrate(registersByAccount) - require.NoError( - t, - err, - "migration `%s` failed, logs: %v", - migration.Name, - logWriter.logs, - ) - } - - // Check error logs. - require.Empty(t, logWriter.logs) -} - -func TestProgramParsingError(t *testing.T) { - t.Parallel() - - rwf := &testReportWriterFactory{} - - logWriter := &writer{} - logger := zerolog.New(logWriter).Level(zerolog.ErrorLevel) - - const nWorker = 2 - - const chainID = flow.Emulator - chain := chainID.Chain() - - testAddress := common.Address(chain.ServiceAddress()) - - payloads, err := newBootstrapPayloads(chainID) - require.NoError(t, err) - - registersByAccount, err := registers.NewByAccountFromPayloads(payloads) - require.NoError(t, err) - - runtime, err := NewInterpreterMigrationRuntime( - registersByAccount, - chainID, - InterpreterMigrationRuntimeConfig{}, - ) - require.NoError(t, err) - - storage := runtime.Storage - - storageMap := storage.GetStorageMap( - testAddress, - common.PathDomainStorage.Identifier(), - true, - ) - - const contractName = "C" - contractLocation := common.NewAddressLocation(nil, testAddress, contractName) - - const nonExistingStructQualifiedIdentifier = contractName + ".NonExistingStruct" - - capabilityValue := interpreter.NewUnmeteredCapabilityValue( - 1, - interpreter.AddressValue(testAddress), - interpreter.NewReferenceStaticType( - nil, - interpreter.UnauthorizedAccess, - interpreter.NewCompositeStaticType( - nil, - contractLocation, - nonExistingStructQualifiedIdentifier, - contractLocation.TypeID(nil, nonExistingStructQualifiedIdentifier), - ), - ), - ) - - storageMap.WriteValue( - runtime.Interpreter, - interpreter.StringStorageMapKey("test"), - capabilityValue, - ) - - err = storage.NondeterministicCommit(runtime.Interpreter, false) - require.NoError(t, err) - - // finalize the transaction - result, err := runtime.TransactionState.FinalizeMainTransaction() - require.NoError(t, err) - - // Merge the changes into the registers - - expectedAddresses := map[flow.Address]struct{}{ - flow.Address(testAddress): {}, - } - - err = registers.ApplyChanges( - registersByAccount, - result.WriteSet, - expectedAddresses, - logger, - ) - require.NoError(t, err) - - // Set the code for the old program - - err = registersByAccount.Set( - string(testAddress[:]), - flow.ContractKey(contractName), - []byte(`pub contract C {}`), - ) - require.NoError(t, err) - - encodedContractNames, err := environment.EncodeContractNames([]string{contractName}) - require.NoError(t, err) - - err = registersByAccount.Set( - string(testAddress[:]), - flow.ContractNamesKey, - encodedContractNames, - ) - require.NoError(t, err) - - // Migrate - - // TODO: EVM contract is not deployed in snapshot yet, so can't update it - const evmContractChange = EVMContractChangeNone - - const burnerContractChange = BurnerContractChangeUpdate - - migrations := NewCadence1Migrations( - logger, - t.TempDir(), - rwf, - Options{ - NWorker: nWorker, - ChainID: chainID, - EVMContractChange: evmContractChange, - BurnerContractChange: burnerContractChange, - VerboseErrorOutput: true, - }, - ) - - for _, migration := range migrations { - err = migration.Migrate(registersByAccount) - require.NoError( - t, - err, - "migration `%s` failed, logs: %v", - migration.Name, - logWriter.logs, - ) - } - - reporter := rwf.reportWriters[contractCheckingReporterName] - require.NotNil(t, reporter) - - var messages []string - - for _, entry := range reporter.entries { - if errorEntry, isErrorEntry := entry.(contractCheckingFailure); isErrorEntry { - messages = append(messages, errorEntry.Error) - break - } - } - - require.Len(t, messages, 1) - - assert.Contains(t, messages[0], "`pub` is no longer a valid access keyword") - assert.NotContains(t, messages[0], "runtime/debug.Stack()") -} - -func TestCoreContractUsage(t *testing.T) { - t.Parallel() - - const chainID = flow.Emulator - - migrate := func(t *testing.T, staticType interpreter.StaticType) interpreter.StaticType { - - rwf := &testReportWriterFactory{} - - logWriter := &writer{} - logger := zerolog.New(logWriter).Level(zerolog.ErrorLevel) - - const nWorker = 2 - - chain := chainID.Chain() - - testFlowAddress, err := chain.AddressAtIndex(1_000_000) - require.NoError(t, err) - - testAddress := common.Address(testFlowAddress) - - payloads, err := newBootstrapPayloads(chainID) - require.NoError(t, err) - - registersByAccount, err := registers.NewByAccountFromPayloads(payloads) - require.NoError(t, err) - - runtime, err := NewInterpreterMigrationRuntime( - registersByAccount, - chainID, - InterpreterMigrationRuntimeConfig{}, - ) - require.NoError(t, err) - - err = runtime.Accounts.Create(nil, testFlowAddress) - require.NoError(t, err) - - storage := runtime.Storage - - storageDomain := common.PathDomainStorage.Identifier() - storageMapKey := interpreter.StringStorageMapKey("test") - - storageMap := storage.GetStorageMap( - testAddress, - storageDomain, - true, - ) - - capabilityValue := interpreter.NewUnmeteredCapabilityValue( - 1, - interpreter.AddressValue(testAddress), - staticType, - ) - - storageMap.WriteValue( - runtime.Interpreter, - storageMapKey, - capabilityValue, - ) - - err = storage.NondeterministicCommit(runtime.Interpreter, false) - require.NoError(t, err) - - // finalize the transaction - result, err := runtime.TransactionState.FinalizeMainTransaction() - require.NoError(t, err) - - // Merge the changes to the original payloads. - - expectedAddresses := map[flow.Address]struct{}{ - flow.Address(testAddress): {}, - } - - err = registers.ApplyChanges( - registersByAccount, - result.WriteSet, - expectedAddresses, - logger, - ) - require.NoError(t, err) - - // Migrate - - // TODO: EVM contract is not deployed in snapshot yet, so can't update it - const evmContractChange = EVMContractChangeNone - - const burnerContractChange = BurnerContractChangeUpdate - - migrations := NewCadence1Migrations( - logger, - t.TempDir(), - rwf, - Options{ - NWorker: nWorker, - ChainID: chainID, - EVMContractChange: evmContractChange, - BurnerContractChange: burnerContractChange, - VerboseErrorOutput: true, - }, - ) - - for _, migration := range migrations { - err = migration.Migrate(registersByAccount) - require.NoError( - t, - err, - "migration `%s` failed, logs: %v", - migration.Name, - logWriter.logs, - ) - } - - // Check error logs - require.Len(t, logWriter.logs, 0) - - // Get result - - mr, err := NewInterpreterMigrationRuntime( - registersByAccount, - chainID, - InterpreterMigrationRuntimeConfig{}, - ) - require.NoError(t, err) - - storageMap = mr.Storage.GetStorageMap( - testAddress, - storageDomain, - false, - ) - require.NotNil(t, storageMap) - - resultValue := storageMap.ReadValue(nil, storageMapKey) - require.NotNil(t, resultValue) - require.IsType(t, &interpreter.IDCapabilityValue{}, resultValue) - - resultCap := resultValue.(*interpreter.IDCapabilityValue) - return resultCap.BorrowType - } - - t.Run("&FungibleToken.Vault => auth(Withdraw) &{FungibleToken.Vault}", func(t *testing.T) { - t.Parallel() - - systemContracts := systemcontracts.SystemContractsForChain(chainID) - - const fungibleTokenContractName = "FungibleToken" - fungibleTokenContractLocation := common.NewAddressLocation( - nil, - common.Address(systemContracts.FungibleToken.Address), - fungibleTokenContractName, - ) - - const fungibleTokenVaultTypeQualifiedIdentifier = fungibleTokenContractName + ".Vault" - - input := interpreter.NewReferenceStaticType( - nil, - interpreter.UnauthorizedAccess, - interpreter.NewCompositeStaticType( - nil, - fungibleTokenContractLocation, - fungibleTokenVaultTypeQualifiedIdentifier, - fungibleTokenContractLocation.TypeID(nil, fungibleTokenVaultTypeQualifiedIdentifier), - ), - ) - - const fungibleTokenWithdrawTypeQualifiedIdentifier = fungibleTokenContractName + ".Withdraw" - expected := interpreter.NewReferenceStaticType( - nil, - interpreter.NewEntitlementSetAuthorization( - nil, - func() []common.TypeID { - return []common.TypeID{ - fungibleTokenContractLocation.TypeID(nil, fungibleTokenWithdrawTypeQualifiedIdentifier), - } - }, - 1, - sema.Conjunction, - ), - interpreter.NewIntersectionStaticType( - nil, - []*interpreter.InterfaceStaticType{ - interpreter.NewInterfaceStaticType( - nil, - fungibleTokenContractLocation, - fungibleTokenVaultTypeQualifiedIdentifier, - fungibleTokenContractLocation.TypeID(nil, fungibleTokenVaultTypeQualifiedIdentifier), - ), - }, - ), - ) - - actual := migrate(t, input) - - require.Equal(t, expected, actual) - }) - - t.Run("&FungibleToken.Vault{FungibleToken.Balance} => &{FungibleToken.Vault}", func(t *testing.T) { - t.Parallel() - - systemContracts := systemcontracts.SystemContractsForChain(chainID) - - const fungibleTokenContractName = "FungibleToken" - fungibleTokenContractLocation := common.NewAddressLocation( - nil, - common.Address(systemContracts.FungibleToken.Address), - fungibleTokenContractName, - ) - - const fungibleTokenVaultTypeQualifiedIdentifier = fungibleTokenContractName + ".Vault" - const fungibleTokenBalanceTypeQualifiedIdentifier = fungibleTokenContractName + ".Balance" - - inputIntersectionType := interpreter.NewIntersectionStaticType( - nil, - []*interpreter.InterfaceStaticType{ - interpreter.NewInterfaceStaticType( - nil, - fungibleTokenContractLocation, - fungibleTokenBalanceTypeQualifiedIdentifier, - fungibleTokenContractLocation.TypeID(nil, fungibleTokenBalanceTypeQualifiedIdentifier), - ), - }, - ) - inputIntersectionType.LegacyType = interpreter.NewCompositeStaticType( - nil, - fungibleTokenContractLocation, - fungibleTokenVaultTypeQualifiedIdentifier, - fungibleTokenContractLocation.TypeID(nil, fungibleTokenVaultTypeQualifiedIdentifier), - ) - - input := interpreter.NewReferenceStaticType( - nil, - interpreter.UnauthorizedAccess, - inputIntersectionType, - ) - - expected := interpreter.NewReferenceStaticType( - nil, - interpreter.UnauthorizedAccess, - interpreter.NewIntersectionStaticType( - nil, - []*interpreter.InterfaceStaticType{ - interpreter.NewInterfaceStaticType( - nil, - fungibleTokenContractLocation, - fungibleTokenVaultTypeQualifiedIdentifier, - fungibleTokenContractLocation.TypeID(nil, fungibleTokenVaultTypeQualifiedIdentifier), - ), - }, - ), - ) - - actual := migrate(t, input) - - require.Equal(t, expected, actual) - }) - - t.Run("&NonFungibleToken.NFT => &{NonFungibleToken.NFT}", func(t *testing.T) { - t.Parallel() - - systemContracts := systemcontracts.SystemContractsForChain(chainID) - - const nonFungibleTokenContractName = "NonFungibleToken" - nonFungibleTokenContractLocation := common.NewAddressLocation( - nil, - common.Address(systemContracts.NonFungibleToken.Address), - nonFungibleTokenContractName, - ) - - const nonFungibleTokenNFTTypeQualifiedIdentifier = nonFungibleTokenContractName + ".NFT" - - input := interpreter.NewReferenceStaticType( - nil, - interpreter.UnauthorizedAccess, - interpreter.NewCompositeStaticType( - nil, - nonFungibleTokenContractLocation, - nonFungibleTokenNFTTypeQualifiedIdentifier, - nonFungibleTokenContractLocation.TypeID(nil, nonFungibleTokenNFTTypeQualifiedIdentifier), - ), - ) - - expected := interpreter.NewReferenceStaticType( - nil, - interpreter.UnauthorizedAccess, - interpreter.NewIntersectionStaticType( - nil, - []*interpreter.InterfaceStaticType{ - interpreter.NewInterfaceStaticType( - nil, - nonFungibleTokenContractLocation, - nonFungibleTokenNFTTypeQualifiedIdentifier, - nonFungibleTokenContractLocation.TypeID(nil, nonFungibleTokenNFTTypeQualifiedIdentifier), - ), - }, - ), - ) - - actual := migrate(t, input) - - require.Equal(t, expected, actual) - }) - - t.Run("&NonFungibleToken.Collection => auth(Withdraw) &{NonFungibleToken.Collection}", func(t *testing.T) { - t.Parallel() - - systemContracts := systemcontracts.SystemContractsForChain(chainID) - - const nonFungibleTokenContractName = "NonFungibleToken" - nonFungibleTokenContractLocation := common.NewAddressLocation( - nil, - common.Address(systemContracts.NonFungibleToken.Address), - nonFungibleTokenContractName, - ) - - const nonFungibleTokenCollectionTypeQualifiedIdentifier = nonFungibleTokenContractName + ".Collection" - - input := interpreter.NewReferenceStaticType( - nil, - interpreter.UnauthorizedAccess, - interpreter.NewCompositeStaticType( - nil, - nonFungibleTokenContractLocation, - nonFungibleTokenCollectionTypeQualifiedIdentifier, - nonFungibleTokenContractLocation.TypeID(nil, nonFungibleTokenCollectionTypeQualifiedIdentifier), - ), - ) - - const nonFungibleTokenWithdrawTypeQualifiedIdentifier = nonFungibleTokenContractName + ".Withdraw" - expected := interpreter.NewReferenceStaticType( - nil, - interpreter.NewEntitlementSetAuthorization( - nil, - func() []common.TypeID { - return []common.TypeID{ - nonFungibleTokenContractLocation.TypeID(nil, nonFungibleTokenWithdrawTypeQualifiedIdentifier), - } - }, - 1, - sema.Conjunction, - ), - interpreter.NewIntersectionStaticType( - nil, - []*interpreter.InterfaceStaticType{ - interpreter.NewInterfaceStaticType( - nil, - nonFungibleTokenContractLocation, - nonFungibleTokenCollectionTypeQualifiedIdentifier, - nonFungibleTokenContractLocation.TypeID(nil, nonFungibleTokenCollectionTypeQualifiedIdentifier), - ), - }, - ), - ) - - actual := migrate(t, input) - - require.Equal(t, expected, actual) - }) - - t.Run("&NonFungibleToken.Provider => auth(Withdraw) &{NonFungibleToken.Provider}", func(t *testing.T) { - t.Parallel() - - systemContracts := systemcontracts.SystemContractsForChain(chainID) - - const nonFungibleTokenContractName = "NonFungibleToken" - nonFungibleTokenContractLocation := common.NewAddressLocation( - nil, - common.Address(systemContracts.NonFungibleToken.Address), - nonFungibleTokenContractName, - ) - - const nonFungibleTokenProviderTypeQualifiedIdentifier = nonFungibleTokenContractName + ".Provider" - - input := interpreter.NewReferenceStaticType( - nil, - interpreter.UnauthorizedAccess, - interpreter.NewInterfaceStaticType( - nil, - nonFungibleTokenContractLocation, - nonFungibleTokenProviderTypeQualifiedIdentifier, - nonFungibleTokenContractLocation.TypeID(nil, nonFungibleTokenProviderTypeQualifiedIdentifier), - ), - ) - - const nonFungibleTokenWithdrawTypeQualifiedIdentifier = nonFungibleTokenContractName + ".Withdraw" - expected := interpreter.NewReferenceStaticType( - nil, - interpreter.NewEntitlementSetAuthorization( - nil, - func() []common.TypeID { - return []common.TypeID{ - nonFungibleTokenContractLocation.TypeID(nil, nonFungibleTokenWithdrawTypeQualifiedIdentifier), - } - }, - 1, - sema.Conjunction, - ), - interpreter.NewIntersectionStaticType( - nil, - []*interpreter.InterfaceStaticType{ - interpreter.NewInterfaceStaticType( - nil, - nonFungibleTokenContractLocation, - nonFungibleTokenProviderTypeQualifiedIdentifier, - nonFungibleTokenContractLocation.TypeID(nil, nonFungibleTokenProviderTypeQualifiedIdentifier), - ), - }, - ), - ) - - actual := migrate(t, input) - - require.Equal(t, expected, actual) - }) - - t.Run("&NonFungibleToken.Collection{NonFungibleToken.CollectionPublic} => &{NonFungibleToken.Collection}", func(t *testing.T) { - t.Parallel() - - systemContracts := systemcontracts.SystemContractsForChain(chainID) - - const nonFungibleTokenContractName = "NonFungibleToken" - nonFungibleTokenContractLocation := common.NewAddressLocation( - nil, - common.Address(systemContracts.NonFungibleToken.Address), - nonFungibleTokenContractName, - ) - - const nonFungibleTokenVaultTypeQualifiedIdentifier = nonFungibleTokenContractName + ".Collection" - const nonFungibleTokenCollectionPublicTypeQualifiedIdentifier = nonFungibleTokenContractName + ".CollectionPublic" - - inputIntersectionType := interpreter.NewIntersectionStaticType( - nil, - []*interpreter.InterfaceStaticType{ - interpreter.NewInterfaceStaticType( - nil, - nonFungibleTokenContractLocation, - nonFungibleTokenCollectionPublicTypeQualifiedIdentifier, - nonFungibleTokenContractLocation.TypeID(nil, nonFungibleTokenCollectionPublicTypeQualifiedIdentifier), - ), - }, - ) - inputIntersectionType.LegacyType = interpreter.NewCompositeStaticType( - nil, - nonFungibleTokenContractLocation, - nonFungibleTokenVaultTypeQualifiedIdentifier, - nonFungibleTokenContractLocation.TypeID(nil, nonFungibleTokenVaultTypeQualifiedIdentifier), - ) - - input := interpreter.NewReferenceStaticType( - nil, - interpreter.UnauthorizedAccess, - inputIntersectionType, - ) - - expected := interpreter.NewReferenceStaticType( - nil, - interpreter.UnauthorizedAccess, - interpreter.NewIntersectionStaticType( - nil, - []*interpreter.InterfaceStaticType{ - interpreter.NewInterfaceStaticType( - nil, - nonFungibleTokenContractLocation, - nonFungibleTokenVaultTypeQualifiedIdentifier, - nonFungibleTokenContractLocation.TypeID(nil, nonFungibleTokenVaultTypeQualifiedIdentifier), - ), - }, - ), - ) - - actual := migrate(t, input) - - require.Equal(t, expected, actual) - }) - - t.Run("&{MetadataViews.Resolver} => &{ViewResolver.Resolver}", func(t *testing.T) { - t.Parallel() - - systemContracts := systemcontracts.SystemContractsForChain(chainID) - - const metadataViewsContractName = "MetadataViews" - metadataViewsContractLocation := common.NewAddressLocation( - nil, - common.Address(systemContracts.MetadataViews.Address), - metadataViewsContractName, - ) - - const metadataViewsResolverTypeQualifiedIdentifier = metadataViewsContractName + ".Resolver" - - input := interpreter.NewReferenceStaticType( - nil, - interpreter.UnauthorizedAccess, - interpreter.NewIntersectionStaticType( - nil, - []*interpreter.InterfaceStaticType{ - interpreter.NewInterfaceStaticType( - nil, - metadataViewsContractLocation, - metadataViewsResolverTypeQualifiedIdentifier, - metadataViewsContractLocation.TypeID(nil, metadataViewsResolverTypeQualifiedIdentifier), - ), - }, - ), - ) - - const viewResolverContractName = "ViewResolver" - viewResolverContractLocation := common.NewAddressLocation( - nil, - common.Address(systemContracts.MetadataViews.Address), - viewResolverContractName, - ) - - const viewResolverResolverTypeQualifiedIdentifier = viewResolverContractName + ".Resolver" - - expected := interpreter.NewReferenceStaticType( - nil, - interpreter.UnauthorizedAccess, - interpreter.NewIntersectionStaticType( - nil, - []*interpreter.InterfaceStaticType{ - interpreter.NewInterfaceStaticType( - nil, - viewResolverContractLocation, - viewResolverResolverTypeQualifiedIdentifier, - viewResolverContractLocation.TypeID(nil, viewResolverResolverTypeQualifiedIdentifier), - ), - }, - ), - ) - - actual := migrate(t, input) - - require.Equal(t, expected, actual) - }) - - t.Run("&{MetadataViews.ResolverCollection} => &{ViewResolver.ResolverCollection}", func(t *testing.T) { - t.Parallel() - - systemContracts := systemcontracts.SystemContractsForChain(chainID) - - const metadataViewsContractName = "MetadataViews" - metadataViewsContractLocation := common.NewAddressLocation( - nil, - common.Address(systemContracts.MetadataViews.Address), - metadataViewsContractName, - ) - - const metadataViewsResolverTypeQualifiedIdentifier = metadataViewsContractName + ".ResolverCollection" - - input := interpreter.NewReferenceStaticType( - nil, - interpreter.UnauthorizedAccess, - interpreter.NewIntersectionStaticType( - nil, - []*interpreter.InterfaceStaticType{ - interpreter.NewInterfaceStaticType( - nil, - metadataViewsContractLocation, - metadataViewsResolverTypeQualifiedIdentifier, - metadataViewsContractLocation.TypeID(nil, metadataViewsResolverTypeQualifiedIdentifier), - ), - }, - ), - ) - - const viewResolverContractName = "ViewResolver" - viewResolverContractLocation := common.NewAddressLocation( - nil, - common.Address(systemContracts.MetadataViews.Address), - viewResolverContractName, - ) - - const viewResolverResolverTypeQualifiedIdentifier = viewResolverContractName + ".ResolverCollection" - - expected := interpreter.NewReferenceStaticType( - nil, - interpreter.UnauthorizedAccess, - interpreter.NewIntersectionStaticType( - nil, - []*interpreter.InterfaceStaticType{ - interpreter.NewInterfaceStaticType( - nil, - viewResolverContractLocation, - viewResolverResolverTypeQualifiedIdentifier, - viewResolverContractLocation.TypeID(nil, viewResolverResolverTypeQualifiedIdentifier), - ), - }, - ), - ) - - actual := migrate(t, input) - - require.Equal(t, expected, actual) - }) - -} - -func TestDictionaryKeyConflictEntry_MarshalJSON(t *testing.T) { - - t.Parallel() - - e := dictionaryKeyConflictEntry{ - AddressPath: interpreter.AddressPath{ - Address: common.MustBytesToAddress([]byte{0x1}), - Path: interpreter.PathValue{ - Domain: common.PathDomainPublic, - Identifier: "test", - }, - }, - } - - actual, err := e.MarshalJSON() - require.NoError(t, err) - - require.JSONEq(t, - //language=JSON - `{ - "kind": "dictionary-key-conflict", - "account_address": "0x0000000000000001", - "path": "/public/test" - }`, - string(actual), - ) -} - -func TestLinkMissingTargetEntry_MarshalJSON(t *testing.T) { - - t.Parallel() - - e := linkMissingTargetEntry{ - AddressPath: interpreter.AddressPath{ - Address: common.MustBytesToAddress([]byte{0x1}), - Path: interpreter.PathValue{ - Domain: common.PathDomainPublic, - Identifier: "test", - }, - }, - } - - actual, err := e.MarshalJSON() - require.NoError(t, err) - - require.JSONEq(t, - //language=JSON - `{ - "kind": "link-missing-target", - "account_address": "0x0000000000000001", - "path": "/public/test" - }`, - string(actual), - ) -} - -func TestCapabilityMissingCapabilityIDEntry_MarshalJSON(t *testing.T) { - - t.Parallel() - - e := capabilityMissingCapabilityIDEntry{ - AccountAddress: common.MustBytesToAddress([]byte{0x2}), - AddressPath: interpreter.AddressPath{ - Address: common.MustBytesToAddress([]byte{0x1}), - Path: interpreter.PathValue{ - Domain: common.PathDomainPublic, - Identifier: "test", - }, - }, - } - - actual, err := e.MarshalJSON() - require.NoError(t, err) - - require.JSONEq(t, - //language=JSON - `{ - "kind": "capability-missing-capability-id", - "account_address": "0x0000000000000002", - "address": "0x0000000000000001", - "path": "/public/test" - }`, - string(actual), - ) -} - -func TestCapabilityMigrationEntry_MarshalJSON(t *testing.T) { - - t.Parallel() - - e := capabilityMigrationEntry{ - AccountAddress: common.MustBytesToAddress([]byte{0x2}), - AddressPath: interpreter.AddressPath{ - Address: common.MustBytesToAddress([]byte{0x1}), - Path: interpreter.PathValue{ - Domain: common.PathDomainPublic, - Identifier: "test", - }, - }, - BorrowType: interpreter.NewReferenceStaticType( - nil, - interpreter.UnauthorizedAccess, - interpreter.PrimitiveStaticTypeInt, - ), - } - - actual, err := e.MarshalJSON() - require.NoError(t, err) - - require.JSONEq(t, - //language=JSON - `{ - "kind": "capability-migration-success", - "account_address": "0x0000000000000002", - "address": "0x0000000000000001", - "path": "/public/test", - "borrow_type": "&Int", - "capability_id": "0" - }`, - string(actual), - ) -} - -func TestLinkMigrationEntry_MarshalJSON(t *testing.T) { - - t.Parallel() - - e := linkMigrationEntry{ - AccountAddressPath: interpreter.AddressPath{ - Address: common.MustBytesToAddress([]byte{0x1}), - Path: interpreter.PathValue{ - Domain: common.PathDomainPublic, - Identifier: "test", - }, - }, - CapabilityID: 42, - } - - actual, err := e.MarshalJSON() - require.NoError(t, err) - - require.JSONEq(t, - //language=JSON - `{ - "kind": "link-migration-success", - "account_address": "0x0000000000000001", - "path": "/public/test", - "capability_id": 42 - }`, - string(actual), - ) -} - -func TestCadenceValueMigrationFailureEntry_MarshalJSON(t *testing.T) { - - t.Parallel() - - e := cadenceValueMigrationFailureEntry{ - StorageKey: interpreter.StorageKey{ - Address: common.MustBytesToAddress([]byte{0x1}), - Key: "storage", - }, - StorageMapKey: interpreter.StringStorageMapKey("test"), - Migration: "test-migration", - Message: "unknown", - } - - actual, err := e.MarshalJSON() - require.NoError(t, err) - - require.JSONEq(t, - //language=JSON - `{ - "kind": "cadence-value-migration-failure", - "account_address": "0x0000000000000001", - "domain": "storage", - "key": "test", - "migration": "test-migration", - "message": "unknown" - }`, - string(actual), - ) -} - -func TestCadenceValueMigrationEntry_MarshalJSON(t *testing.T) { - - t.Parallel() - - e := cadenceValueMigrationEntry{ - StorageKey: interpreter.StorageKey{ - Address: common.MustBytesToAddress([]byte{0x1}), - Key: "storage", - }, - StorageMapKey: interpreter.StringStorageMapKey("test"), - Migration: "test-migration", - } - - actual, err := e.MarshalJSON() - require.NoError(t, err) - - require.JSONEq(t, - //language=JSON - `{ - "kind": "cadence-value-migration-success", - "account_address": "0x0000000000000001", - "domain": "storage", - "key": "test", - "migration": "test-migration" - }`, - string(actual), - ) -} - -func TestStoragePathCapabilityMigration(t *testing.T) { - t.Parallel() - - rwf := &testReportWriterFactory{} - - logWriter := &writer{} - logger := zerolog.New(logWriter).Level(zerolog.ErrorLevel) - - const nWorker = 2 - - const chainID = flow.Emulator - - payloads, err := newBootstrapPayloads(chainID) - require.NoError(t, err) - - registersByAccount, err := registers.NewByAccountFromPayloads(payloads) - require.NoError(t, err) - - addressA := common.Address(chainID.Chain().ServiceAddress()) - - // TODO: is there a way to read this address from bootstrapped payloads - // rather than hard-coding it here? - addressB, err := common.HexToAddress("0xe5a8b7f23e8b548f") - require.NoError(t, err) - - addressC, err := common.HexToAddress("0x1") - require.NoError(t, err) - - // Store a contract in `addressC`. - - const contractName = "Test" - contractAddress := addressC - - err = registersByAccount.Set( - string(contractAddress[:]), - flow.ContractKey(contractName), - []byte(` - pub contract Test { - access(all) resource R {} - } - `), - ) - require.NoError(t, err) - - encodedContractNames, err := environment.EncodeContractNames([]string{contractName}) - require.NoError(t, err) - - err = registersByAccount.Set( - string(contractAddress[:]), - flow.ContractNamesKey, - encodedContractNames, - ) - require.NoError(t, err) - - migrationRuntime, err := NewInterpreterMigrationRuntime( - registersByAccount, - chainID, - InterpreterMigrationRuntimeConfig{}, - ) - require.NoError(t, err) - - storage := migrationRuntime.Storage - storageDomain := common.PathDomainStorage.Identifier() - - // Store a typed-capability with storage path - - storageMapForAddressA := storage.GetStorageMap( - addressA, - storageDomain, - true, - ) - - borrowType := interpreter.NewReferenceStaticType( - nil, - interpreter.UnauthorizedAccess, - interpreter.PrimitiveStaticTypeAnyStruct, - ) - - aCapStorageMapKey := interpreter.StringStorageMapKey("aCap") - - aCapability := interpreter.NewUnmeteredPathCapabilityValue( - borrowType, - // Important: Capability must be for a different address, - // compared to where the capability is stored. - interpreter.AddressValue(addressB), - interpreter.NewUnmeteredPathValue(common.PathDomainStorage, "a"), - ) - - storageMapForAddressA.WriteValue( - migrationRuntime.Interpreter, - aCapStorageMapKey, - aCapability, - ) - - // Store another capability with storage path, but without a borrow type. - // But the target path does not contain a value. - - bCapStorageMapKey := interpreter.StringStorageMapKey("bCap") - - bCapability := interpreter.NewUnmeteredPathCapabilityValue( - // NOTE: no borrow type - nil, - // Important: Capability must be for a different address, - // compared to where the capability is stored. - interpreter.AddressValue(addressB), - interpreter.NewUnmeteredPathValue(common.PathDomainStorage, "b"), - ) - - storageMapForAddressA.WriteValue( - migrationRuntime.Interpreter, - bCapStorageMapKey, - bCapability, - ) - - // Store a third capability with storage path, without a borrow type. - // But the target path contains a value. - - storageMapForAddressB := storage.GetStorageMap( - addressB, - storageDomain, - true, - ) - - storageMapForAddressB.WriteValue( - migrationRuntime.Interpreter, - interpreter.StringStorageMapKey("c"), - interpreter.NewUnmeteredStringValue("This is the bar value"), - ) - - cCapStorageMapKey := interpreter.StringStorageMapKey("cCap") - - cCapability := interpreter.NewUnmeteredPathCapabilityValue( - // NOTE: no borrow type - nil, - // Important: Capability must be for a different address, - // compared to where the capability is stored. - interpreter.AddressValue(addressB), - interpreter.NewUnmeteredPathValue(common.PathDomainStorage, "c"), - ) - - storageMapForAddressA.WriteValue( - migrationRuntime.Interpreter, - cCapStorageMapKey, - cCapability, - ) - - // Store a fourth capability with storage path, and with a broken type - - dBorrowType := interpreter.NewReferenceStaticType( - nil, - interpreter.UnauthorizedAccess, - interpreter.NewCompositeStaticTypeComputeTypeID( - nil, - common.NewAddressLocation(nil, contractAddress, "Test"), - "Test.R", - ), - ) - - dCapStorageMapKey := interpreter.StringStorageMapKey("dCap") - - dCapability := interpreter.NewUnmeteredPathCapabilityValue( - dBorrowType, - // Important: Capability must be for a different address, - // compared to where the capability is stored. - interpreter.AddressValue(addressB), - interpreter.NewUnmeteredPathValue(common.PathDomainStorage, "d"), - ) - - storageMapForAddressA.WriteValue( - migrationRuntime.Interpreter, - dCapStorageMapKey, - dCapability, - ) - - // Commit - - err = storage.NondeterministicCommit(migrationRuntime.Interpreter, false) - require.NoError(t, err) - - // finalize the transaction - result, err := migrationRuntime.TransactionState.FinalizeMainTransaction() - require.NoError(t, err) - - // Merge the changes into the registers - - expectedAddresses := map[flow.Address]struct{}{ - flow.Address(addressA): {}, - } - - err = registers.ApplyChanges( - registersByAccount, - result.WriteSet, - expectedAddresses, - logger, - ) - require.NoError(t, err) - - // Migrate - - // TODO: EVM contract is not deployed in snapshot yet, so can't update it - const evmContractChange = EVMContractChangeNone - - const burnerContractChange = BurnerContractChangeUpdate - - migrations := NewCadence1Migrations( - logger, - t.TempDir(), - rwf, - Options{ - NWorker: nWorker, - ChainID: chainID, - EVMContractChange: evmContractChange, - BurnerContractChange: burnerContractChange, - VerboseErrorOutput: true, - }, - ) - - for _, migration := range migrations { - err = migration.Migrate(registersByAccount) - require.NoError( - t, - err, - "migration `%s` failed, logs: %v", - migration.Name, - logWriter.logs, - ) - } - - reporter := rwf.reportWriters[capabilityValueMigrationReporterName] - require.NotNil(t, reporter) - require.Len(t, reporter.entries, 7) - - inferredBorrowType := interpreter.NewReferenceStaticType( - nil, - interpreter.UnauthorizedAccess, - interpreter.PrimitiveStaticTypeString, - ) - - require.Equal( - t, - []any{ - capabilityMigrationEntry{ - AccountAddress: addressA, - AddressPath: interpreter.AddressPath{ - Address: addressB, - Path: interpreter.NewUnmeteredPathValue(common.PathDomainStorage, "c"), - }, - BorrowType: interpreter.NewReferenceStaticType( - nil, - interpreter.UnauthorizedAccess, - interpreter.PrimitiveStaticTypeString, - ), - CapabilityID: 4, - }, - cadenceValueMigrationEntry{ - StorageKey: interpreter.StorageKey{ - Key: storageDomain, - Address: addressA, - }, - StorageMapKey: cCapStorageMapKey, - Migration: "CapabilityValueMigration", - }, - capabilityMigrationEntry{ - AccountAddress: addressA, - AddressPath: interpreter.AddressPath{ - Address: addressB, - Path: interpreter.NewUnmeteredPathValue(common.PathDomainStorage, "d"), - }, - BorrowType: dBorrowType, - CapabilityID: 5, - }, - cadenceValueMigrationEntry{ - StorageKey: interpreter.StorageKey{ - Key: storageDomain, - Address: addressA, - }, - StorageMapKey: dCapStorageMapKey, - Migration: "CapabilityValueMigration", - }, - capabilityMigrationEntry{ - AccountAddress: addressA, - AddressPath: interpreter.AddressPath{ - Address: addressB, - Path: interpreter.NewUnmeteredPathValue(common.PathDomainStorage, "a"), - }, - BorrowType: borrowType, - CapabilityID: 3, - }, - cadenceValueMigrationEntry{ - StorageKey: interpreter.StorageKey{ - Key: storageDomain, - Address: addressA, - }, - StorageMapKey: aCapStorageMapKey, - Migration: "CapabilityValueMigration", - }, - capabilityMissingCapabilityIDEntry{ - AccountAddress: addressA, - AddressPath: interpreter.AddressPath{ - Address: addressB, - Path: interpreter.NewUnmeteredPathValue(common.PathDomainStorage, "b"), - }, - }, - }, - reporter.entries, - ) - - issueStorageCapConReporter := rwf.reportWriters[issueStorageCapConMigrationReporterName] - require.NotNil(t, issueStorageCapConReporter) - require.Len(t, issueStorageCapConReporter.entries, 6) - require.Equal( - t, - []any{ - storageCapConIssuedEntry{ - AccountAddress: addressB, - AddressPath: interpreter.AddressPath{ - Address: addressB, - Path: interpreter.NewUnmeteredPathValue(common.PathDomainStorage, "a"), - }, - BorrowType: borrowType, - CapabilityID: 3, - }, - storageCapConsMissingBorrowTypeEntry{ - TargetPath: interpreter.AddressPath{ - Address: addressB, - Path: interpreter.NewUnmeteredPathValue(common.PathDomainStorage, "b"), - }, - StoredPath: interpreter.AddressPath{ - Address: addressB, - Path: interpreter.NewUnmeteredPathValue(common.PathDomainStorage, "bCap"), - }, - }, - storageCapConsMissingBorrowTypeEntry{ - TargetPath: interpreter.AddressPath{ - Address: addressB, - Path: interpreter.NewUnmeteredPathValue(common.PathDomainStorage, "c"), - }, - StoredPath: interpreter.AddressPath{ - Address: addressB, - Path: interpreter.NewUnmeteredPathValue(common.PathDomainStorage, "cCap"), - }, - }, - storageCapConsInferredBorrowTypeEntry{ - TargetPath: interpreter.AddressPath{ - Address: addressB, - Path: interpreter.NewUnmeteredPathValue(common.PathDomainStorage, "c"), - }, - BorrowType: inferredBorrowType, - StoredPath: interpreter.AddressPath{ - Address: addressB, - Path: interpreter.NewUnmeteredPathValue(common.PathDomainStorage, "cCap"), - }, - }, - storageCapConIssuedEntry{ - AccountAddress: addressB, - AddressPath: interpreter.AddressPath{ - Address: addressB, - Path: interpreter.NewUnmeteredPathValue(common.PathDomainStorage, "c"), - }, - BorrowType: inferredBorrowType, - CapabilityID: 4, - }, - storageCapConIssuedEntry{ - AccountAddress: addressB, - AddressPath: interpreter.AddressPath{ - Address: addressB, - Path: interpreter.NewUnmeteredPathValue(common.PathDomainStorage, "d"), - }, - BorrowType: dBorrowType, - CapabilityID: 5, - }, - }, - issueStorageCapConReporter.entries, - ) - - // Check account A - - _, err = runScript( - chainID, - registersByAccount, - fmt.Sprintf( - //language=Cadence - ` - access(all) - fun main() { - let storage = getAuthAccount(%s).storage - let aCap = storage.copy(from: /storage/aCap)! - let bCap = storage.copy(from: /storage/bCap)! - let cCap = storage.copy(from: /storage/cCap)! - assert(aCap.id == 3) - assert(bCap.id == 0) - assert(cCap.id == 4) - } - `, - addressA.HexWithPrefix(), - ), - ) - require.NoError(t, err) - - // check the cap with the broken type - - _, err = runScript( - chainID, - registersByAccount, - fmt.Sprintf( - //language=Cadence - ` - access(all) - fun main() { - let storage = getAuthAccount(%s).storage - let dCap = storage.copy(from: /storage/dCap)! - } - `, - addressA.HexWithPrefix(), - ), - ) - require.Error(t, err) - require.ErrorAs(t, &runtime.ParsingCheckingError{}, &err) - - // Check account B - - _, err = runScript( - chainID, - registersByAccount, - fmt.Sprintf( - //language=Cadence - ` - access(all) - fun main() { - let capabilities = getAuthAccount(%s).capabilities.storage - let aCapCons = capabilities.getControllers(forPath: /storage/a) - assert(aCapCons.length == 1) - assert(aCapCons[0].capabilityID == 3) - } - `, - addressB.HexWithPrefix(), - ), - ) - require.NoError(t, err) - -} - -func TestStorageCapConIssuedEntry_MarshalJSON(t *testing.T) { - - t.Parallel() - - e := storageCapConIssuedEntry{ - AccountAddress: common.MustBytesToAddress([]byte{0x2}), - AddressPath: interpreter.AddressPath{ - Address: common.MustBytesToAddress([]byte{0x1}), - Path: interpreter.PathValue{ - Domain: common.PathDomainStorage, - Identifier: "test", - }, - }, - BorrowType: interpreter.NewReferenceStaticType( - nil, - interpreter.UnauthorizedAccess, - interpreter.PrimitiveStaticTypeInt, - ), - CapabilityID: 3, - } - - actual, err := e.MarshalJSON() - require.NoError(t, err) - - require.JSONEq(t, - //language=JSON - `{ - "kind": "storage-capcon-issued", - "account_address": "0x0000000000000002", - "address": "0x0000000000000001", - "path": "/storage/test", - "borrow_type": "&Int", - "capability_id": "3" - }`, - string(actual), - ) -} - -func TestStorageCapConsMissingBorrowTypeEntry_MarshalJSON(t *testing.T) { - - t.Parallel() - - e := storageCapConsMissingBorrowTypeEntry{ - TargetPath: interpreter.AddressPath{ - Address: common.MustBytesToAddress([]byte{0x1}), - Path: interpreter.PathValue{ - Domain: common.PathDomainStorage, - Identifier: "test", - }, - }, - StoredPath: interpreter.AddressPath{ - Address: common.MustBytesToAddress([]byte{0x2}), - Path: interpreter.NewUnmeteredPathValue(common.PathDomainStorage, "storedCap"), - }, - } - - actual, err := e.MarshalJSON() - require.NoError(t, err) - - require.JSONEq(t, - //language=JSON - `{ - "kind": "storage-capcon-missing-borrow-type", - "account_address": "0x0000000000000002", - "address": "0x0000000000000001", - "target_path": "/storage/test", - "stored_path": "/storage/storedCap" - }`, - string(actual), - ) -} - -func TestStorageCapConsInferredBorrowTypeEntry_MarshalJSON(t *testing.T) { - - t.Parallel() - - e := storageCapConsInferredBorrowTypeEntry{ - TargetPath: interpreter.AddressPath{ - Address: common.MustBytesToAddress([]byte{0x1}), - Path: interpreter.PathValue{ - Domain: common.PathDomainStorage, - Identifier: "test", - }, - }, - StoredPath: interpreter.AddressPath{ - Address: common.MustBytesToAddress([]byte{0x2}), - Path: interpreter.NewUnmeteredPathValue(common.PathDomainStorage, "storedCap"), - }, - BorrowType: interpreter.NewReferenceStaticType( - nil, - interpreter.UnauthorizedAccess, - interpreter.PrimitiveStaticTypeInt, - ), - } - - actual, err := e.MarshalJSON() - require.NoError(t, err) - - require.JSONEq(t, - //language=JSON - `{ - "kind": "storage-capcon-inferred-borrow-type", - "account_address": "0x0000000000000002", - "address": "0x0000000000000001", - "borrow_type":"&Int", - "target_path": "/storage/test", - "stored_path": "/storage/storedCap" - }`, - string(actual), - ) -} - -func TestTypeRequirementRemovalMigration(t *testing.T) { - t.Parallel() - - rwf := &testReportWriterFactory{} - - logWriter := &writer{} - logger := zerolog.New(logWriter).Level(zerolog.ErrorLevel) - - const nWorker = 2 - - const chainID = flow.Testnet - - payloads, err := newBootstrapPayloads(chainID) - require.NoError(t, err) - - registersByAccount, err := registers.NewByAccountFromPayloads(payloads) - require.NoError(t, err) - - storedAddress := common.Address(chainID.Chain().ServiceAddress()) - tiblesAddress := mustHexToAddress("e93c412c964bdf40") - - // Store a contract in `addressC`. - - migrationRuntime, err := NewInterpreterMigrationRuntime( - registersByAccount, - chainID, - InterpreterMigrationRuntimeConfig{}, - ) - require.NoError(t, err) - - storage := migrationRuntime.Storage - storageDomain := common.PathDomainStorage.Identifier() - - storageMap := storage.GetStorageMap( - storedAddress, - storageDomain, - true, - ) - - contractName := "TiblesProducer" - - // Store a value with the actual `TiblesProducer.Minter` type - storageMap.WriteValue( - migrationRuntime.Interpreter, - interpreter.StringStorageMapKey("a"), - interpreter.NewTypeValue( - nil, - interpreter.NewCompositeStaticTypeComputeTypeID( - nil, - common.AddressLocation{ - Name: contractName, - Address: tiblesAddress, - }, - "TiblesProducer.Minter", - ), - ), - ) - - // Store a value with a random `TiblesProducer.Minter` type (different address) - storageMap.WriteValue( - migrationRuntime.Interpreter, - interpreter.StringStorageMapKey("b"), - interpreter.NewTypeValue( - nil, - interpreter.NewCompositeStaticTypeComputeTypeID( - nil, - common.AddressLocation{ - Name: contractName, - Address: storedAddress, - }, - "TiblesProducer.Minter", - ), - ), - ) - - // Commit - - err = storage.NondeterministicCommit(migrationRuntime.Interpreter, false) - require.NoError(t, err) - - // finalize the transaction - result, err := migrationRuntime.TransactionState.FinalizeMainTransaction() - require.NoError(t, err) - - // Merge the changes into the registers - - expectedAddresses := map[flow.Address]struct{}{ - flow.Address(storedAddress): {}, - } - - err = registers.ApplyChanges( - registersByAccount, - result.WriteSet, - expectedAddresses, - logger, - ) - require.NoError(t, err) - - // Set contract code - - oldCode := ` - pub contract interface TiblesProducer { - - pub struct ContentLocation {} - pub struct interface IContentLocation {} - - pub resource interface IContent { - access(contract) let contentIdsToPaths: {String: TiblesProducer.ContentLocation} - pub fun getMetadata(contentId: String): {String: AnyStruct}? - } - - pub resource interface IProducer { - access(contract) let minters: @{String: Minter} - } - - pub resource Producer: IContent, IProducer { - access(contract) let minters: @{String: Minter} - } - - pub resource interface IMinter { - pub let id: String - pub var lastMintNumber: UInt32 - pub let contentCapability: Capability - pub fun mintNext() - } - - pub resource Minter: IMinter { - pub let id: String - pub var lastMintNumber: UInt32 - pub let contentCapability: Capability - pub fun mintNext() - } - } - ` - - err = registersByAccount.Set( - string(tiblesAddress[:]), - flow.ContractKey(contractName), - []byte(oldCode), - ) - require.NoError(t, err) - - encodedContractNames, err := environment.EncodeContractNames([]string{contractName}) - require.NoError(t, err) - - err = registersByAccount.Set( - string(tiblesAddress[:]), - flow.ContractNamesKey, - encodedContractNames, - ) - require.NoError(t, err) - - // Migrate - - // TODO: EVM contract is not deployed in snapshot yet, so can't update it - const evmContractChange = EVMContractChangeNone - - const burnerContractChange = BurnerContractChangeUpdate - - migrations := NewCadence1Migrations( - logger, - t.TempDir(), - rwf, - Options{ - NWorker: nWorker, - ChainID: chainID, - EVMContractChange: evmContractChange, - BurnerContractChange: burnerContractChange, - VerboseErrorOutput: true, - }, - ) - - for _, migration := range migrations { - err = migration.Migrate(registersByAccount) - require.NoError( - t, - err, - "migration `%s` failed, logs: %v", - migration.Name, - logWriter.logs, - ) - } - - // Check reporters - - reporter := rwf.reportWriters[typeRequirementExtractingReporterName] - require.NotNil(t, reporter) - require.Len(t, reporter.entries, 3) - - assert.Equal( - t, - []any{ - typeRequirementRemovalEntry{ - TypeRequirement{ - Address: tiblesAddress, - ContractName: contractName, - TypeName: "ContentLocation", - }, - }, - typeRequirementRemovalEntry{ - TypeRequirement{ - Address: tiblesAddress, - ContractName: contractName, - TypeName: "Producer", - }, - }, - typeRequirementRemovalEntry{ - TypeRequirement{ - Address: tiblesAddress, - ContractName: contractName, - TypeName: "Minter", - }, - }, - }, - reporter.entries, - ) - - // Check account - - _, err = runScript( - chainID, - registersByAccount, - fmt.Sprintf( - //language=Cadence - ` - access(all) - fun main() { - let storage = getAuthAccount(%s).storage - assert(storage.copy(from: /storage/a)!.identifier == "{A.%s.TiblesProducer.Minter}") - assert(storage.copy(from: /storage/b)!.identifier == "A.%s.TiblesProducer.Minter") - } - `, - storedAddress.HexWithPrefix(), - tiblesAddress.Hex(), - storedAddress.Hex(), - ), - ) - require.NoError(t, err) -} - -func runScript(chainID flow.ChainID, registersByAccount *registers.ByAccount, script string) (cadence.Value, error) { - options := computation.DefaultFVMOptions(chainID, false, false) - options = append(options, - fvm.WithContractDeploymentRestricted(false), - fvm.WithContractRemovalRestricted(false), - fvm.WithAuthorizationChecksEnabled(false), - fvm.WithSequenceNumberCheckAndIncrementEnabled(false), - fvm.WithTransactionFeesEnabled(false)) - ctx := fvm.NewContext(options...) - - storageSnapshot := registers.StorageSnapshot{ - Registers: registersByAccount, - } - - vm := fvm.NewVirtualMachine() - - _, res, err := vm.Run( - ctx, - fvm.Script([]byte(script)), - storageSnapshot, - ) - if err != nil { - return nil, fmt.Errorf("failed to run transaction: %w", err) - } - - if res.Err != nil { - return nil, fmt.Errorf("transaction failed: %w", res.Err) - } - - return res.Value, nil -} - -func TestStorageCapsMigrationDeterminism(t *testing.T) { - t.Parallel() - - const nWorker = 4 - - const chainID = flow.Emulator - - addressA := common.Address(chainID.Chain().ServiceAddress()) - - addressB, err := common.HexToAddress("0xe5a8b7f23e8b548f") - require.NoError(t, err) - - rwf := &testReportWriterFactory{} - - logWriter := &writer{} - logger := zerolog.New(logWriter).Level(zerolog.ErrorLevel) - - // Store values. - - payloads, err := newBootstrapPayloads(chainID) - require.NoError(t, err) - - registersByAccount, err := registers.NewByAccountFromPayloads(payloads) - require.NoError(t, err) - - migrationRuntime, err := NewInterpreterMigrationRuntime( - registersByAccount, - chainID, - InterpreterMigrationRuntimeConfig{}, - ) - require.NoError(t, err) - - storage := migrationRuntime.Storage - storageDomain := common.PathDomainStorage.Identifier() - - // First, create a capability with storage path, issued for account A, - // and store it in account A. - - borrowType := interpreter.NewReferenceStaticType( - nil, - interpreter.UnauthorizedAccess, - interpreter.PrimitiveStaticTypeAnyStruct, - ) - - aCapability := interpreter.NewUnmeteredPathCapabilityValue( - borrowType, - // Important: Capability must be for address A. - interpreter.AddressValue(addressA), - interpreter.NewUnmeteredPathValue(common.PathDomainStorage, "a"), - ) - - storageMapForAddressA := storage.GetStorageMap( - addressA, - storageDomain, - true, - ) - - aCapStorageMapKey := interpreter.StringStorageMapKey("aCap") - - storageMapForAddressA.WriteValue( - migrationRuntime.Interpreter, - aCapStorageMapKey, - aCapability, - ) - - // Then, create a capability with storage path, issued for account A, - // and store it in account B. - - bCapability := interpreter.NewUnmeteredPathCapabilityValue( - borrowType, - // Important: Capability must be for address A. - interpreter.AddressValue(addressA), - interpreter.NewUnmeteredPathValue(common.PathDomainStorage, "b"), - ) - - storageMapForAddressB := storage.GetStorageMap( - addressB, - storageDomain, - true, - ) - - bCapStorageMapKey := interpreter.StringStorageMapKey("bCap") - - storageMapForAddressB.WriteValue( - migrationRuntime.Interpreter, - bCapStorageMapKey, - bCapability, - ) - - // Commit - - err = storage.NondeterministicCommit(migrationRuntime.Interpreter, false) - require.NoError(t, err) - - // finalize the transaction - result, err := migrationRuntime.TransactionState.FinalizeMainTransaction() - require.NoError(t, err) - - // Merge the changes into the registers - - expectedAddresses := map[flow.Address]struct{}{ - flow.Address(addressA): {}, - flow.Address(addressB): {}, - } - - err = registers.ApplyChanges( - registersByAccount, - result.WriteSet, - expectedAddresses, - logger, - ) - require.NoError(t, err) - - // Migrate - - // TODO: EVM contract is not deployed in snapshot yet, so can't update it - const evmContractChange = EVMContractChangeNone - - const burnerContractChange = BurnerContractChangeUpdate - - migrations := NewCadence1Migrations( - logger, - t.TempDir(), - rwf, - Options{ - NWorker: nWorker, - ChainID: chainID, - EVMContractChange: evmContractChange, - BurnerContractChange: burnerContractChange, - VerboseErrorOutput: true, - }, - ) - - for _, migration := range migrations { - err = migration.Migrate(registersByAccount) - require.NoError( - t, - err, - "migration `%s` failed, logs: %v", - migration.Name, - logWriter.logs, - ) - } - - reporter := rwf.reportWriters[capabilityValueMigrationReporterName] - require.NotNil(t, reporter) - require.Len(t, reporter.entries, 4) - - issueStorageCapConReporter := rwf.reportWriters[issueStorageCapConMigrationReporterName] - require.NotNil(t, issueStorageCapConReporter) - require.Len(t, issueStorageCapConReporter.entries, 2) - require.Equal( - t, - []any{ - storageCapConIssuedEntry{ - AccountAddress: addressA, - AddressPath: interpreter.AddressPath{ - Address: addressA, - Path: interpreter.NewUnmeteredPathValue(common.PathDomainStorage, "a"), - }, - BorrowType: borrowType, - CapabilityID: 6, - }, - storageCapConIssuedEntry{ - AccountAddress: addressA, - AddressPath: interpreter.AddressPath{ - Address: addressA, - Path: interpreter.NewUnmeteredPathValue(common.PathDomainStorage, "b"), - }, - BorrowType: borrowType, - CapabilityID: 7, - }, - }, - issueStorageCapConReporter.entries, - ) -} diff --git a/cmd/util/ledger/migrations/change_contract_code_migration.go b/cmd/util/ledger/migrations/change_contract_code_migration.go deleted file mode 100644 index 6deefd278fd..00000000000 --- a/cmd/util/ledger/migrations/change_contract_code_migration.go +++ /dev/null @@ -1,309 +0,0 @@ -package migrations - -import ( - "fmt" - - "github.com/onflow/cadence/runtime/common" - coreContracts "github.com/onflow/flow-core-contracts/lib/go/contracts" - nftStorefrontContracts "github.com/onflow/nft-storefront/lib/go/contracts" - "github.com/rs/zerolog" - - "github.com/onflow/flow-go/cmd/util/ledger/reporters" - evm "github.com/onflow/flow-go/fvm/evm/stdlib" - "github.com/onflow/flow-go/fvm/systemcontracts" - "github.com/onflow/flow-go/model/flow" -) - -func NewSystemContractChange( - systemContract systemcontracts.SystemContract, - newContractCode []byte, -) StagedContract { - return StagedContract{ - Address: common.Address(systemContract.Address), - Contract: Contract{ - Name: systemContract.Name, - Code: newContractCode, - }, - } -} - -type EVMContractChange uint8 - -const ( - EVMContractChangeNone EVMContractChange = iota - // EVMContractChangeDeployFull deploys the full EVM contract - EVMContractChangeDeployFull - // EVMContractChangeUpdateFull updates the existing EVM contract to the latest, full EVM contract - EVMContractChangeUpdateFull - // EVMContractChangeDeployMinimalAndUpdateFull deploys the minimal EVM contract - // and updates it to the latest, full EVM contract - EVMContractChangeDeployMinimalAndUpdateFull -) - -type BurnerContractChange uint8 - -const ( - BurnerContractChangeNone BurnerContractChange = iota - BurnerContractChangeDeploy - BurnerContractChangeUpdate -) - -func BurnerAddressForChain(chainID flow.ChainID) flow.Address { - - systemContracts := systemcontracts.SystemContractsForChain(chainID) - serviceAccountAddress := systemContracts.FlowServiceAccount.Address - fungibleTokenAddress := systemContracts.FungibleToken.Address - - switch chainID { - case flow.Mainnet, flow.Testnet: - return fungibleTokenAddress - - case flow.Emulator, flow.Localnet: - return serviceAccountAddress - - default: - panic(fmt.Errorf("unsupported chain ID: %s", chainID)) - } -} - -func SystemContractChanges(chainID flow.ChainID, options SystemContractsMigrationOptions) []StagedContract { - systemContracts := systemcontracts.SystemContractsForChain(chainID) - - env := systemContracts.AsTemplateEnv() - env.BurnerAddress = BurnerAddressForChain(chainID).Hex() - - switch chainID { - case flow.Mainnet: - env.StakingCollectionAddress = "0x8d0e87b65159ae63" - env.StakingProxyAddress = "0x62430cf28c26d095" - - case flow.Testnet: - env.StakingCollectionAddress = "0x95e019a17d0e23d7" - env.StakingProxyAddress = "0x7aad92e5a0715d21" - - case flow.Emulator, flow.Localnet: - env.StakingCollectionAddress = env.ServiceAccountAddress - env.StakingProxyAddress = env.ServiceAccountAddress - - default: - panic(fmt.Errorf("unsupported chain ID: %s", chainID)) - } - - env.LockedTokensAddress = env.StakingCollectionAddress - - contractChanges := []StagedContract{ - // epoch related contracts - NewSystemContractChange( - systemContracts.Epoch, - coreContracts.FlowEpoch( - env, - ), - ), - NewSystemContractChange( - systemContracts.IDTableStaking, - coreContracts.FlowIDTableStaking( - env, - ), - ), - NewSystemContractChange( - systemContracts.ClusterQC, - coreContracts.FlowQC(), - ), - NewSystemContractChange( - systemContracts.DKG, - coreContracts.FlowDKG(), - ), - - // service account related contracts - NewSystemContractChange( - systemContracts.FlowServiceAccount, - coreContracts.FlowServiceAccount( - env, - ), - ), - NewSystemContractChange( - systemContracts.NodeVersionBeacon, - coreContracts.NodeVersionBeacon(), - ), - NewSystemContractChange( - systemContracts.RandomBeaconHistory, - coreContracts.RandomBeaconHistory(), - ), - NewSystemContractChange( - systemContracts.FlowStorageFees, - coreContracts.FlowStorageFees( - env, - ), - ), - { - Address: common.Address(flow.HexToAddress(env.StakingCollectionAddress)), - Contract: Contract{ - Name: "FlowStakingCollection", - Code: coreContracts.FlowStakingCollection(env), - }, - }, - { - Address: common.Address(flow.HexToAddress(env.StakingProxyAddress)), - Contract: Contract{ - Name: "StakingProxy", - Code: coreContracts.FlowStakingProxy(), - }, - }, - { - Address: common.Address(flow.HexToAddress(env.LockedTokensAddress)), - Contract: Contract{ - Name: "LockedTokens", - Code: coreContracts.FlowLockedTokens(env), - }, - }, - - // token related contracts - NewSystemContractChange( - systemContracts.FlowFees, - coreContracts.FlowFees( - env, - ), - ), - NewSystemContractChange( - systemContracts.FlowToken, - coreContracts.FlowToken( - env, - ), - ), - NewSystemContractChange( - systemContracts.FungibleToken, - coreContracts.FungibleToken( - env, - ), - ), - { - Address: common.Address(flow.HexToAddress(env.FungibleTokenMetadataViewsAddress)), - Contract: Contract{ - Name: "FungibleTokenMetadataViews", - Code: coreContracts.FungibleTokenMetadataViews(env), - }, - }, - - // NFT related contracts - NewSystemContractChange( - systemContracts.NonFungibleToken, - coreContracts.NonFungibleToken( - env, - ), - ), - NewSystemContractChange( - systemContracts.MetadataViews, - coreContracts.MetadataViews( - env, - ), - ), - NewSystemContractChange( - systemContracts.ViewResolver, - coreContracts.ViewResolver(), - ), - } - - switch chainID { - case flow.Emulator, flow.Localnet: - // skip - - default: - contractChanges = append( - contractChanges, - StagedContract{ - Address: common.Address(flow.HexToAddress(env.FungibleTokenSwitchboardAddress)), - Contract: Contract{ - Name: "FungibleTokenSwitchboard", - Code: coreContracts.FungibleTokenSwitchboard(env), - }, - }, - ) - } - - // EVM contract - switch options.EVM { - case EVMContractChangeNone: - // NO-OP - - case EVMContractChangeDeployFull: - // handled in migration pipeline (NewCadence1ContractsMigrations) - - case EVMContractChangeUpdateFull, - EVMContractChangeDeployMinimalAndUpdateFull: - - contractChanges = append( - contractChanges, - NewSystemContractChange( - systemContracts.EVMContract, - evm.ContractCode( - systemContracts.NonFungibleToken.Address, - systemContracts.FungibleToken.Address, - systemContracts.FlowToken.Address, - ), - ), - ) - } - - // Burner contract - if options.Burner == BurnerContractChangeUpdate { - contractChanges = append( - contractChanges, - StagedContract{ - Address: common.Address(flow.HexToAddress(env.BurnerAddress)), - Contract: Contract{ - Name: "Burner", - Code: coreContracts.Burner(), - }, - }, - ) - } - - if chainID == flow.Testnet { - contractChanges = append( - contractChanges, - StagedContract{ - Address: common.Address(flow.HexToAddress("0x2d55b98eb200daef")), - Contract: Contract{ - Name: "NFTStorefrontV2", - Code: nftStorefrontContracts.NFTStorefrontV2( - systemContracts.FungibleToken.Address.Hex(), - systemContracts.NonFungibleToken.Address.Hex(), - ), - }, - }, - ) - } - - return contractChanges -} - -type SystemContractsMigrationOptions struct { - StagedContractsMigrationOptions - EVM EVMContractChange - Burner BurnerContractChange -} - -func NewSystemContractsMigration( - log zerolog.Logger, - rwf reporters.ReportWriterFactory, - locations map[common.AddressLocation]struct{}, - options SystemContractsMigrationOptions, -) ( - migration *StagedContractsMigration, -) { - migration = NewStagedContractsMigration( - "SystemContractsMigration", - "system-contracts-migration", - log, - rwf, - &LegacyTypeRequirements{}, // This is empty for system contracts - options.StagedContractsMigrationOptions, - ) - - for _, change := range SystemContractChanges(options.ChainID, options) { - migration.registerContractChange(change) - locations[change.AddressLocation()] = struct{}{} - } - - return migration -} diff --git a/cmd/util/ledger/migrations/change_contract_code_migration_test.go b/cmd/util/ledger/migrations/change_contract_code_migration_test.go deleted file mode 100644 index e9bf4c7d0bd..00000000000 --- a/cmd/util/ledger/migrations/change_contract_code_migration_test.go +++ /dev/null @@ -1,737 +0,0 @@ -package migrations - -import ( - "context" - "io" - "strings" - "testing" - - "github.com/onflow/cadence/runtime/common" - "github.com/rs/zerolog" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/onflow/flow-go/cmd/util/ledger/util/registers" - "github.com/onflow/flow-go/model/flow" -) - -const contractA = ` -access(all) contract A { - access(all) fun foo() {} -}` - -const updatedContractA = ` -access(all) contract A { - access(all) fun bar() {} -}` - -const contractB = ` -access(all) contract B { - access(all) fun foo() {} -}` - -const updatedContractB = ` -access(all) contract B { - access(all) fun bar() {} -}` - -type errorLogWriter struct { - logs []string -} - -var _ io.Writer = &errorLogWriter{} - -const errorLogPrefix = "{\"level\":\"error\"" - -func (l *errorLogWriter) Write(bytes []byte) (int, error) { - logStr := string(bytes) - - // Ignore non-error logs - if !strings.HasPrefix(logStr, errorLogPrefix) { - return 0, nil - } - - l.logs = append(l.logs, logStr) - return len(bytes), nil -} - -func TestChangeContractCodeMigration(t *testing.T) { - t.Parallel() - - const chainID = flow.Emulator - addressGenerator := chainID.Chain().NewAddressGenerator() - - address1, err := addressGenerator.NextAddress() - require.NoError(t, err) - - address2, err := addressGenerator.NextAddress() - require.NoError(t, err) - - ctx := context.Background() - - t.Run("no contracts", func(t *testing.T) { - t.Parallel() - - writer := &errorLogWriter{} - log := zerolog.New(writer) - - rwf := &testReportWriterFactory{} - - options := StagedContractsMigrationOptions{ - ChainID: flow.Emulator, - VerboseErrorOutput: true, - } - migration := NewStagedContractsMigration( - "test", - "test", - log, - rwf, - &LegacyTypeRequirements{}, - options, - ) - - registersByAccount := registers.NewByAccount() - - err := migration.InitMigration(log, registersByAccount, 1) - require.NoError(t, err) - - err = migration.MigrateAccount( - ctx, - common.Address(address1), - registersByAccount.AccountRegisters(string(address1[:])), - ) - require.NoError(t, err) - - err = migration.Close() - require.NoError(t, err) - - require.Empty(t, writer.logs) - }) - - t.Run("1 contract - dont migrate", func(t *testing.T) { - t.Parallel() - - writer := &errorLogWriter{} - log := zerolog.New(writer) - - rwf := &testReportWriterFactory{} - - options := StagedContractsMigrationOptions{ - ChainID: flow.Emulator, - VerboseErrorOutput: true, - } - migration := NewStagedContractsMigration( - "test", - "test", - log, - rwf, - &LegacyTypeRequirements{}, - options, - ) - - registersByAccount, err := registersForStagedContracts( - StagedContract{ - Address: common.Address(address1), - Contract: Contract{ - Name: "A", - Code: []byte(contractA), - }, - }, - ) - require.NoError(t, err) - - err = migration.InitMigration(log, registersByAccount, 1) - require.NoError(t, err) - - owner1 := string(address1[:]) - - accountRegisters1 := registersByAccount.AccountRegisters(owner1) - - err = migration.MigrateAccount( - ctx, - common.Address(address1), - accountRegisters1, - ) - require.NoError(t, err) - - require.Equal(t, 1, registersByAccount.AccountCount()) - require.Equal(t, 1, accountRegisters1.Count()) - require.Equal(t, - contractA, - contractCode(t, registersByAccount, owner1, "A"), - ) - - err = migration.Close() - require.NoError(t, err) - - require.Empty(t, writer.logs) - }) - - t.Run("1 contract - migrate", func(t *testing.T) { - t.Parallel() - - writer := &errorLogWriter{} - log := zerolog.New(writer) - - rwf := &testReportWriterFactory{} - - options := StagedContractsMigrationOptions{ - ChainID: flow.Emulator, - VerboseErrorOutput: true, - } - migration := NewStagedContractsMigration( - "test", - "test", - log, - rwf, - &LegacyTypeRequirements{}, - options, - ). - WithStagedContractUpdates([]StagedContract{ - { - Address: common.Address(address1), - Contract: Contract{ - Name: "A", - Code: []byte(updatedContractA), - }, - }, - }) - - registersByAccount, err := registersForStagedContracts( - StagedContract{ - Address: common.Address(address1), - Contract: Contract{ - Name: "A", - Code: []byte(contractA), - }, - }, - ) - require.NoError(t, err) - - err = migration.InitMigration(log, registersByAccount, 1) - require.NoError(t, err) - - owner1 := string(address1[:]) - - accountRegisters1 := registersByAccount.AccountRegisters(owner1) - - err = migration.MigrateAccount( - ctx, - common.Address(address1), - accountRegisters1, - ) - require.NoError(t, err) - - require.Equal(t, 1, registersByAccount.AccountCount()) - require.Equal(t, 1, accountRegisters1.Count()) - require.Equal(t, - updatedContractA, - contractCode(t, registersByAccount, owner1, "A"), - ) - - err = migration.Close() - require.NoError(t, err) - - require.Empty(t, writer.logs) - }) - - t.Run("2 contracts - migrate 1", func(t *testing.T) { - t.Parallel() - - writer := &errorLogWriter{} - log := zerolog.New(writer) - - rwf := &testReportWriterFactory{} - - options := StagedContractsMigrationOptions{ - ChainID: flow.Emulator, - VerboseErrorOutput: true, - } - migration := NewStagedContractsMigration( - "test", - "test", - log, - rwf, - &LegacyTypeRequirements{}, - options, - ). - WithStagedContractUpdates([]StagedContract{ - { - Address: common.Address(address1), - Contract: Contract{ - Name: "A", - Code: []byte(updatedContractA), - }, - }, - }) - - registersByAccount, err := registersForStagedContracts( - StagedContract{ - Address: common.Address(address1), - Contract: Contract{ - Name: "A", - Code: []byte(contractA), - }, - }, - StagedContract{ - Address: common.Address(address1), - Contract: Contract{ - Name: "B", - Code: []byte(contractB), - }, - }, - ) - require.NoError(t, err) - - err = migration.InitMigration(log, registersByAccount, 1) - require.NoError(t, err) - - owner1 := string(address1[:]) - - accountRegisters1 := registersByAccount.AccountRegisters(owner1) - - err = migration.MigrateAccount( - ctx, - common.Address(address1), - accountRegisters1, - ) - require.NoError(t, err) - - require.Equal(t, 1, registersByAccount.AccountCount()) - require.Equal(t, 2, accountRegisters1.Count()) - require.Equal(t, - updatedContractA, - contractCode(t, registersByAccount, owner1, "A"), - ) - require.Equal(t, - contractB, - contractCode(t, registersByAccount, owner1, "B"), - ) - - err = migration.Close() - require.NoError(t, err) - - require.Empty(t, writer.logs) - }) - - t.Run("2 contracts - migrate 2", func(t *testing.T) { - t.Parallel() - - writer := &errorLogWriter{} - log := zerolog.New(writer) - - rwf := &testReportWriterFactory{} - - options := StagedContractsMigrationOptions{ - ChainID: flow.Emulator, - VerboseErrorOutput: true, - } - migration := NewStagedContractsMigration( - "test", - "test", - log, - rwf, - &LegacyTypeRequirements{}, - options, - ). - WithStagedContractUpdates([]StagedContract{ - { - Address: common.Address(address1), - Contract: Contract{ - Name: "A", - Code: []byte(updatedContractA), - }, - }, - { - Address: common.Address(address1), - Contract: Contract{ - Name: "B", - Code: []byte(updatedContractB), - }, - }, - }) - - registersByAccount, err := registersForStagedContracts( - StagedContract{ - Address: common.Address(address1), - Contract: Contract{ - Name: "A", - Code: []byte(contractA), - }, - }, - StagedContract{ - Address: common.Address(address1), - Contract: Contract{ - Name: "B", - Code: []byte(contractB), - }, - }, - ) - require.NoError(t, err) - - err = migration.InitMigration(log, registersByAccount, 1) - require.NoError(t, err) - - owner1 := string(address1[:]) - - accountRegisters1 := registersByAccount.AccountRegisters(owner1) - - err = migration.MigrateAccount( - ctx, - common.Address(address1), - accountRegisters1, - ) - require.NoError(t, err) - - require.Equal(t, 1, registersByAccount.AccountCount()) - require.Equal(t, 2, accountRegisters1.Count()) - require.Equal(t, - updatedContractA, - contractCode(t, registersByAccount, owner1, "A"), - ) - require.Equal(t, - updatedContractB, - contractCode(t, registersByAccount, owner1, "B"), - ) - - err = migration.Close() - require.NoError(t, err) - - require.Empty(t, writer.logs) - }) - - t.Run("2 contracts on different accounts - migrate 1", func(t *testing.T) { - t.Parallel() - - writer := &errorLogWriter{} - log := zerolog.New(writer) - - rwf := &testReportWriterFactory{} - - options := StagedContractsMigrationOptions{ - ChainID: flow.Emulator, - VerboseErrorOutput: true, - } - migration := NewStagedContractsMigration( - "test", - "test", - log, - rwf, - &LegacyTypeRequirements{}, - options, - ). - WithStagedContractUpdates([]StagedContract{ - { - Address: common.Address(address1), - Contract: Contract{ - Name: "A", - Code: []byte(updatedContractA), - }, - }, - }) - - registersByAccount, err := registersForStagedContracts( - StagedContract{ - Address: common.Address(address1), - Contract: Contract{ - Name: "A", - Code: []byte(contractA), - }, - }, - StagedContract{ - Address: common.Address(address2), - Contract: Contract{ - Name: "A", - Code: []byte(contractA), - }, - }, - ) - require.NoError(t, err) - - err = migration.InitMigration(log, registersByAccount, 1) - require.NoError(t, err) - - owner1 := string(address1[:]) - owner2 := string(address2[:]) - - accountRegisters1 := registersByAccount.AccountRegisters(owner1) - accountRegisters2 := registersByAccount.AccountRegisters(owner2) - - err = migration.MigrateAccount( - ctx, - common.Address(address1), - accountRegisters1, - ) - require.NoError(t, err) - - require.Equal(t, 2, registersByAccount.AccountCount()) - require.Equal(t, 1, accountRegisters1.Count()) - require.Equal(t, 1, accountRegisters2.Count()) - require.Equal(t, - updatedContractA, - contractCode(t, registersByAccount, owner1, "A"), - ) - require.Equal(t, - contractA, - contractCode(t, registersByAccount, owner2, "A"), - ) - - err = migration.Close() - require.NoError(t, err) - - require.Empty(t, writer.logs) - }) - - t.Run("not all contracts on one account migrated", func(t *testing.T) { - t.Parallel() - - writer := &errorLogWriter{} - log := zerolog.New(writer) - - rwf := &testReportWriterFactory{} - - options := StagedContractsMigrationOptions{ - ChainID: flow.Emulator, - VerboseErrorOutput: true, - } - migration := NewStagedContractsMigration( - "test", - "test", - log, - rwf, - &LegacyTypeRequirements{}, - options, - ). - WithStagedContractUpdates([]StagedContract{ - { - Address: common.Address(address1), - Contract: Contract{ - Name: "A", - Code: []byte(updatedContractA), - }, - }, - { - Address: common.Address(address1), - Contract: Contract{ - Name: "B", - Code: []byte(updatedContractB), - }, - }, - }) - - registersByAccount, err := registersForStagedContracts( - StagedContract{ - Address: common.Address(address1), - Contract: Contract{ - Name: "A", - Code: []byte(contractA), - }, - }, - ) - require.NoError(t, err) - - err = migration.InitMigration(log, registersByAccount, 1) - require.NoError(t, err) - - owner1 := string(address1[:]) - - accountRegisters1 := registersByAccount.AccountRegisters(owner1) - - err = migration.MigrateAccount( - ctx, - common.Address(address1), - accountRegisters1, - ) - require.NoError(t, err) - - require.Len(t, writer.logs, 1) - assert.Contains(t, - writer.logs[0], - `missing old code`, - ) - }) - - t.Run("not all accounts migrated", func(t *testing.T) { - t.Parallel() - - writer := &errorLogWriter{} - log := zerolog.New(writer) - - rwf := &testReportWriterFactory{} - - options := StagedContractsMigrationOptions{ - ChainID: flow.Emulator, - VerboseErrorOutput: true, - } - migration := NewStagedContractsMigration( - "test", - "test", - log, - rwf, - &LegacyTypeRequirements{}, - options, - ). - WithStagedContractUpdates([]StagedContract{ - { - Address: common.Address(address2), - Contract: Contract{ - Name: "A", - Code: []byte(updatedContractA), - }, - }, - }) - - registersByAccount, err := registersForStagedContracts( - StagedContract{ - Address: common.Address(address1), - Contract: Contract{ - Name: "A", - Code: []byte(contractA), - }, - }, - ) - require.NoError(t, err) - - err = migration.InitMigration(log, registersByAccount, 1) - require.NoError(t, err) - - owner1 := string(address1[:]) - - accountRegisters1 := registersByAccount.AccountRegisters(owner1) - - err = migration.MigrateAccount( - ctx, - common.Address(address1), - accountRegisters1, - ) - require.NoError(t, err) - - err = migration.Close() - require.NoError(t, err) - - require.Len(t, writer.logs, 1) - assert.Contains(t, - writer.logs[0], - `"failed to find all contract registers that need to be changed"`, - ) - }) -} - -func TestSystemContractChanges(t *testing.T) { - t.Parallel() - - const chainID = flow.Mainnet - - changes := SystemContractChanges( - chainID, - SystemContractsMigrationOptions{ - StagedContractsMigrationOptions: StagedContractsMigrationOptions{ - ChainID: chainID, - }, - EVM: EVMContractChangeUpdateFull, - Burner: BurnerContractChangeDeploy, - }, - ) - - var changeLocations []common.AddressLocation - - for _, change := range changes { - location := change.AddressLocation() - changeLocations = append(changeLocations, location) - } - - address1 := common.Address{0x86, 0x24, 0xb5, 0x2f, 0x9d, 0xdc, 0xd0, 0x4a} - address2 := common.Address{0xe4, 0x67, 0xb9, 0xdd, 0x11, 0xfa, 0x00, 0xdf} - address3 := common.Address{0x8d, 0x0e, 0x87, 0xb6, 0x51, 0x59, 0xae, 0x63} - address4 := common.Address{0x62, 0x43, 0x0c, 0xf2, 0x8c, 0x26, 0xd0, 0x95} - address5 := common.Address{0xf9, 0x19, 0xee, 0x77, 0x44, 0x7b, 0x74, 0x97} - address6 := common.Address{0x16, 0x54, 0x65, 0x33, 0x99, 0x04, 0x0a, 0x61} - address7 := common.Address{0xf2, 0x33, 0xdc, 0xee, 0x88, 0xfe, 0x0a, 0xbe} - address8 := common.Address{0x1d, 0x7e, 0x57, 0xaa, 0x55, 0x81, 0x74, 0x48} - - assert.Equal(t, - []common.AddressLocation{ - { - Name: "FlowEpoch", - Address: address1, - }, - { - Name: "FlowIDTableStaking", - Address: address1, - }, - { - Name: "FlowClusterQC", - Address: address1, - }, - { - Name: "FlowDKG", - Address: address1, - }, - { - Name: "FlowServiceAccount", - Address: address2, - }, - { - Name: "NodeVersionBeacon", - Address: address2, - }, - { - Name: "RandomBeaconHistory", - Address: address2, - }, - { - Name: "FlowStorageFees", - Address: address2, - }, - { - Name: "FlowStakingCollection", - Address: address3, - }, - { - Name: "StakingProxy", - Address: address4, - }, - { - Name: "LockedTokens", - Address: address3, - }, - { - Name: "FlowFees", - Address: address5, - }, - { - Name: "FlowToken", - Address: address6, - }, - { - Name: "FungibleToken", - Address: address7, - }, - { - Name: "FungibleTokenMetadataViews", - Address: address7, - }, - { - Name: "NonFungibleToken", - Address: address8, - }, - { - Name: "MetadataViews", - Address: address8, - }, - { - Name: "ViewResolver", - Address: address8, - }, - { - Name: "FungibleTokenSwitchboard", - Address: address7, - }, - { - Name: "EVM", - Address: address2, - }, - }, - changeLocations, - ) -} diff --git a/cmd/util/ledger/migrations/contract_checking_migration.go b/cmd/util/ledger/migrations/contract_checking_migration.go deleted file mode 100644 index bffc05c9dfd..00000000000 --- a/cmd/util/ledger/migrations/contract_checking_migration.go +++ /dev/null @@ -1,259 +0,0 @@ -package migrations - -import ( - "bytes" - "encoding/json" - "fmt" - "sort" - "strings" - - "github.com/onflow/cadence/runtime/common" - "github.com/onflow/cadence/runtime/interpreter" - "github.com/onflow/cadence/runtime/pretty" - "github.com/rs/zerolog" - - "github.com/onflow/flow-go/cmd/util/ledger/reporters" - "github.com/onflow/flow-go/cmd/util/ledger/util/registers" - "github.com/onflow/flow-go/fvm/environment" - "github.com/onflow/flow-go/model/flow" -) - -const contractCheckingReporterName = "contract-checking" -const contractCountEstimate = 1000 - -type AddressContract struct { - Location common.AddressLocation - Code []byte -} - -// NewContractCheckingMigration returns a migration that checks all contracts. -// It parses and checks all contract code and stores the programs in the provided map. -// Important locations is a set of locations that should always succeed to check. -func NewContractCheckingMigration( - log zerolog.Logger, - rwf reporters.ReportWriterFactory, - chainID flow.ChainID, - verboseErrorOutput bool, - importantLocations map[common.AddressLocation]struct{}, - programs map[common.Location]*interpreter.Program, -) RegistersMigration { - return func(registersByAccount *registers.ByAccount) error { - - reporter := rwf.ReportWriter(contractCheckingReporterName) - defer reporter.Close() - - mr, err := NewInterpreterMigrationRuntime( - registersByAccount, - chainID, - InterpreterMigrationRuntimeConfig{}, - ) - if err != nil { - return fmt.Errorf("failed to create interpreter migration runtime: %w", err) - } - - contracts, err := gatherContractsFromRegisters(registersByAccount, log) - if err != nil { - return err - } - - contractsForPrettyPrinting := make(map[common.Location][]byte, len(contracts)) - for _, contract := range contracts { - contractsForPrettyPrinting[contract.Location] = contract.Code - } - - // Check all contracts - - for _, contract := range contracts { - checkContract( - contract, - log, - mr, - contractsForPrettyPrinting, - verboseErrorOutput, - reporter, - importantLocations, - programs, - ) - } - - return nil - } -} - -func gatherContractsFromRegisters(registersByAccount *registers.ByAccount, log zerolog.Logger) ([]AddressContract, error) { - log.Info().Msg("Gathering contracts ...") - - contracts := make([]AddressContract, 0, contractCountEstimate) - - err := registersByAccount.ForEachAccount(func(accountRegisters *registers.AccountRegisters) error { - owner := accountRegisters.Owner() - - encodedContractNames, err := accountRegisters.Get(owner, flow.ContractNamesKey) - if err != nil { - return err - } - - contractNames, err := environment.DecodeContractNames(encodedContractNames) - if err != nil { - return err - } - - for _, contractName := range contractNames { - - contractKey := flow.ContractKey(contractName) - - code, err := accountRegisters.Get(owner, contractKey) - if err != nil { - return err - } - - if len(bytes.TrimSpace(code)) == 0 { - continue - } - - address := common.Address([]byte(owner)) - location := common.AddressLocation{ - Address: address, - Name: contractName, - } - - contracts = append( - contracts, - AddressContract{ - Location: location, - Code: code, - }, - ) - } - - return nil - }) - if err != nil { - return nil, fmt.Errorf("failed to get contracts of accounts: %w", err) - } - - sort.Slice(contracts, func(i, j int) bool { - a := contracts[i] - b := contracts[j] - return a.Location.ID() < b.Location.ID() - }) - - log.Info().Msgf("Gathered all contracts (%d)", len(contracts)) - return contracts, nil -} - -func checkContract( - contract AddressContract, - log zerolog.Logger, - mr *InterpreterMigrationRuntime, - contractsForPrettyPrinting map[common.Location][]byte, - verboseErrorOutput bool, - reporter reporters.ReportWriter, - importantLocations map[common.AddressLocation]struct{}, - programs map[common.Location]*interpreter.Program, -) { - location := contract.Location - code := contract.Code - - log.Info().Msgf("checking contract %s ...", location) - - // Check contract code - const getAndSetProgram = true - program, err := mr.ContractAdditionHandler.ParseAndCheckProgram(code, location, getAndSetProgram) - if err != nil { - - // Pretty print the error - var builder strings.Builder - errorPrinter := pretty.NewErrorPrettyPrinter(&builder, false) - - printErr := errorPrinter.PrettyPrintError(err, location, contractsForPrettyPrinting) - - var errorDetails string - if printErr == nil { - errorDetails = builder.String() - } else { - errorDetails = err.Error() - } - - if _, ok := importantLocations[location]; ok { - log.Error().Msgf( - "error checking important contract %s: %s", - location, - errorDetails, - ) - } else if verboseErrorOutput { - log.Error().Msgf( - "error checking contract %s: %s", - location, - errorDetails, - ) - } - - reporter.Write(contractCheckingFailure{ - AccountAddress: location.Address, - ContractName: location.Name, - Code: string(code), - Error: errorDetails, - }) - - return - } - - // Record the checked program for future use - programs[location] = program - - reporter.Write(contractCheckingSuccess{ - AccountAddress: location.Address, - ContractName: location.Name, - Code: string(code), - }) - - log.Info().Msgf("finished checking contract %s", location) -} - -type contractCheckingFailure struct { - AccountAddress common.Address - ContractName string - Code string - Error string -} - -var _ json.Marshaler = contractCheckingFailure{} - -func (e contractCheckingFailure) MarshalJSON() ([]byte, error) { - return json.Marshal(struct { - Kind string `json:"kind"` - AccountAddress string `json:"address"` - ContractName string `json:"name"` - Code string `json:"code"` - Error string `json:"error"` - }{ - Kind: "checking-failure", - AccountAddress: e.AccountAddress.HexWithPrefix(), - ContractName: e.ContractName, - Code: e.Code, - Error: e.Error, - }) -} - -type contractCheckingSuccess struct { - AccountAddress common.Address - ContractName string - Code string -} - -var _ json.Marshaler = contractCheckingSuccess{} - -func (e contractCheckingSuccess) MarshalJSON() ([]byte, error) { - return json.Marshal(struct { - Kind string `json:"kind"` - AccountAddress string `json:"address"` - ContractName string `json:"name"` - Code string `json:"code"` - }{ - Kind: "checking-success", - AccountAddress: e.AccountAddress.HexWithPrefix(), - ContractName: e.ContractName, - Code: e.Code, - }) -} diff --git a/cmd/util/ledger/migrations/contract_checking_migration_test.go b/cmd/util/ledger/migrations/contract_checking_migration_test.go deleted file mode 100644 index 4d286258227..00000000000 --- a/cmd/util/ledger/migrations/contract_checking_migration_test.go +++ /dev/null @@ -1,413 +0,0 @@ -package migrations - -import ( - "fmt" - "sort" - "testing" - - "github.com/onflow/cadence/runtime" - "github.com/onflow/cadence/runtime/common" - "github.com/onflow/cadence/runtime/interpreter" - coreContracts "github.com/onflow/flow-core-contracts/lib/go/contracts" - "github.com/onflow/flow-core-contracts/lib/go/templates" - "github.com/rs/zerolog" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/onflow/flow-go/cmd/util/ledger/util/registers" - "github.com/onflow/flow-go/fvm" - "github.com/onflow/flow-go/fvm/environment" - "github.com/onflow/flow-go/fvm/storage/snapshot" - "github.com/onflow/flow-go/fvm/systemcontracts" - "github.com/onflow/flow-go/ledger/common/convert" - "github.com/onflow/flow-go/model/flow" -) - -func oldExampleFungibleTokenCode(fungibleTokenAddress flow.Address) string { - return fmt.Sprintf( - ` - import FungibleToken from 0x%s - - pub contract ExampleFungibleToken: FungibleToken { - pub var totalSupply: UFix64 - - pub resource Vault { - pub var balance: UFix64 - } - } - `, - fungibleTokenAddress.Hex(), - ) -} - -func oldExampleNonFungibleTokenCode(fungibleTokenAddress flow.Address) string { - return fmt.Sprintf( - ` - import NonFungibleToken from 0x%s - - pub contract ExampleNFT: NonFungibleToken { - - pub var totalSupply: UInt64 - - pub resource NFT { - - pub let id: UInt64 - } - - pub resource Collection { - - pub var ownedNFTs: @{UInt64: NonFungibleToken.NFT} - } - } - `, - fungibleTokenAddress.Hex(), - ) -} - -func TestContractCheckingMigrationProgramRecovery(t *testing.T) { - - t.Parallel() - - registersByAccount := registers.NewByAccount() - - // Set up contracts - - const chainID = flow.Testnet - chain := chainID.Chain() - - systemContracts := systemcontracts.SystemContractsForChain(chainID) - - contracts := map[flow.Address]map[string][]byte{} - - addContract := func(address flow.Address, name string, code []byte) { - addressContracts, ok := contracts[address] - if !ok { - addressContracts = map[string][]byte{} - contracts[address] = addressContracts - } - require.Empty(t, addressContracts[name]) - addressContracts[name] = code - } - - addSystemContract := func(systemContract systemcontracts.SystemContract, code []byte) { - addContract(systemContract.Address, systemContract.Name, code) - } - - env := templates.Environment{} - - addSystemContract( - systemContracts.ViewResolver, - coreContracts.ViewResolver(), - ) - env.ViewResolverAddress = systemContracts.ViewResolver.Address.Hex() - - addSystemContract( - systemContracts.Burner, - coreContracts.Burner(), - ) - env.BurnerAddress = systemContracts.Burner.Address.Hex() - - addSystemContract( - systemContracts.FungibleToken, - coreContracts.FungibleToken(env), - ) - addSystemContract( - systemContracts.NonFungibleToken, - coreContracts.NonFungibleToken(env), - ) - - const exampleFungibleTokenContractName = "ExampleFungibleToken" - const exampleNonFungibleTokenContractName = "ExampleNonFungibleToken" - - // Use an old version of the ExampleFungibleToken contract, - // and "deploy" it at some arbitrary, high (i.e. non-system) address - exampleAddress, err := chain.AddressAtIndex(1000) - require.NoError(t, err) - addContract( - exampleAddress, - exampleFungibleTokenContractName, - []byte(oldExampleFungibleTokenCode(systemContracts.FungibleToken.Address)), - ) - // Use an old version of the ExampleNonFungibleToken contract, - // and "deploy" it at some arbitrary, high (i.e. non-system) address - require.NoError(t, err) - addContract( - exampleAddress, - exampleNonFungibleTokenContractName, - []byte(oldExampleNonFungibleTokenCode(systemContracts.NonFungibleToken.Address)), - ) - - for address, addressContracts := range contracts { - - for contractName, code := range addressContracts { - - err := registersByAccount.Set( - string(address[:]), - flow.ContractKey(contractName), - code, - ) - require.NoError(t, err) - } - - contractNames := make([]string, 0, len(addressContracts)) - for contractName := range addressContracts { - contractNames = append(contractNames, contractName) - } - sort.Strings(contractNames) - - encodedContractNames, err := environment.EncodeContractNames(contractNames) - require.NoError(t, err) - - err = registersByAccount.Set( - string(address[:]), - flow.ContractNamesKey, - encodedContractNames, - ) - require.NoError(t, err) - } - - programs := map[common.Location]*interpreter.Program{} - - rwf := &testReportWriterFactory{} - - // Run contract checking migration - - log := zerolog.Nop() - checkingMigration := NewContractCheckingMigration( - log, - rwf, - chainID, - false, - nil, - programs, - ) - - err = checkingMigration(registersByAccount) - require.NoError(t, err) - - reporter := rwf.reportWriters[contractCheckingReporterName] - - assert.Equal(t, - []any{ - contractCheckingSuccess{ - AccountAddress: common.Address(systemContracts.NonFungibleToken.Address), - ContractName: systemcontracts.ContractNameNonFungibleToken, - Code: string(coreContracts.NonFungibleToken(env)), - }, - contractCheckingSuccess{ - AccountAddress: common.Address(systemContracts.ViewResolver.Address), - ContractName: systemcontracts.ContractNameViewResolver, - Code: string(coreContracts.ViewResolver()), - }, - contractCheckingSuccess{ - AccountAddress: common.Address(systemContracts.Burner.Address), - ContractName: systemcontracts.ContractNameBurner, - Code: string(coreContracts.Burner()), - }, - contractCheckingSuccess{ - AccountAddress: common.Address(systemContracts.FungibleToken.Address), - ContractName: systemcontracts.ContractNameFungibleToken, - Code: string(coreContracts.FungibleToken(env)), - }, - contractCheckingSuccess{ - AccountAddress: common.Address(exampleAddress), - ContractName: exampleFungibleTokenContractName, - Code: oldExampleFungibleTokenCode(systemContracts.FungibleToken.Address), - }, - contractCheckingSuccess{ - AccountAddress: common.Address(exampleAddress), - ContractName: exampleNonFungibleTokenContractName, - Code: oldExampleNonFungibleTokenCode(systemContracts.NonFungibleToken.Address), - }, - }, - reporter.entries, - ) - - // Check that the programs are recovered correctly after the migration. - - mr, err := NewInterpreterMigrationRuntime(registersByAccount, chainID, InterpreterMigrationRuntimeConfig{}) - require.NoError(t, err) - - // First, we need to create the example account - - err = mr.Accounts.Create(nil, exampleAddress) - require.NoError(t, err) - - expectedAddresses := map[flow.Address]struct{}{ - exampleAddress: {}, - } - - err = mr.Commit(expectedAddresses, log) - require.NoError(t, err) - - // Next, we need to manually store contract values in the example account, - // simulating the effect of the deploying the original contracts. - // - // We need to do so with a new runtime, - // because the previous runtime's transaction state is finalized. - - mr, err = NewInterpreterMigrationRuntime(registersByAccount, chainID, InterpreterMigrationRuntimeConfig{}) - require.NoError(t, err) - - contractsStorageMap := mr.Storage.GetStorageMap( - common.Address(exampleAddress), - runtime.StorageDomainContract, - true, - ) - - inter := mr.Interpreter - - exampleFungibleTokenContractValue := interpreter.NewCompositeValue( - inter, - interpreter.EmptyLocationRange, - common.NewAddressLocation( - nil, - common.Address(exampleAddress), - exampleFungibleTokenContractName, - ), - exampleFungibleTokenContractName, - common.CompositeKindContract, - []interpreter.CompositeField{ - { - Name: "totalSupply", - Value: interpreter.NewUnmeteredUFix64ValueWithInteger(42, interpreter.EmptyLocationRange), - }, - }, - common.Address(exampleAddress), - ) - - contractsStorageMap.SetValue( - inter, - interpreter.StringStorageMapKey(exampleFungibleTokenContractName), - exampleFungibleTokenContractValue, - ) - - exampleNonFungibleTokenContractValue := interpreter.NewCompositeValue( - inter, - interpreter.EmptyLocationRange, - common.NewAddressLocation( - nil, - common.Address(exampleAddress), - exampleNonFungibleTokenContractName, - ), - exampleNonFungibleTokenContractName, - common.CompositeKindContract, - []interpreter.CompositeField{ - { - Name: "totalSupply", - Value: interpreter.NewUnmeteredUInt64Value(42), - }, - }, - common.Address(exampleAddress), - ) - - contractsStorageMap.SetValue( - inter, - interpreter.StringStorageMapKey(exampleNonFungibleTokenContractName), - exampleNonFungibleTokenContractValue, - ) - - err = mr.Storage.NondeterministicCommit(inter, false) - require.NoError(t, err) - - err = mr.Commit(expectedAddresses, log) - require.NoError(t, err) - - // Setup complete, now we can run the test transactions - - type testCase struct { - name string - code string - check func(t *testing.T, err error) - } - - testCases := []testCase{ - { - name: exampleFungibleTokenContractName, - code: fmt.Sprintf( - ` - import ExampleFungibleToken from %s - - transaction { - execute { - assert(ExampleFungibleToken.totalSupply == 42.0) - destroy ExampleFungibleToken.createEmptyVault( - vaultType: Type<@ExampleFungibleToken.Vault>() - ) - } - } - `, - exampleAddress.HexWithPrefix(), - ), - check: func(t *testing.T, err error) { - require.Error(t, err) - require.ErrorContains(t, err, "Contract ExampleFungibleToken is no longer functional") - require.ErrorContains(t, err, "createEmptyVault is not available in recovered program.") - }, - }, - { - name: exampleNonFungibleTokenContractName, - code: fmt.Sprintf( - ` - import ExampleNonFungibleToken from %s - - transaction { - execute { - destroy ExampleNonFungibleToken.createEmptyCollection( - nftType: Type<@ExampleNonFungibleToken.NFT>() - ) - } - } - `, - exampleAddress.HexWithPrefix(), - ), - check: func(t *testing.T, err error) { - require.Error(t, err) - require.ErrorContains(t, err, "Contract ExampleNonFungibleToken is no longer functional") - require.ErrorContains(t, err, "createEmptyCollection is not available in recovered program.") - }, - }, - } - - storageSnapshot := snapshot.MapStorageSnapshot{} - - newPayloads := registersByAccount.DestructIntoPayloads(1) - - for _, newPayload := range newPayloads { - registerID, registerValue, err := convert.PayloadToRegister(newPayload) - require.NoError(t, err) - - storageSnapshot[registerID] = registerValue - } - - test := func(testCase testCase) { - - t.Run(testCase.name, func(t *testing.T) { - - txBody := flow.NewTransactionBody(). - SetScript([]byte(testCase.code)) - - vm := fvm.NewVirtualMachine() - - ctx := fvm.NewContext( - fvm.WithChain(chain), - fvm.WithAuthorizationChecksEnabled(false), - fvm.WithSequenceNumberCheckAndIncrementEnabled(false), - fvm.WithCadenceLogging(true), - ) - - _, output, err := vm.Run( - ctx, - fvm.Transaction(txBody, 0), - storageSnapshot, - ) - - require.NoError(t, err) - testCase.check(t, output.Err) - }) - } - - for _, testCase := range testCases { - test(testCase) - } -} diff --git a/cmd/util/ledger/migrations/contract_cleanup_migration.go b/cmd/util/ledger/migrations/contract_cleanup_migration.go deleted file mode 100644 index e60f1a2f83a..00000000000 --- a/cmd/util/ledger/migrations/contract_cleanup_migration.go +++ /dev/null @@ -1,298 +0,0 @@ -package migrations - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "sort" - - "github.com/rs/zerolog" - - "github.com/onflow/cadence/runtime/common" - - "github.com/onflow/flow-go/cmd/util/ledger/reporters" - "github.com/onflow/flow-go/cmd/util/ledger/util/registers" - "github.com/onflow/flow-go/fvm/environment" - "github.com/onflow/flow-go/model/flow" -) - -// ContractCleanupMigration normalized account's contract names and removes empty contracts. -type ContractCleanupMigration struct { - log zerolog.Logger - reporter reporters.ReportWriter -} - -const contractCleanupReporterName = "contract-cleanup" - -var _ AccountBasedMigration = &ContractCleanupMigration{} - -func NewContractCleanupMigration(rwf reporters.ReportWriterFactory) *ContractCleanupMigration { - return &ContractCleanupMigration{ - reporter: rwf.ReportWriter(contractCleanupReporterName), - } -} - -func (d *ContractCleanupMigration) InitMigration( - log zerolog.Logger, - _ *registers.ByAccount, - _ int, -) error { - d.log = log. - With(). - Str("migration", "ContractCleanupMigration"). - Logger() - - return nil -} - -func (d *ContractCleanupMigration) MigrateAccount( - _ context.Context, - address common.Address, - accountRegisters *registers.AccountRegisters, -) error { - - // Get the set of all contract names for the account. - - oldContractNames, err := d.getContractNames(accountRegisters) - if err != nil { - return fmt.Errorf( - "failed to get contract names for %s: %w", - address.HexWithPrefix(), - err, - ) - } - - contractNameSet := make(map[string]struct{}) - for _, contractName := range oldContractNames { - contractNameSet[contractName] = struct{}{} - } - - contractNamesSorted := make([]string, 0, len(contractNameSet)) - for contractName := range contractNameSet { - contractNamesSorted = append(contractNamesSorted, contractName) - } - sort.Strings(contractNamesSorted) - - // Cleanup the code for each contract in the account. - // If the contract code is empty, the contract code register will be removed, - // and the contract name will be removed from the account's contract names. - - for _, contractName := range contractNamesSorted { - removed, err := d.cleanupContractCode( - address, - accountRegisters, - contractName, - ) - if err != nil { - return fmt.Errorf( - "failed to cleanup contract code for %s: %w", - address.HexWithPrefix(), - err, - ) - } - - if removed { - delete(contractNameSet, contractName) - } - } - - // Sort the contract names and set them back to the account. - - newContractNames := make([]string, 0, len(contractNameSet)) - for contractName := range contractNameSet { - newContractNames = append(newContractNames, contractName) - } - - sort.Strings(newContractNames) - - // NOTE: Always set the contract names back to the account, - // even if there are no contract names. - // This effectively clears the contract names register. - - err = d.setContractNames(accountRegisters, newContractNames) - if err != nil { - return fmt.Errorf( - "failed to set contract names for %s: %w", - address.HexWithPrefix(), - err, - ) - } - - if !stringSlicesEqual(newContractNames, oldContractNames) { - d.reporter.Write(contractNamesChanged{ - AccountAddress: address, - Old: oldContractNames, - New: newContractNames, - }) - } - - return nil -} - -func (d *ContractCleanupMigration) getContractNames( - accountRegisters *registers.AccountRegisters, -) ([]string, error) { - owner := accountRegisters.Owner() - - encodedContractNames, err := accountRegisters.Get(owner, flow.ContractNamesKey) - if err != nil { - return nil, fmt.Errorf( - "failed to get contract names: %w", - err, - ) - } - - if len(encodedContractNames) == 0 { - return nil, nil - } - - contractNames, err := environment.DecodeContractNames(encodedContractNames) - if err != nil { - return nil, fmt.Errorf( - "failed to decode contract names: %w", - err, - ) - } - - return contractNames, nil -} - -func (d *ContractCleanupMigration) setContractNames( - accountRegisters *registers.AccountRegisters, - contractNames []string, -) error { - owner := accountRegisters.Owner() - - var newEncodedContractNames []byte - var err error - - // Encode the new contract names, if there are any. - - if len(contractNames) > 0 { - newEncodedContractNames, err = environment.EncodeContractNames(contractNames) - if err != nil { - return fmt.Errorf( - "failed to encode contract names: %w", - err, - ) - } - } - - // NOTE: always set the contract names register, even if there are not contract names. - // This effectively clears the contract names register. - - err = accountRegisters.Set(owner, flow.ContractNamesKey, newEncodedContractNames) - if err != nil { - return fmt.Errorf( - "failed to set contract names: %w", - err, - ) - } - - return nil -} - -// cleanupContractCode removes the code for the contract if it is empty. -// Returns true if the contract code was removed. -func (d *ContractCleanupMigration) cleanupContractCode( - address common.Address, - accountRegisters *registers.AccountRegisters, - contractName string, -) (removed bool, err error) { - owner := accountRegisters.Owner() - - contractKey := flow.ContractKey(contractName) - - code, err := accountRegisters.Get(owner, contractKey) - if err != nil { - return false, fmt.Errorf( - "failed to get contract code for %s: %w", - contractName, - err, - ) - } - - // If the contract code is empty, remove the contract code register. - - if len(bytes.TrimSpace(code)) == 0 { - err = accountRegisters.Set(owner, contractKey, nil) - if err != nil { - return false, fmt.Errorf( - "failed to clear contract code for %s: %w", - contractName, - err, - ) - } - - d.reporter.Write(emptyContractRemoved{ - AccountAddress: address, - ContractName: contractName, - }) - - removed = true - } - - return removed, nil -} - -func (d *ContractCleanupMigration) Close() error { - d.reporter.Close() - - return nil -} - -type emptyContractRemoved struct { - AccountAddress common.Address - ContractName string -} - -var _ json.Marshaler = emptyContractRemoved{} - -func (e emptyContractRemoved) MarshalJSON() ([]byte, error) { - return json.Marshal(struct { - Kind string `json:"kind"` - AccountAddress string `json:"address"` - ContractName string `json:"name"` - }{ - Kind: "empty-contract-removed", - AccountAddress: e.AccountAddress.HexWithPrefix(), - ContractName: e.ContractName, - }) -} - -func stringSlicesEqual(a, b []string) bool { - if len(a) != len(b) { - return false - } - - for i := range a { - if a[i] != b[i] { - return false - } - } - - return true -} - -type contractNamesChanged struct { - AccountAddress common.Address `json:"address"` - Old []string `json:"old"` - New []string `json:"new"` -} - -var _ json.Marshaler = contractNamesChanged{} - -func (e contractNamesChanged) MarshalJSON() ([]byte, error) { - return json.Marshal(struct { - Kind string `json:"kind"` - AccountAddress string `json:"address"` - Old []string `json:"old"` - New []string `json:"new"` - }{ - Kind: "contract-names-changed", - AccountAddress: e.AccountAddress.HexWithPrefix(), - Old: e.Old, - New: e.New, - }) -} diff --git a/cmd/util/ledger/migrations/contract_cleanup_migration_test.go b/cmd/util/ledger/migrations/contract_cleanup_migration_test.go deleted file mode 100644 index 36435c6ca8b..00000000000 --- a/cmd/util/ledger/migrations/contract_cleanup_migration_test.go +++ /dev/null @@ -1,278 +0,0 @@ -package migrations - -import ( - "context" - "testing" - - "github.com/onflow/cadence/runtime/common" - "github.com/rs/zerolog" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/onflow/flow-go/cmd/util/ledger/util/registers" - "github.com/onflow/flow-go/fvm/environment" - "github.com/onflow/flow-go/model/flow" -) - -func TestContractCleanupMigration1(t *testing.T) { - - t.Parallel() - - // Arrange - - address, err := common.HexToAddress("0x4184b8bdf78db9eb") - require.NoError(t, err) - - flowAddress := flow.ConvertAddress(address) - owner := flow.AddressToRegisterOwner(flowAddress) - - const contractNameEmpty = "Foo" - const contractNameNonEmpty = "Bar" - - registersByAccount := registers.NewByAccount() - - err = registersByAccount.Set( - owner, - flow.ContractKey(contractNameEmpty), - // Some whitespace for testing purposes - []byte(" \t \n "), - ) - require.NoError(t, err) - - err = registersByAccount.Set( - owner, - flow.ContractKey(contractNameNonEmpty), - []byte(" \n \t access(all) contract Bar {} \n \n"), - ) - require.NoError(t, err) - - encodedContractNames, err := environment.EncodeContractNames([]string{ - // Unsorted and duplicates for testing purposes - contractNameEmpty, - contractNameNonEmpty, - contractNameEmpty, - contractNameNonEmpty, - contractNameEmpty, - contractNameEmpty, - contractNameNonEmpty, - }) - require.NoError(t, err) - - err = registersByAccount.Set( - owner, - flow.ContractNamesKey, - encodedContractNames, - ) - require.NoError(t, err) - - // Act - - rwf := &testReportWriterFactory{} - - cleanupMigration := NewContractCleanupMigration(rwf) - - log := zerolog.Nop() - - err = cleanupMigration.InitMigration(log, registersByAccount, 1) - require.NoError(t, err) - - accountRegisters := registersByAccount.AccountRegisters(owner) - - err = cleanupMigration.MigrateAccount( - context.Background(), - address, - accountRegisters, - ) - require.NoError(t, err) - - err = cleanupMigration.Close() - require.NoError(t, err) - - // Assert - - encodedContractNames, err = registersByAccount.Get( - owner, - flow.ContractNamesKey, - ) - require.NoError(t, err) - - contractNames, err := environment.DecodeContractNames(encodedContractNames) - require.NoError(t, err) - assert.Equal(t, - []string{ - contractNameNonEmpty, - }, - contractNames, - ) - - contractEmpty, err := registersByAccount.Get( - owner, - flow.ContractKey(contractNameEmpty), - ) - require.NoError(t, err) - assert.Nil(t, contractEmpty) - - contractNonEmpty, err := registersByAccount.Get( - owner, - flow.ContractKey(contractNameNonEmpty), - ) - require.NoError(t, err) - assert.NotEmpty(t, contractNonEmpty) - - reporter := rwf.reportWriters[contractCleanupReporterName] - require.NotNil(t, reporter) - - assert.Equal(t, - []any{ - emptyContractRemoved{ - AccountAddress: address, - ContractName: contractNameEmpty, - }, - contractNamesChanged{ - AccountAddress: address, - Old: []string{ - contractNameEmpty, - contractNameNonEmpty, - contractNameEmpty, - contractNameNonEmpty, - contractNameEmpty, - contractNameEmpty, - contractNameNonEmpty, - }, - New: []string{ - contractNameNonEmpty, - }, - }, - }, - reporter.entries, - ) -} - -func TestContractCleanupMigration2(t *testing.T) { - - t.Parallel() - - // Arrange - - address, err := common.HexToAddress("0x4184b8bdf78db9eb") - require.NoError(t, err) - - flowAddress := flow.ConvertAddress(address) - owner := flow.AddressToRegisterOwner(flowAddress) - - const contractNameEmpty1 = "Foo" - const contractNameEmpty2 = "Bar" - - registersByAccount := registers.NewByAccount() - - err = registersByAccount.Set( - owner, - flow.ContractKey(contractNameEmpty1), - // Some whitespace for testing purposes - []byte(" \t \n "), - ) - require.NoError(t, err) - - err = registersByAccount.Set( - owner, - flow.ContractKey(contractNameEmpty2), - []byte("\n \t \n \t"), - ) - require.NoError(t, err) - - encodedContractNames, err := environment.EncodeContractNames([]string{ - // Unsorted and duplicates for testing purposes - contractNameEmpty1, - contractNameEmpty2, - contractNameEmpty1, - contractNameEmpty2, - contractNameEmpty1, - contractNameEmpty1, - contractNameEmpty2, - }) - require.NoError(t, err) - - err = registersByAccount.Set( - owner, - flow.ContractNamesKey, - encodedContractNames, - ) - require.NoError(t, err) - - // Act - - rwf := &testReportWriterFactory{} - - cleanupMigration := NewContractCleanupMigration(rwf) - - log := zerolog.Nop() - - err = cleanupMigration.InitMigration(log, registersByAccount, 1) - require.NoError(t, err) - - accountRegisters := registersByAccount.AccountRegisters(owner) - - err = cleanupMigration.MigrateAccount( - context.Background(), - address, - accountRegisters, - ) - require.NoError(t, err) - - err = cleanupMigration.Close() - require.NoError(t, err) - - // Assert - - encodedContractNames, err = registersByAccount.Get( - owner, - flow.ContractNamesKey, - ) - require.NoError(t, err) - assert.Nil(t, encodedContractNames) - - contractEmpty1, err := registersByAccount.Get( - owner, - flow.ContractKey(contractNameEmpty1), - ) - require.NoError(t, err) - assert.Nil(t, contractEmpty1) - - contractEmpty2, err := registersByAccount.Get( - owner, - flow.ContractKey(contractNameEmpty2), - ) - require.NoError(t, err) - assert.Nil(t, contractEmpty2) - - reporter := rwf.reportWriters[contractCleanupReporterName] - require.NotNil(t, reporter) - - // Order is alphabetical - assert.Equal(t, - []any{ - emptyContractRemoved{ - AccountAddress: address, - ContractName: contractNameEmpty2, - }, - emptyContractRemoved{ - AccountAddress: address, - ContractName: contractNameEmpty1, - }, - contractNamesChanged{ - AccountAddress: address, - Old: []string{ - contractNameEmpty1, - contractNameEmpty2, - contractNameEmpty1, - contractNameEmpty2, - contractNameEmpty1, - contractNameEmpty1, - contractNameEmpty2, - }, - New: []string{}, - }, - }, - reporter.entries, - ) -} diff --git a/cmd/util/ledger/migrations/deploy_migration.go b/cmd/util/ledger/migrations/deploy_migration.go index ed631279ca7..0fc2c4223e7 100644 --- a/cmd/util/ledger/migrations/deploy_migration.go +++ b/cmd/util/ledger/migrations/deploy_migration.go @@ -8,6 +8,11 @@ import ( "github.com/onflow/flow-go/model/flow" ) +type Contract struct { + Name string + Code []byte +} + func NewDeploymentMigration( chainID flow.ChainID, contract Contract, diff --git a/cmd/util/ledger/migrations/evm.go b/cmd/util/ledger/migrations/evm.go deleted file mode 100644 index 8ce7a49729c..00000000000 --- a/cmd/util/ledger/migrations/evm.go +++ /dev/null @@ -1,84 +0,0 @@ -package migrations - -import ( - "fmt" - - "github.com/rs/zerolog" - - "github.com/onflow/flow-go/fvm/evm/stdlib" - "github.com/onflow/flow-go/fvm/systemcontracts" - "github.com/onflow/flow-go/model/flow" -) - -func NewEVMDeploymentMigration( - chainID flow.ChainID, - logger zerolog.Logger, - full bool, -) RegistersMigration { - - systemContracts := systemcontracts.SystemContractsForChain(chainID) - address := systemContracts.EVMContract.Address - - var code []byte - if full { - code = stdlib.ContractCode( - systemContracts.NonFungibleToken.Address, - systemContracts.FungibleToken.Address, - systemContracts.FlowToken.Address, - ) - } else { - code = []byte(stdlib.ContractMinimalCode) - } - - return NewDeploymentMigration( - chainID, - Contract{ - Name: systemContracts.EVMContract.Name, - Code: code, - }, - address, - map[flow.Address]struct{}{ - address: {}, - }, - logger, - ) -} - -// NewEVMSetupMigration returns a migration that sets up the EVM contract account. -// It performs the same operations as the EVM contract's initializer, calling the function EVM.setupHeartbeat, -// which in turn creates an EVM.Heartbeat resource, and writes it to the account's storage. -func NewEVMSetupMigration( - chainID flow.ChainID, - logger zerolog.Logger, -) RegistersMigration { - - systemContracts := systemcontracts.SystemContractsForChain(chainID) - evmContract := systemContracts.EVMContract - - tx := flow.NewTransactionBody(). - SetScript([]byte(fmt.Sprintf( - ` - import EVM from %s - - transaction { - prepare() { - EVM.setupHeartbeat() - } - } - `, - evmContract.Address.HexWithPrefix(), - ))) - - return NewTransactionBasedMigration( - tx, - chainID, - logger, - map[flow.Address]struct{}{ - // The function call writes to the EVM contract account - evmContract.Address: {}, - // The function call writes to the global account, - // as the function creates a resource, which gets initialized with a UUID - flow.Address{}: {}, - }, - ) -} diff --git a/cmd/util/ledger/migrations/filter_unreferenced_slabs_migration.go b/cmd/util/ledger/migrations/filter_unreferenced_slabs_migration.go index 2ccce5c161d..a9fac706c8c 100644 --- a/cmd/util/ledger/migrations/filter_unreferenced_slabs_migration.go +++ b/cmd/util/ledger/migrations/filter_unreferenced_slabs_migration.go @@ -12,8 +12,8 @@ import ( "time" "github.com/onflow/atree" + "github.com/onflow/cadence/common" "github.com/onflow/cadence/runtime" - "github.com/onflow/cadence/runtime/common" "github.com/rs/zerolog" "github.com/onflow/flow-go/cmd/util/ledger/reporters" diff --git a/cmd/util/ledger/migrations/filter_unreferenced_slabs_migration_test.go b/cmd/util/ledger/migrations/filter_unreferenced_slabs_migration_test.go index 0ed6f5f7752..1d3149cb988 100644 --- a/cmd/util/ledger/migrations/filter_unreferenced_slabs_migration_test.go +++ b/cmd/util/ledger/migrations/filter_unreferenced_slabs_migration_test.go @@ -3,16 +3,19 @@ package migrations import ( "context" "encoding/binary" + "fmt" + "sync" "testing" "github.com/onflow/atree" + "github.com/onflow/cadence/common" + "github.com/onflow/cadence/interpreter" "github.com/onflow/cadence/runtime" - "github.com/onflow/cadence/runtime/common" - "github.com/onflow/cadence/runtime/interpreter" "github.com/rs/zerolog" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/onflow/flow-go/cmd/util/ledger/reporters" "github.com/onflow/flow-go/cmd/util/ledger/util" "github.com/onflow/flow-go/cmd/util/ledger/util/registers" "github.com/onflow/flow-go/ledger" @@ -20,6 +23,42 @@ import ( "github.com/onflow/flow-go/model/flow" ) +type testReportWriterFactory struct { + lock sync.Mutex + reportWriters map[string]*testReportWriter +} + +func (f *testReportWriterFactory) ReportWriter(dataNamespace string) reporters.ReportWriter { + f.lock.Lock() + defer f.lock.Unlock() + + if f.reportWriters == nil { + f.reportWriters = make(map[string]*testReportWriter) + } + reportWriter := &testReportWriter{} + if _, ok := f.reportWriters[dataNamespace]; ok { + panic(fmt.Sprintf("report writer already exists for namespace %s", dataNamespace)) + } + f.reportWriters[dataNamespace] = reportWriter + return reportWriter +} + +type testReportWriter struct { + lock sync.Mutex + entries []any +} + +var _ reporters.ReportWriter = &testReportWriter{} + +func (r *testReportWriter) Write(entry any) { + r.lock.Lock() + defer r.lock.Unlock() + + r.entries = append(r.entries, entry) +} + +func (r *testReportWriter) Close() {} + func TestFilterUnreferencedSlabs(t *testing.T) { t.Parallel() diff --git a/cmd/util/ledger/migrations/fix_broken_data_migration.go b/cmd/util/ledger/migrations/fix_broken_data_migration.go index 81988dddddd..56bfda49257 100644 --- a/cmd/util/ledger/migrations/fix_broken_data_migration.go +++ b/cmd/util/ledger/migrations/fix_broken_data_migration.go @@ -8,12 +8,12 @@ import ( "sync" "time" - "github.com/onflow/cadence/migrations" + "github.com/onflow/cadence/interpreter" "github.com/rs/zerolog" "github.com/onflow/atree" - "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/common" "github.com/onflow/flow-go/cmd/util/ledger/reporters" "github.com/onflow/flow-go/cmd/util/ledger/util" @@ -23,6 +23,28 @@ import ( "github.com/onflow/flow-go/model/flow" ) +// ShouldFixBrokenCompositeKeyedDictionary returns true if the given value is a dictionary with a composite key type. +// +// It is useful for use with atree's PersistentSlabStorage.FixLoadedBrokenReferences. +// +// NOTE: The intended use case is to enable migration programs in onflow/flow-go to fix broken references. +// As of April 2024, only 10 registers in testnet (not mainnet) were found to have broken references, +// and they seem to have resulted from a bug that was fixed 2 years ago by https://github.com/onflow/cadence/pull/1565. +func ShouldFixBrokenCompositeKeyedDictionary(atreeValue atree.Value) bool { + orderedMap, ok := atreeValue.(*atree.OrderedMap) + if !ok { + return false + } + + dictionaryStaticType, ok := orderedMap.Type().(*interpreter.DictionaryStaticType) + if !ok { + return false + } + + _, ok = dictionaryStaticType.KeyType.(*interpreter.CompositeStaticType) + return ok +} + type FixSlabsWithBrokenReferencesMigration struct { log zerolog.Logger rw reporters.ReportWriter @@ -86,7 +108,7 @@ func (m *FixSlabsWithBrokenReferencesMigration) MigrateAccount( // Fix broken references fixedStorageIDs, skippedStorageIDs, err := - storage.FixLoadedBrokenReferences(migrations.ShouldFixBrokenCompositeKeyedDictionary) + storage.FixLoadedBrokenReferences(ShouldFixBrokenCompositeKeyedDictionary) if err != nil { return err } diff --git a/cmd/util/ledger/migrations/fix_broken_data_migration_test.go b/cmd/util/ledger/migrations/fix_broken_data_migration_test.go index 585ec2515a6..44be376a80a 100644 --- a/cmd/util/ledger/migrations/fix_broken_data_migration_test.go +++ b/cmd/util/ledger/migrations/fix_broken_data_migration_test.go @@ -6,7 +6,7 @@ import ( "encoding/hex" "testing" - "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/common" "github.com/rs/zerolog" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/cmd/util/ledger/migrations/migration_matrics_collector_test.go b/cmd/util/ledger/migrations/migration_matrics_collector_test.go deleted file mode 100644 index 29afafb19ef..00000000000 --- a/cmd/util/ledger/migrations/migration_matrics_collector_test.go +++ /dev/null @@ -1,209 +0,0 @@ -package migrations - -import ( - "testing" - - "github.com/rs/zerolog" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/onflow/cadence/runtime/common" - - "github.com/onflow/flow-go/cmd/util/ledger/util" - "github.com/onflow/flow-go/cmd/util/ledger/util/registers" - "github.com/onflow/flow-go/model/flow" -) - -func TestMigrationMetricsCollection(t *testing.T) { - - t.Parallel() - - t.Run("contract not staged", func(t *testing.T) { - - t.Parallel() - - // Get the old payloads - payloads, err := util.PayloadsFromEmulatorSnapshot(snapshotPath) - require.NoError(t, err) - - registersByAccount, err := registers.NewByAccountFromPayloads(payloads) - require.NoError(t, err) - - rwf := &testReportWriterFactory{} - - logWriter := &writer{} - logger := zerolog.New(logWriter).Level(zerolog.ErrorLevel) - - const nWorker = 2 - - const chainID = flow.Emulator - const evmContractChange = EVMContractChangeNone - const burnerContractChange = BurnerContractChangeDeploy - - migrations := NewCadence1Migrations( - logger, - t.TempDir(), - rwf, - Options{ - NWorker: nWorker, - ChainID: chainID, - EVMContractChange: evmContractChange, - BurnerContractChange: burnerContractChange, - VerboseErrorOutput: true, - ReportMetrics: true, - - // Important: 'Test' contract is NOT staged intentionally. - // So values belongs to types from 'Test' contract should be - // identified as un-migrated values. - }, - ) - - for _, migration := range migrations { - err = migration.Migrate(registersByAccount) - require.NoError( - t, - err, - "migration `%s` failed, logs: %v", - migration.Name, - logWriter.logs, - ) - } - - require.NoError(t, err) - - // Should have 1 error: - // - The `Test` contract checking error - logs := logWriter.logs - require.Len(t, logs, 1) - assert.Contains(t, logs[0], "`pub` is no longer a valid access keyword") - - reportWriter := rwf.reportWriters["metrics-collecting-migration"] - require.Len(t, reportWriter.entries, 1) - - entry := reportWriter.entries[0] - require.IsType(t, Metrics{}, entry) - - require.Equal( - t, - Metrics{ - TotalValues: 789, - TotalErrors: 6, - ErrorsPerContract: map[string]int{ - "A.01cf0e2f2f715450.Test": 6, - }, - ValuesPerContract: map[string]int{ - "A.01cf0e2f2f715450.Test": 6, - "A.0ae53cb6e3f42a79.FlowToken": 20, - "A.f8d6e0586b0a20c7.FlowClusterQC": 6, - "A.f8d6e0586b0a20c7.FlowDKG": 4, - "A.f8d6e0586b0a20c7.FlowEpoch": 1, - "A.f8d6e0586b0a20c7.FlowIDTableStaking": 5, - "A.f8d6e0586b0a20c7.LockedTokens": 3, - "A.f8d6e0586b0a20c7.NodeVersionBeacon": 1, - }, - }, - entry, - ) - }) - - t.Run("staged contract with errors", func(t *testing.T) { - - t.Parallel() - - address, err := common.HexToAddress(testAccountAddress) - require.NoError(t, err) - - // Get the old payloads - payloads, err := util.PayloadsFromEmulatorSnapshot(snapshotPath) - require.NoError(t, err) - - registersByAccount, err := registers.NewByAccountFromPayloads(payloads) - require.NoError(t, err) - - rwf := &testReportWriterFactory{} - - logWriter := &writer{} - logger := zerolog.New(logWriter).Level(zerolog.ErrorLevel) - - const nWorker = 2 - - const chainID = flow.Emulator - const evmContractChange = EVMContractChangeNone - const burnerContractChange = BurnerContractChangeDeploy - - stagedContracts := []StagedContract{ - { - Contract: Contract{ - Name: "Test", - Code: []byte(`access(all) contract Test {}`), - }, - Address: address, - }, - } - - migrations := NewCadence1Migrations( - logger, - t.TempDir(), - rwf, - Options{ - NWorker: nWorker, - ChainID: chainID, - EVMContractChange: evmContractChange, - BurnerContractChange: burnerContractChange, - VerboseErrorOutput: true, - ReportMetrics: true, - StagedContracts: stagedContracts, - }, - ) - - for _, migration := range migrations { - err = migration.Migrate(registersByAccount) - require.NoError( - t, - err, - "migration `%s` failed, logs: %v", - migration.Name, - logWriter.logs, - ) - } - - require.NoError(t, err) - - // Should have 2 errors: - // - Contract update failure error - // - The `Test` contract checking error - logs := logWriter.logs - require.Len(t, logs, 2) - assert.Contains(t, logs[0], `"migration":"StagedContractsMigration","account":"0x01cf0e2f2f715450","contract":"Test"`) - assert.Contains(t, logs[1], "`pub` is no longer a valid access keyword") - - reportWriter := rwf.reportWriters["metrics-collecting-migration"] - require.Len(t, reportWriter.entries, 1) - - entry := reportWriter.entries[0] - require.IsType(t, Metrics{}, entry) - - require.Equal( - t, - Metrics{ - TotalValues: 789, - TotalErrors: 6, - ErrorsPerContract: map[string]int{ - "A.01cf0e2f2f715450.Test": 6, - }, - ValuesPerContract: map[string]int{ - "A.01cf0e2f2f715450.Test": 6, - "A.0ae53cb6e3f42a79.FlowToken": 20, - "A.f8d6e0586b0a20c7.FlowClusterQC": 6, - "A.f8d6e0586b0a20c7.FlowDKG": 4, - "A.f8d6e0586b0a20c7.FlowEpoch": 1, - "A.f8d6e0586b0a20c7.FlowIDTableStaking": 5, - "A.f8d6e0586b0a20c7.LockedTokens": 3, - "A.f8d6e0586b0a20c7.NodeVersionBeacon": 1, - }, - }, - entry, - ) - }) -} diff --git a/cmd/util/ledger/migrations/migration_metrics_collector.go b/cmd/util/ledger/migrations/migration_metrics_collector.go deleted file mode 100644 index b6b62841203..00000000000 --- a/cmd/util/ledger/migrations/migration_metrics_collector.go +++ /dev/null @@ -1,444 +0,0 @@ -package migrations - -import ( - "context" - "fmt" - "sync" - - "github.com/rs/zerolog" - - "github.com/onflow/cadence/migrations" - "github.com/onflow/cadence/runtime/ast" - "github.com/onflow/cadence/runtime/common" - "github.com/onflow/cadence/runtime/errors" - "github.com/onflow/cadence/runtime/interpreter" - "github.com/onflow/cadence/runtime/sema" - - "github.com/onflow/flow-go/cmd/util/ledger/reporters" - "github.com/onflow/flow-go/cmd/util/ledger/util/registers" - "github.com/onflow/flow-go/model/flow" -) - -const metricsCollectingMigrationName = "metrics_collecting_migration" - -type MetricsCollectingMigration struct { - name string - chainID flow.ChainID - log zerolog.Logger - mutex sync.RWMutex - reporter reporters.ReportWriter - metricsCollector *MigrationMetricsCollector - migratedTypes map[common.TypeID]struct{} - migratedContracts map[common.Location]struct{} - programs map[common.Location]*interpreter.Program -} - -var _ migrations.ValueMigration = &MetricsCollectingMigration{} -var _ AccountBasedMigration = &MetricsCollectingMigration{} - -func NewMetricsCollectingMigration( - log zerolog.Logger, - chainID flow.ChainID, - rwf reporters.ReportWriterFactory, - programs map[common.Location]*interpreter.Program, -) *MetricsCollectingMigration { - - return &MetricsCollectingMigration{ - name: metricsCollectingMigrationName, - log: log, - reporter: rwf.ReportWriter("metrics-collecting-migration"), - metricsCollector: NewMigrationMetricsCollector(), - chainID: chainID, - programs: programs, - } -} - -func (*MetricsCollectingMigration) Name() string { - return "MetricsCollectingMigration" -} - -func (m *MetricsCollectingMigration) InitMigration( - _ zerolog.Logger, - _ *registers.ByAccount, - _ int, -) error { - m.migratedTypes = make(map[common.TypeID]struct{}) - m.migratedContracts = make(map[common.Location]struct{}) - - // If the program is available, that means the associated contracts is compatible with Cadence 1.0. - // i.e: the contract is either migrated to be compatible with 1.0 or existing contract already compatible. - for location, program := range m.programs { - var nestedDecls *ast.Members - - contract := program.Program.SoleContractDeclaration() - if contract != nil { - nestedDecls = contract.Members - - contractType := program.Elaboration.CompositeDeclarationType(contract) - m.migratedTypes[contractType.ID()] = struct{}{} - m.migratedContracts[contractType.Location] = struct{}{} - } else { - contractInterface := program.Program.SoleContractInterfaceDeclaration() - if contractInterface == nil { - declarations := program.Program.Declarations() - declarationKinds := make([]common.DeclarationKind, 0, len(declarations)) - for _, declaration := range declarations { - declarationKinds = append(declarationKinds, declaration.DeclarationKind()) - } - panic(errors.NewUnexpectedError( - "invalid program %s: expected a sole contract or contract interface, got %s", - location, - declarationKinds, - )) - } - nestedDecls = contractInterface.Members - - contractInterfaceType := program.Elaboration.InterfaceDeclarationType(contractInterface) - m.migratedTypes[contractInterfaceType.ID()] = struct{}{} - m.migratedContracts[contractInterfaceType.Location] = struct{}{} - } - - for _, compositeDecl := range nestedDecls.Composites() { - compositeType := program.Elaboration.CompositeDeclarationType(compositeDecl) - if compositeType == nil { - continue - } - m.migratedTypes[compositeType.ID()] = struct{}{} - } - - for _, interfaceDecl := range nestedDecls.Interfaces() { - interfaceType := program.Elaboration.InterfaceDeclarationType(interfaceDecl) - if interfaceType == nil { - continue - } - m.migratedTypes[interfaceType.ID()] = struct{}{} - } - - for _, attachmentDecl := range nestedDecls.Attachments() { - attachmentType := program.Elaboration.CompositeDeclarationType(attachmentDecl) - if attachmentType == nil { - continue - } - m.migratedTypes[attachmentType.ID()] = struct{}{} - } - - // Entitlements are not needed, since old values won't have them. - - // TODO: Anything else? e.g: Enum cases? - - } - - return nil -} - -func (m *MetricsCollectingMigration) MigrateAccount( - _ context.Context, - address common.Address, - accountRegisters *registers.AccountRegisters, -) error { - // Create all the runtime components we need for the migration - migrationRuntime, err := NewInterpreterMigrationRuntime( - accountRegisters, - m.chainID, - InterpreterMigrationRuntimeConfig{}, - ) - if err != nil { - return fmt.Errorf("failed to create interpreter migration runtime: %w", err) - } - - storage := migrationRuntime.Storage - - migration, err := migrations.NewStorageMigration( - migrationRuntime.Interpreter, - storage, - m.name, - address, - ) - if err != nil { - return fmt.Errorf("failed to create storage migration: %w", err) - } - - migration.Migrate( - migration.NewValueMigrationsPathMigrator( - NewStorageVisitingErrorReporter(m.log), - m, - ), - ) - - err = migration.Commit() - if err != nil { - return fmt.Errorf("failed to commit changes: %w", err) - } - - return nil -} - -func (m *MetricsCollectingMigration) Migrate( - _ interpreter.StorageKey, - _ interpreter.StorageMapKey, - value interpreter.Value, - _ *interpreter.Interpreter, - _ migrations.ValueMigrationPosition, -) ( - newValue interpreter.Value, - err error, -) { - - if m.metricsCollector != nil { - m.metricsCollector.RecordValue() - } - - var migrated bool - - switch value := value.(type) { - case interpreter.TypeValue: - // Type is optional. nil represents "unknown"/"invalid" type - ty := value.Type - if ty == nil { - return - } - - migrated = m.isTypeMigrated(ty) - - case *interpreter.IDCapabilityValue: - migrated = m.isTypeMigrated(value.BorrowType) - - case *interpreter.PathCapabilityValue: //nolint:staticcheck - // Type is optional - borrowType := value.BorrowType - if borrowType == nil { - return - } - migrated = m.isTypeMigrated(borrowType) - - case interpreter.PathLinkValue: //nolint:staticcheck - migrated = m.isTypeMigrated(value.Type) - - case *interpreter.AccountCapabilityControllerValue: - migrated = m.isTypeMigrated(value.BorrowType) - - case *interpreter.StorageCapabilityControllerValue: - migrated = m.isTypeMigrated(value.BorrowType) - - case *interpreter.ArrayValue: - migrated = m.isTypeMigrated(value.Type) - - case *interpreter.DictionaryValue: - migrated = m.isTypeMigrated(value.Type) - default: - migrated = true - } - - if !migrated && m.metricsCollector != nil { - m.metricsCollector.RecordError() - } - - return -} - -func (m *MetricsCollectingMigration) isTypeMigrated(staticType interpreter.StaticType) bool { - switch staticType := staticType.(type) { - case *interpreter.ConstantSizedStaticType: - return m.isTypeMigrated(staticType.Type) - - case *interpreter.VariableSizedStaticType: - return m.isTypeMigrated(staticType.Type) - - case *interpreter.DictionaryStaticType: - keyTypeMigrated := m.isTypeMigrated(staticType.KeyType) - if !keyTypeMigrated { - return false - } - return m.isTypeMigrated(staticType.ValueType) - - case *interpreter.CapabilityStaticType: - borrowType := staticType.BorrowType - if borrowType == nil { - return true - } - return m.isTypeMigrated(borrowType) - - case *interpreter.IntersectionStaticType: - for _, interfaceStaticType := range staticType.Types { - migrated := m.isTypeMigrated(interfaceStaticType) - if !migrated { - return false - } - } - return true - - case *interpreter.OptionalStaticType: - return m.isTypeMigrated(staticType.Type) - - case *interpreter.ReferenceStaticType: - return m.isTypeMigrated(staticType.ReferencedType) - - case interpreter.FunctionStaticType: - // Non-storable - return true - - case *interpreter.CompositeStaticType: - primitiveType := interpreter.PrimitiveStaticTypeFromTypeID(staticType.TypeID) - if primitiveType != interpreter.PrimitiveStaticTypeUnknown { - return true - } - return m.checkAndRecordIsTypeMigrated(staticType.TypeID, staticType.Location) - - case *interpreter.InterfaceStaticType: - return m.checkAndRecordIsTypeMigrated(staticType.TypeID, staticType.Location) - - case interpreter.PrimitiveStaticType: - return true - - default: - panic(errors.NewUnexpectedError("unexpected static type: %T", staticType)) - } -} - -func (m *MetricsCollectingMigration) checkAndRecordIsTypeMigrated(typeID sema.TypeID, location common.Location) bool { - // If a value related to a composite/interface type is found, - // then count this value, to measure the total number of values/objects. - m.metricsCollector.RecordValueForContract(location) - - _, ok := m.migratedTypes[typeID] - if !ok { - // If this type is not migrated/usable with cadence 1.0, - // then record this as an erroneous value. - m.metricsCollector.RecordErrorForContract(location) - - // If the type is not migrated, but the contract is migrated, then report an error. - // This is more likely to be an implementation error, where the typeID haven't got added to the list. - _, ok := m.migratedContracts[location] - if ok { - m.log.Error().Msgf( - "contract `%s` is migrated, but cannot find the migrated type: `%s`", - location, - typeID, - ) - } - } - - return ok -} - -func (m *MetricsCollectingMigration) Close() error { - m.mutex.Lock() - defer m.mutex.Unlock() - - m.reporter.Write(m.metricsCollector.metrics()) - - // Close the report writer so it flushes to file. - m.reporter.Close() - return nil -} - -func (m *MetricsCollectingMigration) CanSkip(interpreter.StaticType) bool { - return false -} - -func (m *MetricsCollectingMigration) Domains() map[string]struct{} { - return nil -} - -type MigrationMetricsCollector struct { - mutex sync.RWMutex - TotalValues int - TotalErrors int - ValuesPerContract map[common.Location]int - ErrorsPerContract map[common.Location]int -} - -func NewMigrationMetricsCollector() *MigrationMetricsCollector { - return &MigrationMetricsCollector{ - ErrorsPerContract: make(map[common.Location]int), - ValuesPerContract: make(map[common.Location]int), - } -} - -func (c *MigrationMetricsCollector) RecordValue() { - c.mutex.Lock() - defer c.mutex.Unlock() - c.TotalValues++ -} - -func (c *MigrationMetricsCollector) RecordError() { - c.mutex.Lock() - defer c.mutex.Unlock() - c.TotalErrors++ -} - -func (c *MigrationMetricsCollector) RecordErrorForContract(location common.Location) { - c.mutex.Lock() - defer c.mutex.Unlock() - c.ErrorsPerContract[location]++ -} - -func (c *MigrationMetricsCollector) RecordValueForContract(location common.Location) { - c.mutex.Lock() - defer c.mutex.Unlock() - c.ValuesPerContract[location]++ -} - -func (c *MigrationMetricsCollector) metrics() Metrics { - c.mutex.RLock() - defer c.mutex.RUnlock() - - errorsPerContract := make(map[string]int, len(c.ErrorsPerContract)) - for location, count := range c.ErrorsPerContract { - errorsPerContract[location.ID()] = count - } - - valuesPerContract := make(map[string]int, len(c.ValuesPerContract)) - for location, count := range c.ValuesPerContract { - valuesPerContract[location.ID()] = count - } - - return Metrics{ - TotalValues: c.TotalValues, - TotalErrors: c.TotalErrors, - ErrorsPerContract: errorsPerContract, - ValuesPerContract: valuesPerContract, - } -} - -type Metrics struct { - // Total values in the storage - TotalValues int `json:"totalValues"` - - // Total values with errors (un-migrated values) - TotalErrors int `json:"TotalErrors"` - - // Values with errors (un-migrated) related to each contract - ErrorsPerContract map[string]int `json:"errorsPerContract"` - - // Total values related to each contract - ValuesPerContract map[string]int `json:"valuesPerContract"` -} - -type storageVisitingErrorReporter struct { - log zerolog.Logger -} - -func NewStorageVisitingErrorReporter(log zerolog.Logger) *storageVisitingErrorReporter { - return &storageVisitingErrorReporter{ - log: log, - } -} - -var _ migrations.Reporter = &storageVisitingErrorReporter{} - -func (p *storageVisitingErrorReporter) Migrated( - _ interpreter.StorageKey, - _ interpreter.StorageMapKey, - _ string, -) { - // Ignore -} - -func (p *storageVisitingErrorReporter) DictionaryKeyConflict(addressPath interpreter.AddressPath) { - p.log.Error().Msgf("dictionary key conflict for %s", addressPath) -} - -func (p *storageVisitingErrorReporter) Error(err error) { - p.log.Error().Msgf("%s", err.Error()) -} diff --git a/cmd/util/ledger/migrations/migrator_runtime.go b/cmd/util/ledger/migrations/migrator_runtime.go index 72a049539ec..2baf8bb2468 100644 --- a/cmd/util/ledger/migrations/migrator_runtime.go +++ b/cmd/util/ledger/migrations/migrator_runtime.go @@ -3,10 +3,10 @@ package migrations import ( "fmt" + "github.com/onflow/cadence/common" + "github.com/onflow/cadence/interpreter" "github.com/onflow/cadence/runtime" - "github.com/onflow/cadence/runtime/common" - "github.com/onflow/cadence/runtime/interpreter" - "github.com/onflow/cadence/runtime/stdlib" + "github.com/onflow/cadence/stdlib" "github.com/onflow/crypto/hash" "github.com/rs/zerolog" @@ -16,6 +16,7 @@ import ( "github.com/onflow/flow-go/fvm/evm" evmStdlib "github.com/onflow/flow-go/fvm/evm/stdlib" "github.com/onflow/flow-go/fvm/storage/state" + "github.com/onflow/flow-go/fvm/systemcontracts" "github.com/onflow/flow-go/model/flow" ) @@ -111,8 +112,11 @@ func (c InterpreterMigrationRuntimeConfig) NewRuntimeInterface( } } + sc := systemcontracts.SystemContractsForChain(chainID) + return util.NewMigrationRuntimeInterface( chainID, + common.Address(sc.Crypto.Address), getCodeFunc, getContractNames, getOrLoadProgram, @@ -160,9 +164,7 @@ func NewInterpreterMigrationRuntime( ) { basicMigrationRuntime := NewBasicMigrationRuntime(regs) - env := runtime.NewBaseInterpreterEnvironment(runtime.Config{ - AttachmentsEnabled: true, - }) + env := runtime.NewBaseInterpreterEnvironment(runtime.Config{}) runtimeInterface, err := config.NewRuntimeInterface( chainID, diff --git a/cmd/util/ledger/migrations/prune_migration.go b/cmd/util/ledger/migrations/prune_migration.go deleted file mode 100644 index a2670ba7734..00000000000 --- a/cmd/util/ledger/migrations/prune_migration.go +++ /dev/null @@ -1,113 +0,0 @@ -package migrations - -import ( - "fmt" - - "github.com/onflow/cadence/runtime" - "github.com/onflow/cadence/runtime/common" - "github.com/onflow/cadence/runtime/interpreter" - "github.com/rs/zerolog" - - "github.com/onflow/flow-go/ledger" - "github.com/onflow/flow-go/model/flow" -) - -// PruneEmptyMigration removes all the payloads with empty value -// this prunes the trie for values that has been deleted -func PruneEmptyMigration(payload []ledger.Payload) ([]ledger.Payload, error) { - newPayload := make([]ledger.Payload, 0, len(payload)) - for _, p := range payload { - if len(p.Value()) > 0 { - newPayload = append(newPayload, p) - } - } - return newPayload, nil -} - -// NewCadence1PruneMigration prunes some values from the service account in the Testnet state -func NewCadence1PruneMigration( - chainID flow.ChainID, - log zerolog.Logger, - nWorkers int, -) RegistersMigration { - if chainID != flow.Testnet { - return nil - } - - serviceAccountAddress := common.Address(chainID.Chain().ServiceAddress()) - - migrate := func(storage *runtime.Storage, inter *interpreter.Interpreter) error { - - err := pruneRandomBeaconHistory(storage, inter, log, serviceAccountAddress) - if err != nil { - return err - } - - return nil - } - - return NewAccountStorageMigration( - serviceAccountAddress, - log, - chainID, - migrate, - ) -} - -func pruneRandomBeaconHistory( - storage *runtime.Storage, - inter *interpreter.Interpreter, - log zerolog.Logger, - serviceAccountAddress common.Address, -) error { - - log.Info().Msgf("pruning RandomBeaconHistory in service account %s", serviceAccountAddress) - - contracts := storage.GetStorageMap(serviceAccountAddress, runtime.StorageDomainContract, false) - if contracts == nil { - return fmt.Errorf("failed to get contracts storage map") - } - - randomBeaconHistory, ok := contracts.ReadValue( - nil, - interpreter.StringStorageMapKey("RandomBeaconHistory"), - ).(*interpreter.CompositeValue) - if !ok { - return fmt.Errorf("failed to read RandomBeaconHistory contract") - } - - randomSourceHistory, ok := randomBeaconHistory.GetField( - inter, - interpreter.EmptyLocationRange, - "randomSourceHistory", - ).(*interpreter.ArrayValue) - if !ok { - return fmt.Errorf("failed to read randomSourceHistory field") - } - - // Remove all but the last value from the randomSourceHistory - oldCount := randomSourceHistory.Count() - removalCount := oldCount - 1 - - for i := 0; i < removalCount; i++ { - randomSourceHistory.RemoveWithoutTransfer( - inter, - interpreter.EmptyLocationRange, - // NOTE: always remove the first element - 0, - ) - } - - // Check - if randomSourceHistory.Count() != 1 { - return fmt.Errorf("failed to prune randomSourceHistory") - } - - log.Info().Msgf( - "pruned %d entries in RandomBeaconHistory in service account %s", - removalCount, - serviceAccountAddress, - ) - - return nil -} diff --git a/cmd/util/ledger/migrations/staged_contracts_migration.go b/cmd/util/ledger/migrations/staged_contracts_migration.go deleted file mode 100644 index 54725538726..00000000000 --- a/cmd/util/ledger/migrations/staged_contracts_migration.go +++ /dev/null @@ -1,724 +0,0 @@ -package migrations - -import ( - "context" - "encoding/csv" - "encoding/json" - "fmt" - "io" - "os" - "sort" - "strings" - "sync" - - "github.com/onflow/cadence/runtime" - "github.com/onflow/cadence/runtime/common" - "github.com/onflow/cadence/runtime/interpreter" - "github.com/onflow/cadence/runtime/old_parser" - "github.com/onflow/cadence/runtime/pretty" - "github.com/onflow/cadence/runtime/sema" - "github.com/onflow/cadence/runtime/stdlib" - "github.com/rs/zerolog" - - "github.com/onflow/flow-go/cmd/util/ledger/reporters" - "github.com/onflow/flow-go/cmd/util/ledger/util/registers" - "github.com/onflow/flow-go/ledger" - "github.com/onflow/flow-go/model/flow" - - coreContracts "github.com/onflow/flow-core-contracts/lib/go/contracts" -) - -type StagedContractsMigration struct { - name string - chainID flow.ChainID - log zerolog.Logger - mutex sync.RWMutex - stagedContracts map[common.Address]map[string]Contract - contractsByLocation map[common.Location][]byte - enableUpdateValidation bool - userDefinedTypeChangeCheckFunc func(oldTypeID common.TypeID, newTypeID common.TypeID) (checked bool, valid bool) - elaborations map[common.Location]*sema.Elaboration - contractAdditionHandler stdlib.AccountContractAdditionHandler - contractNamesProvider stdlib.AccountContractNamesProvider - reporter reporters.ReportWriter - verboseErrorOutput bool - legacyTypeRequirements *LegacyTypeRequirements -} - -type StagedContract struct { - Contract - Address common.Address -} - -func (s StagedContract) AddressLocation() common.AddressLocation { - return common.AddressLocation{ - Name: s.Name, - Address: s.Address, - } -} - -type Contract struct { - Name string - Code []byte -} - -var _ AccountBasedMigration = &StagedContractsMigration{} - -type StagedContractsMigrationOptions struct { - ChainID flow.ChainID - VerboseErrorOutput bool -} - -func NewStagedContractsMigration( - name string, - reporterName string, - log zerolog.Logger, - rwf reporters.ReportWriterFactory, - legacyTypeRequirements *LegacyTypeRequirements, - options StagedContractsMigrationOptions, -) *StagedContractsMigration { - return &StagedContractsMigration{ - name: name, - log: log, - chainID: options.ChainID, - legacyTypeRequirements: legacyTypeRequirements, - stagedContracts: map[common.Address]map[string]Contract{}, - contractsByLocation: map[common.Location][]byte{}, - reporter: rwf.ReportWriter(reporterName), - verboseErrorOutput: options.VerboseErrorOutput, - } -} - -func (m *StagedContractsMigration) WithContractUpdateValidation() *StagedContractsMigration { - m.enableUpdateValidation = true - return m -} - -// WithStagedContractUpdates prepares the contract updates as a map for easy lookup. -func (m *StagedContractsMigration) WithStagedContractUpdates(stagedContracts []StagedContract) *StagedContractsMigration { - m.registerContractUpdates(stagedContracts) - m.log.Info(). - Msgf("total of %d staged contracts are provided externally", len(m.contractsByLocation)) - - return m -} - -func (m *StagedContractsMigration) Close() error { - m.mutex.RLock() - defer m.mutex.RUnlock() - - // Close the report writer so it flushes to file. - m.reporter.Close() - - if len(m.stagedContracts) > 0 { - dict := zerolog.Dict() - for address, contracts := range m.stagedContracts { - arr := zerolog.Arr() - for name := range contracts { - arr = arr.Str(name) - } - dict = dict.Array( - address.HexWithPrefix(), - arr, - ) - } - m.log.Error(). - Dict("contracts", dict). - Msg("failed to find all contract registers that need to be changed") - } - - return nil -} - -func (m *StagedContractsMigration) InitMigration( - log zerolog.Logger, - registersByAccount *registers.ByAccount, - _ int, -) error { - m.log = log. - With(). - Str("migration", m.name). - Logger() - - err := m.collectAndRegisterStagedContracts(registersByAccount) - if err != nil { - return err - } - - // Manually register burner contract - burnerLocation := common.AddressLocation{ - Name: "Burner", - Address: common.Address(BurnerAddressForChain(m.chainID)), - } - m.contractsByLocation[burnerLocation] = coreContracts.Burner() - - // Initialize elaborations, ContractAdditionHandler and ContractNamesProvider. - // These needs to be initialized using **ALL** payloads, not just the payloads of the account. - - elaborations := map[common.Location]*sema.Elaboration{} - - config := InterpreterMigrationRuntimeConfig{ - GetCode: func(location common.AddressLocation) ([]byte, error) { - return m.contractsByLocation[location], nil - }, - GetOrLoadProgramListener: func(location runtime.Location, program *interpreter.Program, err error) { - if err == nil { - elaborations[location] = program.Elaboration - } - }, - } - - mr, err := NewInterpreterMigrationRuntime( - registersByAccount, - m.chainID, - config, - ) - if err != nil { - return err - } - - m.elaborations = elaborations - m.contractAdditionHandler = mr.ContractAdditionHandler - m.contractNamesProvider = mr.ContractNamesProvider - - // `legacyTypeRequirements` are populated by a previous migration. - // So it should only be used once the previous migrations are complete. - if m.enableUpdateValidation { - m.userDefinedTypeChangeCheckFunc = NewUserDefinedTypeChangeCheckerFunc(m.chainID, m.legacyTypeRequirements) - } - - return nil -} - -// collectAndRegisterStagedContracts scans through the registers and collects the contracts -// staged through the `MigrationContractStaging` contract. -func (m *StagedContractsMigration) collectAndRegisterStagedContracts( - registersByAccount *registers.ByAccount, -) error { - - // If the contracts are already passed as an input to the migration - // then no need to scan the storage. - if len(m.contractsByLocation) > 0 { - return nil - } - - var stagingAccount string - - switch m.chainID { - case flow.Testnet: - stagingAccount = "0x2ceae959ed1a7e7a" - case flow.Mainnet: - stagingAccount = "0x56100d46aa9b0212" - default: - // For other networks such as emulator etc. no need to scan for staged contracts. - m.log.Warn().Msgf("staged contracts are not collected for %s state", m.chainID) - return nil - } - - stagingAccountAddress := common.Address(flow.HexToAddress(stagingAccount)) - - stagingAccountRegisters := registersByAccount.AccountRegisters(string(stagingAccountAddress[:])) - - m.log.Info().Msgf( - "found %d registers in account %s", - stagingAccountRegisters.Count(), - stagingAccount, - ) - - mr, err := NewInterpreterMigrationRuntime( - stagingAccountRegisters, - m.chainID, - InterpreterMigrationRuntimeConfig{}, - ) - if err != nil { - return err - } - - inter := mr.Interpreter - locationRange := interpreter.EmptyLocationRange - - storageMap := mr.Storage.GetStorageMap( - stagingAccountAddress, - common.PathDomainStorage.Identifier(), - false, - ) - if storageMap == nil { - m.log.Error(). - Msgf("failed to get staged contracts from account %s", stagingAccount) - return nil - } - - iterator := storageMap.Iterator(inter) - - stagedContractCapsuleStaticType := interpreter.NewCompositeStaticTypeComputeTypeID( - nil, - common.AddressLocation{ - Name: "MigrationContractStaging", - Address: stagingAccountAddress, - }, - "MigrationContractStaging.Capsule", - ) - - stagedContracts := make([]StagedContract, 0) - - for key, value := iterator.Next(); key != nil; key, value = iterator.Next() { - stringAtreeValue, ok := key.(interpreter.StringAtreeValue) - if !ok { - continue - } - - storagePath := string(stringAtreeValue) - - // Only consider paths that starts with "MigrationContractStagingCapsule_". - if !strings.HasPrefix(storagePath, "MigrationContractStagingCapsule_") { - continue - } - - staticType := value.StaticType(inter) - if !staticType.Equal(stagedContractCapsuleStaticType) { - // This shouldn't occur, but technically possible. - // e.g: accidentally storing other values under the same storage path pattern. - // So skip such values. We are not interested in those. - m.log.Debug(). - Msgf("found a value with an unexpected type `%s`", staticType) - continue - } - - stagedContract, err := m.getStagedContractFromValue(value, inter, locationRange) - if err != nil { - return err - } - - stagedContracts = append(stagedContracts, stagedContract) - } - - m.log.Info(). - Msgf("found %d staged contracts from payloads", len(stagedContracts)) - - m.registerContractUpdates(stagedContracts) - m.log.Info(). - Msgf("total of %d unique contracts are staged for all accounts", len(m.contractsByLocation)) - - return nil -} - -func (m *StagedContractsMigration) getStagedContractFromValue( - value interpreter.Value, - inter *interpreter.Interpreter, - locationRange interpreter.LocationRange, -) (StagedContract, error) { - - stagedContractCapsule, ok := value.(*interpreter.CompositeValue) - if !ok { - return StagedContract{}, - fmt.Errorf("unexpected value of type %T", value) - } - - // The stored value should take the form of: - // - // resource Capsule { - // let update: ContractUpdate - // } - // - // struct ContractUpdate { - // let address: Address - // let name: String - // var code: String - // var lastUpdated: UFix64 - // } - - updateField := stagedContractCapsule.GetField(inter, locationRange, "update") - contractUpdate, ok := updateField.(*interpreter.CompositeValue) - if !ok { - return StagedContract{}, - fmt.Errorf("unexpected value: expected `CompositeValue`, found `%T`", updateField) - } - - addressField := contractUpdate.GetField(inter, locationRange, "address") - address, ok := addressField.(interpreter.AddressValue) - if !ok { - return StagedContract{}, - fmt.Errorf("unexpected value: expected `AddressValue`, found `%T`", addressField) - } - - nameField := contractUpdate.GetField(inter, locationRange, "name") - name, ok := nameField.(*interpreter.StringValue) - if !ok { - return StagedContract{}, - fmt.Errorf("unexpected value: expected `StringValue`, found `%T`", nameField) - } - - codeField := contractUpdate.GetField(inter, locationRange, "code") - code, ok := codeField.(*interpreter.StringValue) - if !ok { - return StagedContract{}, - fmt.Errorf("unexpected value: expected `StringValue`, found `%T`", codeField) - } - - return StagedContract{ - Contract: Contract{ - Name: name.Str, - Code: []byte(code.Str), - }, - Address: common.Address(address), - }, nil -} - -// registerContractUpdates prepares the contract updates as a map for easy lookup. -func (m *StagedContractsMigration) registerContractUpdates(stagedContracts []StagedContract) { - for _, contractChange := range stagedContracts { - m.registerContractChange(contractChange) - } -} - -func (m *StagedContractsMigration) registerContractChange(change StagedContract) { - m.mutex.Lock() - defer m.mutex.Unlock() - - address := change.Address - - chain := m.chainID.Chain() - - if _, err := chain.IndexFromAddress(flow.Address(address)); err != nil { - m.log.Error().Msgf( - "invalid contract update: invalid address for chain %s: %s (%s)", - m.chainID, - address.HexWithPrefix(), - change.Name, - ) - } - - if _, ok := m.stagedContracts[address]; !ok { - m.stagedContracts[address] = map[string]Contract{} - } - - name := change.Name - - _, exist := m.stagedContracts[address][name] - if exist { - // Staged multiple updates for the same contract. - // Overwrite the previous update. - m.log.Warn().Msgf( - "existing staged update found for contract %s.%s. Previous update will be overwritten.", - address.HexWithPrefix(), - name, - ) - } - - m.stagedContracts[address][name] = change.Contract - - location := common.AddressLocation{ - Name: name, - Address: address, - } - m.contractsByLocation[location] = change.Code -} - -func (m *StagedContractsMigration) contractUpdatesForAccount( - address common.Address, -) (map[string]Contract, bool) { - m.mutex.Lock() - defer m.mutex.Unlock() - - contracts, ok := m.stagedContracts[address] - - // remove address from set of addresses - // to keep track of which addresses are left to change - delete(m.stagedContracts, address) - - return contracts, ok -} - -func (m *StagedContractsMigration) MigrateAccount( - _ context.Context, - address common.Address, - accountRegisters *registers.AccountRegisters, -) error { - - contractUpdates, ok := m.contractUpdatesForAccount(address) - if !ok { - // no contracts to change on this address - return nil - } - - var contractNames []string - for name := range contractUpdates { - contractNames = append(contractNames, name) - } - sort.Strings(contractNames) - - for _, name := range contractNames { - contract := contractUpdates[name] - - owner := string(address[:]) - key := flow.ContractKey(name) - - newCode := contract.Code - oldCode, err := accountRegisters.Get(owner, key) - if err != nil { - m.log.Err(err). - Str("account", address.HexWithPrefix()). - Str("contract", name). - Msg("failed to get old contract code") - continue - } - - if len(oldCode) == 0 { - m.log.Error(). - Str("address", address.HexWithPrefix()). - Str("contract", name). - Msg("missing old code for contract, skipping update") - continue - } - - if m.enableUpdateValidation { - err = m.checkContractUpdateValidity( - address, - name, - newCode, - oldCode, - ) - } - if err != nil { - var builder strings.Builder - errorPrinter := pretty.NewErrorPrettyPrinter(&builder, false) - - location := common.AddressLocation{ - Name: name, - Address: address, - } - printErr := errorPrinter.PrettyPrintError(err, location, m.contractsByLocation) - - var errorDetails string - if printErr == nil { - errorDetails = builder.String() - } else { - errorDetails = err.Error() - } - - if m.verboseErrorOutput { - m.log.Error(). - Str("account", address.HexWithPrefix()). - Str("contract", name). - Msgf( - "failed to update contract %s in account %s: %s", - name, - address.HexWithPrefix(), - errorDetails, - ) - } - - m.reporter.Write(contractUpdateFailureEntry{ - AccountAddress: address, - ContractName: name, - Error: errorDetails, - }) - - continue - } - - // change contract code - err = accountRegisters.Set(owner, key, newCode) - if err != nil { - m.log.Err(err). - Str("account", address.HexWithPrefix()). - Str("contract", name). - Msg("failed to set new contract code") - continue - } - - m.reporter.Write(contractUpdateEntry{ - AccountAddress: address, - ContractName: name, - }) - } - - return nil -} - -func (m *StagedContractsMigration) checkContractUpdateValidity( - address common.Address, - contractName string, - newCode []byte, - oldCode ledger.Value, -) (err error) { - // Parsing and checking of programs has to be done synchronously. - m.mutex.Lock() - defer m.mutex.Unlock() - - // Recover panics that might occur due to bugs while parsing, checking, - // or validating the contract update - defer func() { - // recover - if r := recover(); r != nil { - var ok bool - err, ok = r.(error) - if !ok { - err = fmt.Errorf("panic: %v", r) - } - - m.log.Err(err). - Stack(). - Str("account", address.HexWithPrefix()). - Str("contract", contractName). - Msg("failed to validate contract update") - } - }() - - location := common.AddressLocation{ - Name: contractName, - Address: address, - } - - // NOTE: do NOT use the program obtained from the host environment, as the current program. - // Always re-parse and re-check the new program. - // NOTE: *DO NOT* store the program – the new or updated program - // should not be effective during the execution - const getAndSetProgram = false - - newProgram, err := m.contractAdditionHandler.ParseAndCheckProgram(newCode, location, getAndSetProgram) - if err != nil { - return err - } - - oldProgram, err := old_parser.ParseProgram(nil, oldCode, old_parser.Config{}) - if err != nil { - return err - } - - validator := stdlib.NewCadenceV042ToV1ContractUpdateValidator( - location, - contractName, - m.contractNamesProvider, - oldProgram, - newProgram, - m.elaborations, - ) - - validator.WithUserDefinedTypeChangeChecker( - m.userDefinedTypeChangeCheckFunc, - ) - - return validator.Validate() -} - -func StagedContractsFromCSV(path string) ([]StagedContract, error) { - if path == "" { - return nil, nil - } - - file, err := os.Open(path) - if err != nil { - return nil, err - } - - defer file.Close() - - reader := csv.NewReader(file) - - // Expect 3 fields: address, name, code - reader.FieldsPerRecord = 3 - - var contracts []StagedContract - - for { - rec, err := reader.Read() - if err == io.EOF { - break - } - - if err != nil { - return nil, err - } - - addressHex := rec[0] - name := rec[1] - code := rec[2] - - address, err := common.HexToAddress(addressHex) - if err != nil { - return nil, err - } - - contracts = append(contracts, StagedContract{ - Contract: Contract{ - Name: name, - Code: []byte(code), - }, - Address: address, - }) - } - - return contracts, nil -} - -func NewUserDefinedTypeChangeCheckerFunc( - chainID flow.ChainID, - legacyTypeRequirements *LegacyTypeRequirements, -) func(oldTypeID common.TypeID, newTypeID common.TypeID) (checked, valid bool) { - - typeChangeRules := map[common.TypeID]common.TypeID{} - - compositeTypeRules := NewCompositeTypeConversionRules(chainID, legacyTypeRequirements) - for typeID, newStaticType := range compositeTypeRules { - typeChangeRules[typeID] = newStaticType.ID() - } - - interfaceTypeRules := NewInterfaceTypeConversionRules(chainID) - for typeID, newStaticType := range interfaceTypeRules { - typeChangeRules[typeID] = newStaticType.ID() - } - - return func(oldTypeID common.TypeID, newTypeID common.TypeID) (checked, valid bool) { - expectedNewTypeID, found := typeChangeRules[oldTypeID] - if found { - return true, expectedNewTypeID == newTypeID - } - return false, false - } -} - -// contractUpdateFailureEntry - -type contractUpdateEntry struct { - AccountAddress common.Address - ContractName string -} - -var _ json.Marshaler = contractUpdateEntry{} - -func (e contractUpdateEntry) MarshalJSON() ([]byte, error) { - return json.Marshal(struct { - Kind string `json:"kind"` - AccountAddress string `json:"account_address"` - ContractName string `json:"contract_name"` - }{ - Kind: "contract-update-success", - AccountAddress: e.AccountAddress.HexWithPrefix(), - ContractName: e.ContractName, - }) -} - -// contractUpdateFailureEntry - -type contractUpdateFailureEntry struct { - AccountAddress common.Address - ContractName string - Error string -} - -var _ json.Marshaler = contractUpdateFailureEntry{} - -func (e contractUpdateFailureEntry) MarshalJSON() ([]byte, error) { - return json.Marshal(struct { - Kind string `json:"kind"` - AccountAddress string `json:"account_address"` - ContractName string `json:"contract_name"` - Error string `json:"error"` - }{ - Kind: "contract-update-failure", - AccountAddress: e.AccountAddress.HexWithPrefix(), - ContractName: e.ContractName, - Error: e.Error, - }) -} diff --git a/cmd/util/ledger/migrations/staged_contracts_migration_test.go b/cmd/util/ledger/migrations/staged_contracts_migration_test.go deleted file mode 100644 index 972177e634f..00000000000 --- a/cmd/util/ledger/migrations/staged_contracts_migration_test.go +++ /dev/null @@ -1,2926 +0,0 @@ -package migrations - -import ( - "context" - "encoding/json" - "fmt" - "io" - "strings" - "testing" - - "github.com/onflow/cadence/runtime/interpreter" - "github.com/rs/zerolog" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/onflow/flow-go/cmd/util/ledger/util/registers" - "github.com/onflow/flow-go/fvm/environment" - "github.com/onflow/flow-go/fvm/systemcontracts" - "github.com/onflow/flow-go/model/flow" - - "github.com/onflow/cadence/runtime/common" -) - -type logWriter struct { - logs []string - enableInfoLogs bool -} - -var _ io.Writer = &logWriter{} - -const infoLogPrefix = "{\"level\":\"info\"" - -func (l *logWriter) Write(bytes []byte) (int, error) { - logStr := string(bytes) - - if !l.enableInfoLogs && strings.HasPrefix(logStr, infoLogPrefix) { - return 0, nil - } - - l.logs = append(l.logs, logStr) - return len(bytes), nil -} - -func registersForStagedContracts(stagedContracts ...StagedContract) (*registers.ByAccount, error) { - registersByAccount := registers.NewByAccount() - - for _, stagedContract := range stagedContracts { - err := registersByAccount.Set( - string(stagedContract.Address[:]), - flow.ContractKey(stagedContract.Name), - stagedContract.Code, - ) - if err != nil { - return nil, err - } - } - - return registersByAccount, nil -} - -func TestStagedContractsMigration(t *testing.T) { - t.Parallel() - - const chainID = flow.Emulator - addressGenerator := chainID.Chain().NewAddressGenerator() - - address1, err := addressGenerator.NextAddress() - require.NoError(t, err) - - address2, err := addressGenerator.NextAddress() - require.NoError(t, err) - - t.Run("one contract", func(t *testing.T) { - t.Parallel() - - oldCode := "access(all) contract A {}" - newCode := "access(all) contract A { access(all) struct B {} }" - - stagedContracts := []StagedContract{ - { - Address: common.Address(address1), - Contract: Contract{ - Name: "A", - Code: []byte(newCode), - }, - }, - } - - logWriter := &logWriter{} - log := zerolog.New(logWriter) - - rwf := &testReportWriterFactory{} - - options := StagedContractsMigrationOptions{ - ChainID: chainID, - VerboseErrorOutput: true, - } - - migration := NewStagedContractsMigration( - "test", - "test", - log, - rwf, - &LegacyTypeRequirements{}, - options, - ). - WithStagedContractUpdates(stagedContracts) - - registersByAccount, err := registersForStagedContracts( - StagedContract{ - Address: common.Address(address1), - Contract: Contract{ - Name: "A", - Code: []byte(oldCode), - }, - }, - ) - require.NoError(t, err) - - err = migration.InitMigration(log, registersByAccount, 1) - require.NoError(t, err) - - owner1 := string(address1[:]) - - accountRegisters1 := registersByAccount.AccountRegisters(owner1) - - err = migration.MigrateAccount( - context.Background(), - common.Address(address1), - accountRegisters1, - ) - require.NoError(t, err) - - err = migration.Close() - require.NoError(t, err) - - require.Empty(t, logWriter.logs) - - // Registers should have the new code - require.Equal(t, 1, registersByAccount.AccountCount()) - require.Equal(t, 1, accountRegisters1.Count()) - require.Equal(t, newCode, contractCode(t, registersByAccount, owner1, "A")) - }) - - t.Run("syntax error in new code", func(t *testing.T) { - t.Parallel() - - oldCode := "access(all) contract A {}" - newCode := "access(all) contract A { access(all) struct B () }" - - stagedContracts := []StagedContract{ - { - Address: common.Address(address1), - Contract: Contract{ - Name: "A", - Code: []byte(newCode), - }, - }, - } - - logWriter := &logWriter{} - log := zerolog.New(logWriter) - - rwf := &testReportWriterFactory{} - - options := StagedContractsMigrationOptions{ - ChainID: chainID, - VerboseErrorOutput: true, - } - - migration := NewStagedContractsMigration( - "test", - "test", - log, - rwf, - &LegacyTypeRequirements{}, - options, - ). - WithContractUpdateValidation() - migration.WithStagedContractUpdates(stagedContracts) - - registersByAccount, err := registersForStagedContracts( - StagedContract{ - Address: common.Address(address1), - Contract: Contract{ - Name: "A", - Code: []byte(oldCode), - }, - }, - ) - require.NoError(t, err) - - err = migration.InitMigration(log, registersByAccount, 1) - require.NoError(t, err) - - owner1 := string(address1[:]) - - accountRegisters1 := registersByAccount.AccountRegisters(owner1) - - err = migration.MigrateAccount( - context.Background(), - common.Address(address1), - accountRegisters1, - ) - require.NoError(t, err) - - err = migration.Close() - require.NoError(t, err) - - require.Len(t, logWriter.logs, 1) - require.Contains(t, logWriter.logs[0], `error: expected token '{'`) - - // Registers should still have the old code - require.Equal(t, 1, registersByAccount.AccountCount()) - require.Equal(t, 1, accountRegisters1.Count()) - require.Equal(t, oldCode, contractCode(t, registersByAccount, owner1, "A")) - }) - - t.Run("syntax error in old code", func(t *testing.T) { - t.Parallel() - - oldCode := "access(all) contract A {" - newCode := "access(all) contract A { access(all) struct B {} }" - - stagedContracts := []StagedContract{ - { - Address: common.Address(address1), - Contract: Contract{ - Name: "A", - Code: []byte(newCode), - }, - }, - } - - logWriter := &logWriter{} - log := zerolog.New(logWriter) - - rwf := &testReportWriterFactory{} - - options := StagedContractsMigrationOptions{ - ChainID: chainID, - VerboseErrorOutput: true, - } - - migration := NewStagedContractsMigration( - "test", - "test", - log, - rwf, - &LegacyTypeRequirements{}, - options, - ). - WithContractUpdateValidation(). - WithStagedContractUpdates(stagedContracts) - - registersByAccount, err := registersForStagedContracts( - StagedContract{ - Address: common.Address(address1), - Contract: Contract{ - Name: "A", - Code: []byte(oldCode), - }, - }, - ) - require.NoError(t, err) - - err = migration.InitMigration(log, registersByAccount, 1) - require.NoError(t, err) - - owner1 := string(address1[:]) - - accountRegisters1 := registersByAccount.AccountRegisters(owner1) - - err = migration.MigrateAccount( - context.Background(), - common.Address(address1), - accountRegisters1, - ) - require.NoError(t, err) - - err = migration.Close() - require.NoError(t, err) - - require.Len(t, logWriter.logs, 1) - require.Contains(t, logWriter.logs[0], `error: expected token '}'`) - - // Registers should still have the old code - require.Equal(t, 1, registersByAccount.AccountCount()) - require.Equal(t, 1, accountRegisters1.Count()) - require.Equal(t, oldCode, contractCode(t, registersByAccount, owner1, "A")) - }) - - t.Run("one fail, one success", func(t *testing.T) { - t.Parallel() - - oldCode1 := "access(all) contract A {}" - oldCode2 := "access(all) contract B {}" - - newCode1 := "access(all) contract A { access(all) struct C () }" // broken - newCode2 := "access(all) contract B { access(all) struct C {} }" // all good - - stagedContracts := []StagedContract{ - { - Address: common.Address(address1), - Contract: Contract{ - Name: "A", - Code: []byte(newCode1), - }, - }, - { - Address: common.Address(address1), - Contract: Contract{ - Name: "B", - Code: []byte(newCode2), - }, - }, - } - - logWriter := &logWriter{ - enableInfoLogs: true, - } - - log := zerolog.New(logWriter) - - rwf := &testReportWriterFactory{} - - options := StagedContractsMigrationOptions{ - ChainID: chainID, - VerboseErrorOutput: true, - } - - const reporterName = "test" - migration := NewStagedContractsMigration( - "test", - reporterName, - log, - rwf, - &LegacyTypeRequirements{}, - options, - ). - WithContractUpdateValidation(). - WithStagedContractUpdates(stagedContracts) - - registersByAccount, err := registersForStagedContracts( - StagedContract{ - Address: common.Address(address1), - Contract: Contract{ - Name: "A", - Code: []byte(oldCode1), - }, - }, - StagedContract{ - Address: common.Address(address1), - Contract: Contract{ - Name: "B", - Code: []byte(oldCode2), - }, - }, - ) - require.NoError(t, err) - - err = migration.InitMigration(log, registersByAccount, 1) - require.NoError(t, err) - - owner1 := string(address1[:]) - - accountRegisters1 := registersByAccount.AccountRegisters(owner1) - - err = migration.MigrateAccount( - context.Background(), - common.Address(address1), - accountRegisters1, - ) - require.NoError(t, err) - - err = migration.Close() - require.NoError(t, err) - - require.Len(t, logWriter.logs, 2) - require.Contains(t, logWriter.logs[0], `{"level":"info","message":"total of 2 staged contracts are provided externally"}`) - require.Contains(t, logWriter.logs[1], `error: expected token '{'`) - - require.Equal(t, 1, registersByAccount.AccountCount()) - require.Equal(t, 2, accountRegisters1.Count()) - // First register should still have the old code - require.Equal(t, oldCode1, contractCode(t, registersByAccount, owner1, "A")) - // Second register should have the updated code - require.Equal(t, newCode2, contractCode(t, registersByAccount, owner1, "B")) - - reportWriter := rwf.reportWriters[reporterName] - require.NotNil(t, reportWriter) - assert.ElementsMatch( - t, - []any{ - contractUpdateFailureEntry{ - AccountAddress: common.Address(address1), - ContractName: "A", - Error: "error: expected token '{'\n --> f8d6e0586b0a20c7.A:1:46\n |\n1 | access(all) contract A { access(all) struct C () }\n | ^\n", - }, - contractUpdateEntry{ - AccountAddress: common.Address(address1), - ContractName: "B", - }, - }, - reportWriter.entries, - ) - }) - - t.Run("different accounts", func(t *testing.T) { - t.Parallel() - - oldCode := "access(all) contract A {}" - newCode := "access(all) contract A { access(all) struct B {} }" - - stagedContracts := []StagedContract{ - { - Address: common.Address(address2), - Contract: Contract{ - Name: "A", - Code: []byte(newCode), - }, - }, - } - - logWriter := &logWriter{} - log := zerolog.New(logWriter) - - rwf := &testReportWriterFactory{} - - options := StagedContractsMigrationOptions{ - ChainID: chainID, - VerboseErrorOutput: true, - } - - const reporterName = "test" - migration := NewStagedContractsMigration( - "test", - reporterName, - log, - rwf, - &LegacyTypeRequirements{}, - options, - ). - WithStagedContractUpdates(stagedContracts) - - registersByAccount, err := registersForStagedContracts( - StagedContract{ - Address: common.Address(address1), - Contract: Contract{ - Name: "A", - Code: []byte(oldCode), - }, - }, - StagedContract{ - Address: common.Address(address2), - Contract: Contract{ - Name: "A", - Code: []byte(oldCode), - }, - }, - ) - require.NoError(t, err) - - err = migration.InitMigration(log, registersByAccount, 1) - require.NoError(t, err) - - // Run migration for account 1, - // There are no staged updates for contracts in account 1. - // So codes should not have been updated. - - owner1 := string(address1[:]) - accountRegisters1 := registersByAccount.AccountRegisters(owner1) - - err = migration.MigrateAccount( - context.Background(), - common.Address(address1), - accountRegisters1, - ) - require.NoError(t, err) - require.Equal(t, 1, accountRegisters1.Count()) - require.Equal(t, oldCode, contractCode(t, registersByAccount, owner1, "A")) - - // Run migration for account 2 - // There is one staged update for contracts in account 2. - // So one register/contract-code should be updated, and the other should remain the same. - - owner2 := string(address2[:]) - accountRegisters2 := registersByAccount.AccountRegisters(owner2) - - err = migration.MigrateAccount( - context.Background(), - common.Address(address2), - accountRegisters2, - ) - require.NoError(t, err) - require.Equal(t, 1, accountRegisters2.Count()) - require.Equal(t, newCode, contractCode(t, registersByAccount, owner2, "A")) - - err = migration.Close() - require.NoError(t, err) - - // No errors. - require.Empty(t, logWriter.logs) - - reportWriter := rwf.reportWriters[reporterName] - require.NotNil(t, reportWriter) - require.Len(t, reportWriter.entries, 1) - assert.Equal( - t, - contractUpdateEntry{ - AccountAddress: common.Address(address2), - ContractName: "A", - }, - reportWriter.entries[0], - ) - }) - - t.Run("multiple updates for same contract", func(t *testing.T) { - t.Parallel() - - oldCode := "access(all) contract A {}" - update1 := "access(all) contract A { access(all) struct B {} }" - update2 := "access(all) contract A { access(all) struct B {} access(all) struct C {} }" - - stagedContracts := []StagedContract{ - { - Address: common.Address(address1), - Contract: Contract{ - Name: "A", - Code: []byte(update1), - }, - }, - { - Address: common.Address(address1), - Contract: Contract{ - Name: "A", - Code: []byte(update2), - }, - }, - } - - logWriter := &logWriter{} - log := zerolog.New(logWriter) - - rwf := &testReportWriterFactory{} - - options := StagedContractsMigrationOptions{ - ChainID: chainID, - VerboseErrorOutput: true, - } - - migration := NewStagedContractsMigration( - "test", - "test", - log, - rwf, - &LegacyTypeRequirements{}, - options, - ). - WithStagedContractUpdates(stagedContracts) - - registersByAccount, err := registersForStagedContracts( - StagedContract{ - Address: common.Address(address1), - Contract: Contract{ - Name: "A", - Code: []byte(oldCode), - }, - }, - ) - require.NoError(t, err) - - err = migration.InitMigration(log, registersByAccount, 1) - require.NoError(t, err) - - owner1 := string(address1[:]) - accountRegisters1 := registersByAccount.AccountRegisters(owner1) - - err = migration.MigrateAccount( - context.Background(), - common.Address(address1), - accountRegisters1, - ) - require.NoError(t, err) - - err = migration.Close() - require.NoError(t, err) - - require.Len(t, logWriter.logs, 1) - require.Contains( - t, - logWriter.logs[0], - `existing staged update found`, - ) - - require.Equal(t, 1, registersByAccount.AccountCount()) - require.Equal(t, 1, accountRegisters1.Count()) - require.Equal(t, update2, contractCode(t, registersByAccount, owner1, "A")) - }) - - t.Run("missing old contract", func(t *testing.T) { - t.Parallel() - - newCode := "access(all) contract A { access(all) struct B {} }" - - stagedContracts := []StagedContract{ - { - Address: common.Address(address1), - Contract: Contract{ - Name: "A", - Code: []byte(newCode), - }, - }, - } - - logWriter := &logWriter{} - log := zerolog.New(logWriter) - - rwf := &testReportWriterFactory{} - - options := StagedContractsMigrationOptions{ - ChainID: chainID, - VerboseErrorOutput: true, - } - - migration := NewStagedContractsMigration( - "test", - "test", - log, - rwf, - &LegacyTypeRequirements{}, - options, - ). - WithStagedContractUpdates(stagedContracts) - - // NOTE: no payloads - registersByAccount := registers.NewByAccount() - - err := migration.InitMigration(log, registersByAccount, 1) - require.NoError(t, err) - - owner1 := string(address1[:]) - accountRegisters1 := registersByAccount.AccountRegisters(owner1) - - err = migration.MigrateAccount( - context.Background(), - common.Address(address1), - accountRegisters1, - ) - require.NoError(t, err) - - require.Len(t, logWriter.logs, 1) - assert.Contains(t, - logWriter.logs[0], - "missing old code for contract", - ) - }) - - t.Run("staged contract in storage", func(t *testing.T) { - t.Parallel() - - oldCode := "access(all) contract A {}" - newCode := "access(all) contract A { access(all) struct B {} }" - - const chainID = flow.Testnet - addressGenerator := chainID.Chain().NewAddressGenerator() - accountAddress, err := addressGenerator.NextAddress() - require.NoError(t, err) - - stagingAccountAddress := common.Address(flow.HexToAddress("0x2ceae959ed1a7e7a")) - - registersByAccount := registers.NewByAccount() - - // Create account status register - accountStatus := environment.NewAccountStatus() - - err = registersByAccount.Set( - string(stagingAccountAddress[:]), - flow.AccountStatusKey, - accountStatus.ToBytes(), - ) - require.NoError(t, err) - - mr, err := NewInterpreterMigrationRuntime( - registersByAccount, - chainID, - InterpreterMigrationRuntimeConfig{}, - ) - require.NoError(t, err) - - // Create new storage map - domain := common.PathDomainStorage.Identifier() - storageMap := mr.Storage.GetStorageMap(stagingAccountAddress, domain, true) - - contractUpdateValue := interpreter.NewCompositeValue( - mr.Interpreter, - interpreter.EmptyLocationRange, - common.AddressLocation{ - Address: stagingAccountAddress, - Name: "MigrationContractStaging", - }, - "MigrationContractStaging.ContractUpdate", - common.CompositeKindStructure, - []interpreter.CompositeField{ - { - Name: "address", - Value: interpreter.AddressValue(accountAddress), - }, - { - Name: "name", - Value: interpreter.NewUnmeteredStringValue("A"), - }, - { - Name: "code", - Value: interpreter.NewUnmeteredStringValue(newCode), - }, - }, - stagingAccountAddress, - ) - - capsuleValue := interpreter.NewCompositeValue( - mr.Interpreter, - interpreter.EmptyLocationRange, - common.AddressLocation{ - Address: stagingAccountAddress, - Name: "MigrationContractStaging", - }, - "MigrationContractStaging.Capsule", - common.CompositeKindResource, - []interpreter.CompositeField{ - { - Name: "update", - Value: contractUpdateValue, - }, - }, - stagingAccountAddress, - ) - - // Write the staged contract capsule value. - storageMap.WriteValue( - mr.Interpreter, - interpreter.StringStorageMapKey("MigrationContractStagingCapsule_some_random_suffix"), - capsuleValue, - ) - - // Write some random values as well. - storageMap.WriteValue( - mr.Interpreter, - interpreter.StringStorageMapKey("some_key"), - interpreter.NewUnmeteredStringValue("Also in the same account"), - ) - - storageMap.WriteValue( - mr.Interpreter, - interpreter.StringStorageMapKey("MigrationContractStagingCapsule_some_garbage_value"), - interpreter.NewUnmeteredStringValue("Also in the same storage path prefix"), - ) - - err = mr.Storage.NondeterministicCommit(mr.Interpreter, false) - require.NoError(t, err) - - result, err := mr.TransactionState.FinalizeMainTransaction() - require.NoError(t, err) - - err = registers.ApplyChanges( - registersByAccount, - result.WriteSet, - nil, - zerolog.Nop(), - ) - require.NoError(t, err) - - logWriter := &logWriter{ - enableInfoLogs: true, - } - log := zerolog.New(logWriter) - - rwf := &testReportWriterFactory{} - - // Important: Do not stage contracts externally. - // Should be scanned and collected from the storage. - - options := StagedContractsMigrationOptions{ - ChainID: chainID, - VerboseErrorOutput: true, - } - - migration := NewStagedContractsMigration( - "test", - "test", - log, - rwf, - &LegacyTypeRequirements{}, - options, - ) - - accountOwner := string(accountAddress[:]) - - err = registersByAccount.Set( - accountOwner, - flow.ContractKey("A"), - []byte(oldCode), - ) - require.NoError(t, err) - - err = migration.InitMigration(log, registersByAccount, 1) - require.NoError(t, err) - - accountRegisters := registersByAccount.AccountRegisters(accountOwner) - - err = migration.MigrateAccount( - context.Background(), - common.Address(accountAddress), - accountRegisters, - ) - require.NoError(t, err) - - err = migration.Close() - require.NoError(t, err) - - require.Len(t, logWriter.logs, 4) - require.Contains(t, logWriter.logs[0], "found 4 registers in account 0x2ceae959ed1a7e7a") - require.Contains(t, logWriter.logs[1], "found a value with an unexpected type `String`") - require.Contains(t, logWriter.logs[2], "found 1 staged contracts from payloads") - require.Contains(t, logWriter.logs[3], "total of 1 unique contracts are staged for all accounts") - - require.Equal(t, 1, accountRegisters.Count()) - require.Equal(t, newCode, contractCode(t, registersByAccount, accountOwner, "A")) - }) -} - -func contractCode(t *testing.T, registersByAccount *registers.ByAccount, owner, contractName string) string { - code, err := registersByAccount.Get(owner, flow.ContractKey(contractName)) - require.NoError(t, err) - return string(code) -} - -func TestStagedContractsWithImports(t *testing.T) { - t.Parallel() - - const chainID = flow.Emulator - - addressGenerator := chainID.Chain().NewAddressGenerator() - - address1, err := addressGenerator.NextAddress() - require.NoError(t, err) - - address2, err := addressGenerator.NextAddress() - require.NoError(t, err) - - t.Run("valid import", func(t *testing.T) { - t.Parallel() - - oldCodeA := fmt.Sprintf(` - import B from %s - access(all) contract A {} - `, - address2.HexWithPrefix(), - ) - - oldCodeB := `access(all) contract B {}` - - newCodeA := fmt.Sprintf(` - import B from %s - access(all) contract A { - access(all) fun foo(a: B.C) {} - } - `, - address2.HexWithPrefix(), - ) - - newCodeB := ` - access(all) contract B { - access(all) struct C {} - } - ` - - stagedContracts := []StagedContract{ - { - Address: common.Address(address1), - Contract: Contract{ - Name: "A", - Code: []byte(newCodeA), - }, - }, - { - Address: common.Address(address2), - Contract: Contract{ - Name: "B", - Code: []byte(newCodeB), - }, - }, - } - - logWriter := &logWriter{} - log := zerolog.New(logWriter) - - rwf := &testReportWriterFactory{} - - options := StagedContractsMigrationOptions{ - ChainID: chainID, - VerboseErrorOutput: true, - } - - migration := NewStagedContractsMigration( - "test", - "test", - log, - rwf, - &LegacyTypeRequirements{}, - options, - ). - WithStagedContractUpdates(stagedContracts) - - registersByAccount, err := registersForStagedContracts( - StagedContract{ - Address: common.Address(address1), - Contract: Contract{ - Name: "A", - Code: []byte(oldCodeA), - }, - }, - StagedContract{ - Address: common.Address(address2), - Contract: Contract{ - Name: "B", - Code: []byte(oldCodeB), - }, - }, - ) - require.NoError(t, err) - - err = migration.InitMigration(log, registersByAccount, 1) - require.NoError(t, err) - - owner1 := string(address1[:]) - accountRegisters1 := registersByAccount.AccountRegisters(owner1) - - err = migration.MigrateAccount( - context.Background(), - common.Address(address1), - accountRegisters1, - ) - require.NoError(t, err) - - owner2 := string(address2[:]) - accountRegisters2 := registersByAccount.AccountRegisters(owner2) - - err = migration.MigrateAccount( - context.Background(), - common.Address(address2), - accountRegisters2, - ) - require.NoError(t, err) - - err = migration.Close() - require.NoError(t, err) - - require.Empty(t, logWriter.logs) - - require.Equal(t, 1, accountRegisters1.Count()) - assert.Equal(t, newCodeA, contractCode(t, registersByAccount, owner1, "A")) - - require.Equal(t, 1, accountRegisters2.Count()) - assert.Equal(t, newCodeB, contractCode(t, registersByAccount, owner2, "B")) - }) - - t.Run("broken import, no update staged", func(t *testing.T) { - t.Parallel() - - oldCodeA := fmt.Sprintf( - ` - import B from %s - access(all) contract A {} - `, - address2.HexWithPrefix(), - ) - - oldCodeB := `pub contract B {} // not compatible` - - newCodeA := fmt.Sprintf( - ` - import B from %s - access(all) contract A { - access(all) fun foo(a: B.C) {} - } - `, - address2.HexWithPrefix(), - ) - - stagedContracts := []StagedContract{ - { - Address: common.Address(address1), - Contract: Contract{ - Name: "A", - Code: []byte(newCodeA), - }, - }, - } - - logWriter := &logWriter{} - log := zerolog.New(logWriter) - - rwf := &testReportWriterFactory{} - - options := StagedContractsMigrationOptions{ - ChainID: chainID, - VerboseErrorOutput: true, - } - - migration := NewStagedContractsMigration( - "test", - "test", - log, - rwf, - &LegacyTypeRequirements{}, - options, - ). - WithContractUpdateValidation(). - WithStagedContractUpdates(stagedContracts) - - registersByAccount, err := registersForStagedContracts( - StagedContract{ - Address: common.Address(address1), - Contract: Contract{ - Name: "A", - Code: []byte(oldCodeA), - }, - }, - StagedContract{ - Address: common.Address(address2), - Contract: Contract{ - Name: "B", - Code: []byte(oldCodeB), - }, - }, - ) - require.NoError(t, err) - - err = migration.InitMigration(log, registersByAccount, 1) - require.NoError(t, err) - - owner1 := string(address1[:]) - accountRegisters1 := registersByAccount.AccountRegisters(owner1) - - err = migration.MigrateAccount( - context.Background(), - common.Address(address1), - accountRegisters1, - ) - require.NoError(t, err) - - owner2 := string(address2[:]) - accountRegisters2 := registersByAccount.AccountRegisters(owner2) - - err = migration.MigrateAccount( - context.Background(), - common.Address(address2), - accountRegisters2, - ) - require.NoError(t, err) - - err = migration.Close() - require.NoError(t, err) - - require.Len(t, logWriter.logs, 1) - require.Contains( - t, - logWriter.logs[0], - "`pub` is no longer a valid access keyword", - ) - - // Registers should be the old ones - require.Equal(t, 1, accountRegisters1.Count()) - assert.Equal(t, oldCodeA, contractCode(t, registersByAccount, owner1, "A")) - - require.Equal(t, 1, accountRegisters2.Count()) - assert.Equal(t, oldCodeB, contractCode(t, registersByAccount, owner2, "B")) - }) - - t.Run("broken import ", func(t *testing.T) { - t.Parallel() - - oldCodeA := fmt.Sprintf( - ` - import B from %s - access(all) contract A {} - `, - address2.HexWithPrefix(), - ) - - oldCodeB := `pub contract B {} // not compatible` - - newCodeA := fmt.Sprintf( - ` - import B from %s - access(all) contract A { - access(all) fun foo(a: B.C) {} - } - `, - address2.HexWithPrefix(), - ) - - newCodeB := `pub contract B {} // not compatible` - - stagedContracts := []StagedContract{ - { - Address: common.Address(address1), - Contract: Contract{ - Name: "A", - Code: []byte(newCodeA), - }, - }, - { - Address: common.Address(address2), - Contract: Contract{ - Name: "B", - Code: []byte(newCodeB), - }, - }, - } - - logWriter := &logWriter{} - log := zerolog.New(logWriter) - - rwf := &testReportWriterFactory{} - - options := StagedContractsMigrationOptions{ - ChainID: chainID, - VerboseErrorOutput: true, - } - - migration := NewStagedContractsMigration( - "test", - "test", - log, - rwf, - &LegacyTypeRequirements{}, - options, - ). - WithContractUpdateValidation(). - WithStagedContractUpdates(stagedContracts) - - registersByAccount, err := registersForStagedContracts( - StagedContract{ - Address: common.Address(address1), - Contract: Contract{ - Name: "A", - Code: []byte(oldCodeA), - }, - }, - StagedContract{ - Address: common.Address(address2), - Contract: Contract{ - Name: "B", - Code: []byte(oldCodeB), - }, - }, - ) - require.NoError(t, err) - - err = migration.InitMigration(log, registersByAccount, 1) - require.NoError(t, err) - - owner1 := string(address1[:]) - accountRegisters1 := registersByAccount.AccountRegisters(owner1) - - err = migration.MigrateAccount( - context.Background(), - common.Address(address1), - accountRegisters1, - ) - require.NoError(t, err) - - owner2 := string(address2[:]) - accountRegisters2 := registersByAccount.AccountRegisters(owner2) - - err = migration.MigrateAccount( - context.Background(), - common.Address(address2), - accountRegisters2, - ) - require.NoError(t, err) - - err = migration.Close() - require.NoError(t, err) - - require.Len(t, logWriter.logs, 2) - assert.Contains( - t, - logWriter.logs[0], - "cannot find type in this scope: `B`", - ) - assert.Contains( - t, - logWriter.logs[1], - "`pub` is no longer a valid access keyword", - ) - - // Registers should be the old ones - require.Equal(t, 1, accountRegisters1.Count()) - assert.Equal(t, oldCodeA, contractCode(t, registersByAccount, owner1, "A")) - - require.Equal(t, 1, accountRegisters2.Count()) - assert.Equal(t, oldCodeB, contractCode(t, registersByAccount, owner2, "B")) - }) - - t.Run("broken import in one, valid third contract", func(t *testing.T) { - t.Parallel() - - oldCodeA := fmt.Sprintf(` - import B from %s - access(all) contract A {} - `, - address2.HexWithPrefix(), - ) - - oldCodeB := `pub contract B {} // not compatible` - - oldCodeC := `pub contract C {}` - - newCodeA := fmt.Sprintf(` - import B from %s - access(all) contract A { - access(all) fun foo(a: B.X) {} - } - `, - address2.HexWithPrefix(), - ) - - newCodeC := `access(all) contract C {}` - - stagedContracts := []StagedContract{ - { - Address: common.Address(address1), - Contract: Contract{ - Name: "A", - Code: []byte(newCodeA), - }, - }, - { - Address: common.Address(address1), - Contract: Contract{ - Name: "C", - Code: []byte(newCodeC), - }, - }, - } - - logWriter := &logWriter{} - log := zerolog.New(logWriter) - - rwf := &testReportWriterFactory{} - - options := StagedContractsMigrationOptions{ - ChainID: chainID, - VerboseErrorOutput: true, - } - - migration := NewStagedContractsMigration( - "test", - "test", - log, - rwf, - &LegacyTypeRequirements{}, - options, - ). - WithContractUpdateValidation(). - WithStagedContractUpdates(stagedContracts) - - registersByAccount, err := registersForStagedContracts( - StagedContract{ - Address: common.Address(address1), - Contract: Contract{ - Name: "A", - Code: []byte(oldCodeA), - }, - }, - StagedContract{ - Address: common.Address(address1), - Contract: Contract{ - Name: "C", - Code: []byte(oldCodeC), - }, - }, - StagedContract{ - Address: common.Address(address2), - Contract: Contract{ - Name: "B", - Code: []byte(oldCodeB), - }, - }, - ) - require.NoError(t, err) - - err = migration.InitMigration(log, registersByAccount, 1) - require.NoError(t, err) - - owner1 := string(address1[:]) - accountRegisters1 := registersByAccount.AccountRegisters(owner1) - - err = migration.MigrateAccount( - context.Background(), - common.Address(address1), - accountRegisters1, - ) - require.NoError(t, err) - - owner2 := string(address2[:]) - accountRegisters2 := registersByAccount.AccountRegisters(owner2) - - err = migration.MigrateAccount( - context.Background(), - common.Address(address2), - accountRegisters2, - ) - require.NoError(t, err) - - err = migration.Close() - require.NoError(t, err) - - require.Len(t, logWriter.logs, 1) - require.Contains( - t, - logWriter.logs[0], - "`pub` is no longer a valid access keyword", - ) - - // A and B should be the old ones. - // C should be updated. - // Type checking failures in unrelated contracts should not - // stop other contracts from being migrated. - require.Equal(t, 2, accountRegisters1.Count()) - require.Equal(t, oldCodeA, contractCode(t, registersByAccount, owner1, "A")) - require.Equal(t, newCodeC, contractCode(t, registersByAccount, owner1, "C")) - - require.Equal(t, 1, accountRegisters2.Count()) - require.Equal(t, oldCodeB, contractCode(t, registersByAccount, owner2, "B")) - }) -} - -func TestStagedContractsFromCSV(t *testing.T) { - - t.Parallel() - - t.Run("valid csv", func(t *testing.T) { - - t.Parallel() - - const path = "test-data/staged_contracts_migration/staged_contracts.csv" - - contracts, err := StagedContractsFromCSV(path) - require.NoError(t, err) - - require.Len(t, contracts, 4) - assert.Equal( - t, - contracts, - []StagedContract{ - { - Address: common.MustBytesToAddress([]byte{0x1}), - Contract: Contract{ - Name: "Foo", - Code: []byte("access(all) contract Foo{}"), - }, - }, - { - Address: common.MustBytesToAddress([]byte{0x1}), - Contract: Contract{ - Name: "Bar", - Code: []byte("access(all) contract Bar{}"), - }, - }, - { - Address: common.MustBytesToAddress([]byte{0x2}), - Contract: Contract{ - Name: "MultilineContract", - Code: []byte(` -import Foo from 0x01 - -access(all) -contract MultilineContract{ - init() { - var a = "hello" - } -} -`), - }, - }, - { - Address: common.MustBytesToAddress([]byte{0x2}), - Contract: Contract{ - Name: "Baz", - Code: []byte("import Foo from 0x01 access(all) contract Baz{}"), - }, - }, - }, - ) - }) - - t.Run("malformed csv", func(t *testing.T) { - - t.Parallel() - - const path = "test-data/staged_contracts_migration/staged_contracts_malformed.csv" - - contracts, err := StagedContractsFromCSV(path) - require.Error(t, err) - assert.Equal(t, "record on line 2: wrong number of fields", err.Error()) - require.Empty(t, contracts) - }) - - t.Run("too few fields", func(t *testing.T) { - - t.Parallel() - - const path = "test-data/staged_contracts_migration/too_few_fields.csv" - - contracts, err := StagedContractsFromCSV(path) - require.Error(t, err) - assert.Equal(t, "record on line 1: wrong number of fields", err.Error()) - require.Empty(t, contracts) - }) - - t.Run("empty path", func(t *testing.T) { - - t.Parallel() - - const emptyPath = "" - - contracts, err := StagedContractsFromCSV(emptyPath) - require.NoError(t, err) - require.Empty(t, contracts) - }) -} - -func TestStagedContractsWithUpdateValidator(t *testing.T) { - t.Parallel() - - const chainID = flow.Emulator - systemContracts := systemcontracts.SystemContractsForChain(chainID) - - chain := chainID.Chain() - - // Prevent conflicts with system contracts - addressA, err := chain.AddressAtIndex(1_000_000) - require.NoError(t, err) - - addressB, err := chain.AddressAtIndex(2_000_000) - require.NoError(t, err) - - t.Run("FungibleToken.Vault", func(t *testing.T) { - t.Parallel() - - ftAddress := common.Address(systemContracts.FungibleToken.Address) - - oldCodeA := fmt.Sprintf( - ` - import FungibleToken from %s - - pub contract A { - pub var vault: @FungibleToken.Vault? - init() { - self.vault <- nil - } - } - `, - ftAddress.HexWithPrefix(), - ) - - newCodeA := fmt.Sprintf( - ` - import FungibleToken from %s - - access(all) contract A { - access(all) var vault: @{FungibleToken.Vault}? - init() { - self.vault <- nil - } - } - `, - ftAddress.HexWithPrefix(), - ) - - ftContract := ` - access(all) contract FungibleToken { - access(all) resource interface Vault {} - } - ` - - stagedContracts := []StagedContract{ - { - Address: common.Address(addressA), - Contract: Contract{ - Name: "A", - Code: []byte(newCodeA), - }, - }, - { - Address: ftAddress, - Contract: Contract{ - Name: "FungibleToken", - Code: []byte(ftContract), - }, - }, - } - - logWriter := &logWriter{} - log := zerolog.New(logWriter) - - rwf := &testReportWriterFactory{} - - options := StagedContractsMigrationOptions{ - ChainID: chainID, - VerboseErrorOutput: true, - } - - migration := NewStagedContractsMigration( - "test", - "test", - log, - rwf, - &LegacyTypeRequirements{}, - options, - ). - WithContractUpdateValidation(). - WithStagedContractUpdates(stagedContracts) - - registersByAccount, err := registersForStagedContracts( - StagedContract{ - Address: common.Address(addressA), - Contract: Contract{ - Name: "A", - Code: []byte(oldCodeA), - }, - }, - StagedContract{ - Address: ftAddress, - Contract: Contract{ - Name: "FungibleToken", - Code: []byte(ftContract), - }, - }, - ) - require.NoError(t, err) - - err = migration.InitMigration(log, registersByAccount, 1) - require.NoError(t, err) - - ownerA := string(addressA[:]) - accountRegistersA := registersByAccount.AccountRegisters(ownerA) - - err = migration.MigrateAccount( - context.Background(), - common.Address(addressA), - accountRegistersA, - ) - require.NoError(t, err) - - ftOwner := string(ftAddress[:]) - ftAccountRegisters := registersByAccount.AccountRegisters(ftOwner) - - err = migration.MigrateAccount( - context.Background(), - ftAddress, - ftAccountRegisters, - ) - require.NoError(t, err) - - err = migration.Close() - require.NoError(t, err) - - require.Empty(t, logWriter.logs) - - require.Equal(t, 1, accountRegistersA.Count()) - assert.Equal(t, newCodeA, contractCode(t, registersByAccount, ownerA, "A")) - - require.Equal(t, 1, ftAccountRegisters.Count()) - assert.Equal(t, ftContract, contractCode(t, registersByAccount, ftOwner, "FungibleToken")) - }) - - t.Run("other type", func(t *testing.T) { - t.Parallel() - - otherAddress, err := common.HexToAddress("0x2") - require.NoError(t, err) - - oldCodeA := fmt.Sprintf( - ` - import FungibleToken from %s - - pub contract A { - pub var vault: @FungibleToken.Vault? - init() { - self.vault <- nil - } - } - `, - otherAddress.HexWithPrefix(), // Importing from some other address - ) - - newCodeA := fmt.Sprintf( - ` - import FungibleToken from %s - - access(all) contract A { - access(all) var vault: @{FungibleToken.Vault}? - init() { - self.vault <- nil - } - } - `, - otherAddress.HexWithPrefix(), // Importing from some other address - ) - - ftContract := ` - access(all) contract FungibleToken { - access(all) resource interface Vault {} - } - ` - - stagedContracts := []StagedContract{ - { - Address: common.Address(addressA), - Contract: Contract{ - Name: "A", - Code: []byte(newCodeA), - }, - }, - } - - logWriter := &logWriter{} - log := zerolog.New(logWriter) - - rwf := &testReportWriterFactory{} - - options := StagedContractsMigrationOptions{ - ChainID: chainID, - VerboseErrorOutput: true, - } - - migration := NewStagedContractsMigration( - "test", - "test", - log, - rwf, - &LegacyTypeRequirements{}, - options, - ). - WithContractUpdateValidation(). - WithStagedContractUpdates(stagedContracts) - - registersByAccount, err := registersForStagedContracts( - StagedContract{ - Address: common.Address(addressA), - Contract: Contract{ - Name: "A", - Code: []byte(oldCodeA), - }, - }, - StagedContract{ - Address: otherAddress, - Contract: Contract{ - Name: "FungibleToken", - Code: []byte(ftContract), - }, - }, - ) - require.NoError(t, err) - - err = migration.InitMigration(log, registersByAccount, 1) - require.NoError(t, err) - - ownerA := string(addressA[:]) - accountRegistersA := registersByAccount.AccountRegisters(ownerA) - - err = migration.MigrateAccount( - context.Background(), - common.Address(addressA), - accountRegistersA, - ) - require.NoError(t, err) - - otherOwner := string(otherAddress[:]) - otherAccountRegisters := registersByAccount.AccountRegisters(otherOwner) - - err = migration.MigrateAccount( - context.Background(), - otherAddress, - otherAccountRegisters, - ) - require.NoError(t, err) - - err = migration.Close() - require.NoError(t, err) - - require.Len(t, logWriter.logs, 1) - assert.Contains(t, - logWriter.logs[0], - "incompatible type annotations. expected `FungibleToken.Vault`, found `{FungibleToken.Vault}`", - ) - - require.Equal(t, 1, accountRegistersA.Count()) - assert.Equal(t, oldCodeA, contractCode(t, registersByAccount, ownerA, "A")) - - require.Equal(t, 1, otherAccountRegisters.Count()) - assert.Equal(t, ftContract, contractCode(t, registersByAccount, otherOwner, "FungibleToken")) - }) - - t.Run("import from other account", func(t *testing.T) { - t.Parallel() - - oldCodeA := fmt.Sprintf( - ` - import B from %s - - pub contract A {} - `, - addressB.HexWithPrefix(), - ) - - newCodeA := fmt.Sprintf( - ` - import B from %s - - access(all) contract A {} - `, - addressB.HexWithPrefix(), - ) - - codeB := ` - access(all) contract B {} - ` - - stagedContracts := []StagedContract{ - { - Address: common.Address(addressA), - Contract: Contract{ - Name: "A", - Code: []byte(newCodeA), - }, - }, - } - - logWriter := &logWriter{} - log := zerolog.New(logWriter) - - rwf := &testReportWriterFactory{} - - options := StagedContractsMigrationOptions{ - ChainID: chainID, - VerboseErrorOutput: true, - } - - migration := NewStagedContractsMigration( - "test", - "test", - log, - rwf, - &LegacyTypeRequirements{}, - options, - ). - WithContractUpdateValidation(). - WithStagedContractUpdates(stagedContracts) - - registersByAccount, err := registersForStagedContracts( - StagedContract{ - Address: common.Address(addressA), - Contract: Contract{ - Name: "A", - Code: []byte(oldCodeA), - }, - }, - StagedContract{ - Address: common.Address(addressB), - Contract: Contract{ - Name: "B", - Code: []byte(codeB), - }, - }, - ) - require.NoError(t, err) - - err = migration.InitMigration(log, registersByAccount, 1) - require.NoError(t, err) - - ownerA := string(addressA[:]) - accountRegistersA := registersByAccount.AccountRegisters(ownerA) - - err = migration.MigrateAccount( - context.Background(), - common.Address(addressA), - accountRegistersA, - ) - require.NoError(t, err) - - err = migration.Close() - require.NoError(t, err) - - require.Empty(t, logWriter.logs) - - require.Equal(t, 1, accountRegistersA.Count()) - assert.Equal(t, newCodeA, contractCode(t, registersByAccount, ownerA, "A")) - }) -} - -func TestStagedContractConformanceChanges(t *testing.T) { - t.Parallel() - - const chainID = flow.Emulator - systemContracts := systemcontracts.SystemContractsForChain(chainID) - - // Prevent conflict with system contracts - address, err := chainID.Chain().AddressAtIndex(1_000_000) - require.NoError(t, err) - - type testCase struct { - oldContract, oldInterface, newContract, newInterface string - } - - test := func(oldContract, oldInterface, newContract, newInterface string) { - - name := fmt.Sprintf( - "%s.%s to %s.%s", - oldContract, - oldInterface, - newContract, - newInterface, - ) - - t.Run(name, func(t *testing.T) { - - t.Parallel() - - metadataViewsAddress := common.Address(systemContracts.MetadataViews.Address) - viewResolverAddress := common.Address(systemContracts.ViewResolver.Address) - - oldCode := fmt.Sprintf( - ` - import %[2]s from %[1]s - - pub contract C { - pub resource A: %[2]s.%[3]s {} - } - `, - metadataViewsAddress.HexWithPrefix(), - oldContract, - oldInterface, - ) - - newCode := fmt.Sprintf( - ` - import %[2]s from %[1]s - - access(all) contract C { - access(all) resource A: %[2]s.%[3]s {} - } - `, - viewResolverAddress.HexWithPrefix(), - newContract, - newInterface, - ) - - newImportedContract := fmt.Sprintf( - ` - access(all) contract %s { - access(all) resource interface %s {} - } - `, - newContract, - newInterface, - ) - - stagedContracts := []StagedContract{ - { - Address: common.Address(address), - Contract: Contract{ - Name: "A", - Code: []byte(newCode), - }, - }, - } - - logWriter := &logWriter{} - log := zerolog.New(logWriter) - - rwf := &testReportWriterFactory{} - - options := StagedContractsMigrationOptions{ - ChainID: chainID, - VerboseErrorOutput: true, - } - - migration := NewStagedContractsMigration( - "test", - "test", - log, - rwf, - &LegacyTypeRequirements{}, - options, - ). - WithContractUpdateValidation(). - WithStagedContractUpdates(stagedContracts) - - registersByAccount, err := registersForStagedContracts( - StagedContract{ - Address: common.Address(address), - Contract: Contract{ - Name: "A", - Code: []byte(oldCode), - }, - }, - StagedContract{ - Address: viewResolverAddress, - Contract: Contract{ - Name: newContract, - Code: []byte(newImportedContract), - }, - }, - ) - require.NoError(t, err) - - err = migration.InitMigration(log, registersByAccount, 1) - require.NoError(t, err) - - owner := string(address[:]) - accountRegisters := registersByAccount.AccountRegisters(owner) - require.Equal(t, 1, accountRegisters.Count()) - - err = migration.MigrateAccount( - context.Background(), - common.Address(address), - accountRegisters, - ) - require.NoError(t, err) - - err = migration.Close() - require.NoError(t, err) - - require.Empty(t, logWriter.logs) - - require.Equal(t, 1, accountRegisters.Count()) - assert.Equal(t, newCode, contractCode(t, registersByAccount, owner, "A")) - }) - } - - testCases := []testCase{ - { - oldContract: "MetadataViews", - oldInterface: "Resolver", - newContract: "ViewResolver", - newInterface: "Resolver", - }, - { - oldContract: "MetadataViews", - oldInterface: "ResolverCollection", - newContract: "ViewResolver", - newInterface: "ResolverCollection", - }, - { - oldContract: "NonFungibleToken", - oldInterface: "INFT", - newContract: "NonFungibleToken", - newInterface: "NFT", - }, - } - - for _, testCase := range testCases { - test( - testCase.oldContract, - testCase.oldInterface, - testCase.newContract, - testCase.newInterface, - ) - } - - t.Run("MetadataViews.Resolver to ArbitraryContract.Resolver unsupported", func(t *testing.T) { - - // `MetadataViews.Resolver` shouldn't be able to replace with any arbitrary `Resolver` interface! - - t.Parallel() - - metadataViewsAddress := common.Address(systemContracts.MetadataViews.Address) - viewResolverAddress := common.Address(systemContracts.ViewResolver.Address) - - oldCode := fmt.Sprintf( - ` - import MetadataViews from %s - - pub contract C { - pub resource A: MetadataViews.Resolver {} - } - `, - metadataViewsAddress.HexWithPrefix(), - ) - - newCode := fmt.Sprintf( - ` - import ArbitraryContract from %s - - access(all) contract C { - access(all) resource A: ArbitraryContract.Resolver {} - } - `, - viewResolverAddress.HexWithPrefix(), - ) - - arbitraryContract := ` - access(all) contract ArbitraryContract { - access(all) resource interface Resolver {} - } - ` - - stagedContracts := []StagedContract{ - { - Address: common.Address(address), - Contract: Contract{ - Name: "A", - Code: []byte(newCode), - }, - }, - } - - logWriter := &logWriter{} - log := zerolog.New(logWriter) - - rwf := &testReportWriterFactory{} - - options := StagedContractsMigrationOptions{ - ChainID: chainID, - VerboseErrorOutput: true, - } - - migration := NewStagedContractsMigration( - "test", - "test", - log, - rwf, - &LegacyTypeRequirements{}, - options, - ). - WithContractUpdateValidation(). - WithStagedContractUpdates(stagedContracts) - - registersByAccount, err := registersForStagedContracts( - StagedContract{ - Address: common.Address(address), - Contract: Contract{ - Name: "A", - Code: []byte(oldCode), - }, - }, - StagedContract{ - Address: viewResolverAddress, - Contract: Contract{ - Name: "ArbitraryContract", - Code: []byte(arbitraryContract), - }, - }, - ) - require.NoError(t, err) - - err = migration.InitMigration(log, registersByAccount, 1) - require.NoError(t, err) - - owner := string(address[:]) - accountRegisters := registersByAccount.AccountRegisters(owner) - require.Equal(t, 1, accountRegisters.Count()) - - err = migration.MigrateAccount( - context.Background(), - common.Address(address), - accountRegisters, - ) - require.NoError(t, err) - - err = migration.Close() - require.NoError(t, err) - - require.Len(t, logWriter.logs, 1) - require.Contains(t, logWriter.logs[0], "conformances do not match in `A`") - - require.Equal(t, 1, accountRegisters.Count()) - assert.Equal(t, oldCode, contractCode(t, registersByAccount, owner, "A")) - }) -} - -func TestConcurrentContractUpdate(t *testing.T) { - - t.Parallel() - - const chainID = flow.Emulator - addressGenerator := chainID.Chain().NewAddressGenerator() - - addressA, err := addressGenerator.NextAddress() - require.NoError(t, err) - - addressB, err := addressGenerator.NextAddress() - require.NoError(t, err) - - addressImport, err := addressGenerator.NextAddress() - require.NoError(t, err) - - oldCodeA := fmt.Sprintf( - ` - import Foo from %[1]s - import Bar from %[1]s - import Baz from %[1]s - - pub contract A {} - `, - addressImport.HexWithPrefix(), - ) - - newCodeA := fmt.Sprintf( - ` - import Foo from %[1]s - import Baz from %[1]s - import Bar from %[1]s - - access(all) contract A { - access(all) struct AA {} - } - `, - addressImport.HexWithPrefix(), - ) - - oldCodeB := fmt.Sprintf( - ` - import Foo from %[1]s - import Bar from %[1]s - import Baz from %[1]s - - pub contract B {} - `, - addressImport.HexWithPrefix(), - ) - - newCodeB := fmt.Sprintf( - ` - import Foo from %[1]s - import Baz from %[1]s - import Bar from %[1]s - - access(all) contract B { - access(all) struct BB {} - } - `, - addressImport.HexWithPrefix(), - ) - - codeFoo := `access(all) contract Foo {}` - codeBar := `access(all) contract Bar {}` - codeBaz := `access(all) contract Baz {}` - - stagedContracts := []StagedContract{ - { - Address: common.Address(addressA), - Contract: Contract{ - Name: "A", - Code: []byte(newCodeA), - }, - }, - { - Address: common.Address(addressB), - Contract: Contract{ - Name: "B", - Code: []byte(newCodeB), - }, - }, - } - - registersByAccount, err := registersForStagedContracts( - StagedContract{ - Address: common.Address(addressA), - Contract: Contract{ - Name: "A", - Code: []byte(oldCodeA), - }, - }, - StagedContract{ - Address: common.Address(addressB), - Contract: Contract{ - Name: "B", - Code: []byte(oldCodeB), - }, - }, - StagedContract{ - Address: common.Address(addressImport), - Contract: Contract{ - Name: "Foo", - Code: []byte(codeFoo), - }, - }, - StagedContract{ - Address: common.Address(addressImport), - Contract: Contract{ - Name: "Bar", - Code: []byte(codeBar), - }, - }, - StagedContract{ - Address: common.Address(addressImport), - Contract: Contract{ - Name: "Baz", - Code: []byte(codeBaz), - }, - }, - ) - require.NoError(t, err) - - rwf := &testReportWriterFactory{} - - logWriter := &logWriter{} - logger := zerolog.New(logWriter).Level(zerolog.ErrorLevel) - - // NOTE: Run with multiple workers (>2) - const nWorker = 2 - - const evmContractChange = EVMContractChangeNone - const burnerContractChange = BurnerContractChangeNone - - migrations := NewCadence1Migrations( - logger, - t.TempDir(), - rwf, - Options{ - NWorker: nWorker, - ChainID: chainID, - EVMContractChange: evmContractChange, - BurnerContractChange: burnerContractChange, - StagedContracts: stagedContracts, - VerboseErrorOutput: true, - }, - ) - - for _, migration := range migrations { - // only run the staged contracts migration - if migration.Name != stagedContractUpdateMigrationName { - continue - } - - err = migration.Migrate(registersByAccount) - require.NoError( - t, - err, - "migration `%s` failed, logs: %v", - migration.Name, - logWriter.logs, - ) - } - - // No errors. - require.Empty(t, logWriter.logs) - - require.NoError(t, err) - require.Equal(t, 5, registersByAccount.Count()) -} - -func TestStagedContractsUpdateValidationErrors(t *testing.T) { - t.Parallel() - - const chainID = flow.Emulator - systemContracts := systemcontracts.SystemContractsForChain(chainID) - - // Prevent conflict with system contracts - address, err := chainID.Chain().AddressAtIndex(1_000_000) - require.NoError(t, err) - - t.Run("field mismatch", func(t *testing.T) { - t.Parallel() - - oldCodeA := ` - access(all) contract Test { - access(all) var a: Int - init() { - self.a = 0 - } - } - ` - - newCodeA := ` - access(all) contract Test { - access(all) var a: String - init() { - self.a = "hello" - } - } - ` - - stagedContracts := []StagedContract{ - { - Address: common.Address(address), - Contract: Contract{ - Name: "A", - Code: []byte(newCodeA), - }, - }, - } - - logWriter := &logWriter{} - log := zerolog.New(logWriter) - - rwf := &testReportWriterFactory{} - - options := StagedContractsMigrationOptions{ - ChainID: chainID, - VerboseErrorOutput: true, - } - - migration := NewStagedContractsMigration( - "test", - "test", - log, - rwf, - &LegacyTypeRequirements{}, - options, - ). - WithContractUpdateValidation(). - WithStagedContractUpdates(stagedContracts) - - registersByAccount, err := registersForStagedContracts( - StagedContract{ - Address: common.Address(address), - Contract: Contract{ - Name: "A", - Code: []byte(oldCodeA), - }, - }, - ) - require.NoError(t, err) - - err = migration.InitMigration(log, registersByAccount, 1) - require.NoError(t, err) - - owner := string(address[:]) - accountRegisters := registersByAccount.AccountRegisters(owner) - require.Equal(t, 1, accountRegisters.Count()) - - err = migration.MigrateAccount( - context.Background(), - common.Address(address), - accountRegisters, - ) - require.NoError(t, err) - - err = migration.Close() - require.NoError(t, err) - - require.Len(t, logWriter.logs, 1) - - var jsonObject map[string]any - - err = json.Unmarshal([]byte(logWriter.logs[0]), &jsonObject) - require.NoError(t, err) - - assert.Equal( - t, - "failed to update contract A in account 0x9bc576e17b370b16: error: mismatching field `a` in `Test`\n"+ - " --> 9bc576e17b370b16.A:3:33\n"+ - " |\n"+ - "3 | access(all) var a: String\n"+ - " | ^^^^^^ incompatible type annotations. expected `Int`, found `String`\n", - jsonObject["message"], - ) - }) - - t.Run("field mismatch with entitlements", func(t *testing.T) { - t.Parallel() - - nftAddress := common.Address(systemContracts.NonFungibleToken.Address) - - oldCodeA := fmt.Sprintf( - ` - import NonFungibleToken from %s - - access(all) contract Test { - access(all) var a: Capability<&{NonFungibleToken.Provider}>? - init() { - self.a = nil - } - } - `, - nftAddress.HexWithPrefix(), - ) - - newCodeA := fmt.Sprintf( - ` - import NonFungibleToken from %s - - access(all) contract Test { - access(all) var a: Capability? - init() { - self.a = nil - } - } - `, - nftAddress.HexWithPrefix(), - ) - - nftContract := ` - access(all) contract NonFungibleToken { - - access(all) entitlement E1 - access(all) entitlement E2 - - access(all) resource interface Provider { - access(E1) fun foo() - access(E2) fun bar() - } - } - ` - - stagedContracts := []StagedContract{ - { - Address: common.Address(address), - Contract: Contract{ - Name: "A", - Code: []byte(newCodeA), - }, - }, - } - - logWriter := &logWriter{} - log := zerolog.New(logWriter) - - rwf := &testReportWriterFactory{} - - options := StagedContractsMigrationOptions{ - ChainID: chainID, - VerboseErrorOutput: true, - } - - migration := NewStagedContractsMigration( - "test", - "test", - log, - rwf, - &LegacyTypeRequirements{}, - options, - ). - WithContractUpdateValidation(). - WithStagedContractUpdates(stagedContracts) - - registersByAccount, err := registersForStagedContracts( - StagedContract{ - Address: common.Address(address), - Contract: Contract{ - Name: "A", - Code: []byte(oldCodeA), - }, - }, - StagedContract{ - Address: nftAddress, - Contract: Contract{ - Name: "NonFungibleToken", - Code: []byte(nftContract), - }, - }, - ) - require.NoError(t, err) - - err = migration.InitMigration(log, registersByAccount, 1) - require.NoError(t, err) - - owner := string(address[:]) - accountRegisters := registersByAccount.AccountRegisters(owner) - require.Equal(t, 1, accountRegisters.Count()) - - err = migration.MigrateAccount( - context.Background(), - common.Address(address), - accountRegisters, - ) - require.NoError(t, err) - - require.Len(t, logWriter.logs, 1) - - var jsonObject map[string]any - err = json.Unmarshal([]byte(logWriter.logs[0]), &jsonObject) - require.NoError(t, err) - - assert.Equal( - t, - "failed to update contract A in account 0x9bc576e17b370b16: error: mismatching field `a` in `Test`\n"+ - " --> 9bc576e17b370b16.A:5:37\n"+ - " |\n"+ - "5 | access(all) var a: Capability?\n"+ - " | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ mismatching authorization:"+ - " the entitlements migration would only grant this value `NonFungibleToken.E1, NonFungibleToken.E2`, but the annotation present is `NonFungibleToken.E1`\n", - jsonObject["message"], - ) - }) -} - -func TestContractUpdateEntry_MarshalJSON(t *testing.T) { - - t.Parallel() - - e := contractUpdateEntry{ - AccountAddress: common.MustBytesToAddress([]byte{0x1}), - ContractName: "Test", - } - - actual, err := e.MarshalJSON() - require.NoError(t, err) - - require.JSONEq(t, - //language=JSON - `{ - "kind": "contract-update-success", - "account_address": "0x0000000000000001", - "contract_name": "Test" - }`, - string(actual), - ) -} - -func TestContractUpdateFailureEntry_MarshalJSON(t *testing.T) { - - t.Parallel() - - e := contractUpdateFailureEntry{ - AccountAddress: common.MustBytesToAddress([]byte{0x1}), - ContractName: "Test", - Error: "unknown", - } - - actual, err := e.MarshalJSON() - require.NoError(t, err) - - require.JSONEq(t, - //language=JSON - `{ - "kind": "contract-update-failure", - "account_address": "0x0000000000000001", - "contract_name": "Test", - "error": "unknown" - }`, - string(actual), - ) -} - -func TestTypeRequirementRemoval(t *testing.T) { - t.Parallel() - - const chainID = flow.Testnet - - t.Run("TiblesProducer contract", func(t *testing.T) { - t.Parallel() - - tiblesAddress := mustHexToAddress("e93c412c964bdf40") - - oldCode := ` - pub contract interface TiblesProducer { - - pub struct ContentLocation {} - pub struct interface IContentLocation {} - - pub resource interface IContent { - access(contract) let contentIdsToPaths: {String: TiblesProducer.ContentLocation} - pub fun getMetadata(contentId: String): {String: AnyStruct}? - } - - pub resource interface IProducer { - access(contract) let minters: @{String: Minter} - } - - pub resource Producer: IContent, IProducer { - access(contract) let minters: @{String: Minter} - } - - pub resource interface IMinter { - pub let id: String - pub var lastMintNumber: UInt32 - pub let contentCapability: Capability - pub fun mintNext() - } - - pub resource Minter: IMinter { - pub let id: String - pub var lastMintNumber: UInt32 - pub let contentCapability: Capability - pub fun mintNext() - } - } - ` - - newCode := ` - access(all) contract interface TiblesProducer { - - access(all) struct interface ContentLocation {} - access(all) struct interface IContentLocation {} - - access(all) resource interface IContent { - access(contract) let contentIdsToPaths: {String: {TiblesProducer.ContentLocation}} - access(all) fun getMetadata(contentId: String): {String: AnyStruct}? - } - - access(all) resource interface IProducer { - access(contract) let minters: @{String: {Minter}} - } - - access(all) resource interface Producer: IContent, IProducer { - access(contract) let minters: @{String: {Minter}} - } - - access(all) resource interface IMinter { - access(all) let id: String - access(all) var lastMintNumber: UInt32 - access(all) let contentCapability: Capability - access(all) fun mintNext() - } - - access(all) resource interface Minter: IMinter { - access(all) let id: String - access(all) var lastMintNumber: UInt32 - access(all) let contentCapability: Capability - access(all) fun mintNext() - } - } - ` - - contractName := "TiblesProducer" - - stagedContracts := []StagedContract{ - { - Address: tiblesAddress, - Contract: Contract{ - Name: contractName, - Code: []byte(newCode), - }, - }, - } - - logWriter := &logWriter{} - log := zerolog.New(logWriter) - - rwf := &testReportWriterFactory{} - - options := StagedContractsMigrationOptions{ - ChainID: chainID, - VerboseErrorOutput: true, - } - - registersByAccount, err := registersForStagedContracts( - StagedContract{ - Address: tiblesAddress, - Contract: Contract{ - Name: contractName, - Code: []byte(oldCode), - }, - }, - ) - require.NoError(t, err) - - encodedContractNames, err := environment.EncodeContractNames([]string{contractName}) - require.NoError(t, err) - - err = registersByAccount.Set( - string(tiblesAddress[:]), - flow.ContractNamesKey, - encodedContractNames, - ) - require.NoError(t, err) - - // Run type-requirement extractor - - legacyTypeRequirements := &LegacyTypeRequirements{} - - cadenceTypeRequirementsExtractor := NewTypeRequirementsExtractingMigration( - log, - rwf, - nil, - legacyTypeRequirements, - ) - err = cadenceTypeRequirementsExtractor(registersByAccount) - require.NoError(t, err) - - require.Equal( - t, - []TypeRequirement{ - { - Address: tiblesAddress, - ContractName: contractName, - TypeName: "ContentLocation", - }, - { - Address: tiblesAddress, - ContractName: contractName, - TypeName: "Producer", - }, - { - Address: tiblesAddress, - ContractName: contractName, - TypeName: "Minter", - }, - }, - legacyTypeRequirements.typeRequirements, - ) - - // Run staged contract migration - - migration := NewStagedContractsMigration( - "test", - "test", - log, - rwf, - legacyTypeRequirements, - options, - ). - WithStagedContractUpdates(stagedContracts). - WithContractUpdateValidation() - - err = migration.InitMigration(log, registersByAccount, 1) - require.NoError(t, err) - - owner := string(tiblesAddress[:]) - accountRegisters := registersByAccount.AccountRegisters(owner) - - err = migration.MigrateAccount( - context.Background(), - tiblesAddress, - accountRegisters, - ) - require.NoError(t, err) - - err = migration.Close() - require.NoError(t, err) - - require.Empty(t, logWriter.logs) - - require.Equal(t, 2, accountRegisters.Count()) - assert.Equal(t, newCode, contractCode(t, registersByAccount, owner, contractName)) - }) - - t.Run("random contract", func(t *testing.T) { - t.Parallel() - - addressGenerator := chainID.Chain().NewAddressGenerator() - randomAddress, err := addressGenerator.NextAddress() - require.NoError(t, err) - - oldCode := ` - pub contract interface Foo { - - pub struct ContentLocation {} - pub struct interface IContentLocation {} - - pub resource interface IContent { - access(contract) let contentIdsToPaths: {String: Foo.ContentLocation} - pub fun getMetadata(contentId: String): {String: AnyStruct}? - } - - pub resource interface IProducer { - access(contract) let minters: @{String: Minter} - } - - pub resource Producer: IContent, IProducer { - access(contract) let minters: @{String: Minter} - } - - pub resource interface IMinter { - pub let id: String - pub var lastMintNumber: UInt32 - pub let contentCapability: Capability - pub fun mintNext() - } - - pub resource Minter: IMinter { - pub let id: String - pub var lastMintNumber: UInt32 - pub let contentCapability: Capability - pub fun mintNext() - } - } - ` - - newCode := ` - access(all) contract interface Foo { - - access(all) struct interface ContentLocation {} - access(all) struct interface IContentLocation {} - - access(all) resource interface IContent { - access(contract) let contentIdsToPaths: {String: {Foo.ContentLocation}} - access(all) fun getMetadata(contentId: String): {String: AnyStruct}? - } - - access(all) resource interface IProducer { - access(contract) let minters: @{String: {Minter}} - } - - access(all) resource interface Producer: IContent, IProducer { - access(contract) let minters: @{String: {Minter}} - } - - access(all) resource interface IMinter { - access(all) let id: String - access(all) var lastMintNumber: UInt32 - access(all) let contentCapability: Capability - access(all) fun mintNext() - } - - access(all) resource interface Minter: IMinter { - access(all) let id: String - access(all) var lastMintNumber: UInt32 - access(all) let contentCapability: Capability - access(all) fun mintNext() - } - } - ` - - fooContractName := "Foo" - - stagedContracts := []StagedContract{ - { - Address: common.Address(randomAddress), - Contract: Contract{ - Name: fooContractName, - Code: []byte(newCode), - }, - }, - } - - logWriter := &logWriter{} - log := zerolog.New(logWriter) - - rwf := &testReportWriterFactory{} - - options := StagedContractsMigrationOptions{ - ChainID: chainID, - VerboseErrorOutput: true, - } - - registersByAccount, err := registersForStagedContracts( - StagedContract{ - Address: common.Address(randomAddress), - Contract: Contract{ - Name: fooContractName, - Code: []byte(oldCode), - }, - }, - ) - require.NoError(t, err) - - encodedContractNames, err := environment.EncodeContractNames([]string{fooContractName}) - require.NoError(t, err) - - err = registersByAccount.Set( - string(randomAddress[:]), - flow.ContractNamesKey, - encodedContractNames, - ) - require.NoError(t, err) - - // Run type-requirement extractor - - legacyTypeRequirements := &LegacyTypeRequirements{} - - cadenceTypeRequirementsExtractor := NewTypeRequirementsExtractingMigration( - log, - rwf, - nil, - legacyTypeRequirements, - ) - err = cadenceTypeRequirementsExtractor(registersByAccount) - require.NoError(t, err) - - require.Equal( - t, - []TypeRequirement{ - { - Address: common.Address(randomAddress), - ContractName: fooContractName, - TypeName: "ContentLocation", - }, - { - Address: common.Address(randomAddress), - ContractName: fooContractName, - TypeName: "Producer", - }, - { - Address: common.Address(randomAddress), - ContractName: fooContractName, - TypeName: "Minter", - }, - }, - legacyTypeRequirements.typeRequirements, - ) - - // Run staged contract migration - - migration := NewStagedContractsMigration( - "test", - "test", - log, - rwf, - legacyTypeRequirements, - options, - ). - WithStagedContractUpdates(stagedContracts). - WithContractUpdateValidation() - - err = migration.InitMigration(log, registersByAccount, 1) - require.NoError(t, err) - - owner := string(randomAddress[:]) - accountRegisters := registersByAccount.AccountRegisters(owner) - - err = migration.MigrateAccount( - context.Background(), - common.Address(randomAddress), - accountRegisters, - ) - require.NoError(t, err) - - err = migration.Close() - require.NoError(t, err) - - require.Empty(t, logWriter.logs) - - require.Equal(t, 2, accountRegisters.Count()) - assert.Equal(t, newCode, contractCode(t, registersByAccount, owner, fooContractName)) - }) -} diff --git a/cmd/util/ledger/migrations/static_type_migration.go b/cmd/util/ledger/migrations/static_type_migration.go deleted file mode 100644 index c34164cd755..00000000000 --- a/cmd/util/ledger/migrations/static_type_migration.go +++ /dev/null @@ -1,38 +0,0 @@ -package migrations - -import ( - "github.com/onflow/cadence/runtime/common" - "github.com/onflow/cadence/runtime/interpreter" -) - -type StaticTypeMigrationRules map[common.TypeID]interpreter.StaticType - -// NewStaticTypeMigration returns a type converter function. -// Accepts a `rulesGetter` which return -// This is because this constructor is called at the time of constructing the migrations (e.g: Cadence value migration), -// but the rules can only be finalized after running previous TypeRequirementsExtractingMigration migration. -// i.e: the LegacyTypeRequirements list used by NewCompositeTypeConversionRules is lazily populated. -// So we need to delay the construction of the rules, until after the execution of previous migration. -func NewStaticTypeMigration[T interpreter.StaticType]( - rulesGetter func() StaticTypeMigrationRules, -) func(staticType T) interpreter.StaticType { - - var rules StaticTypeMigrationRules - - return func(original T) interpreter.StaticType { - // Initialize only once - if rules == nil { - rules = rulesGetter() - } - - // Returning `nil` form the callback indicates the type wasn't converted. - if rules == nil { - return nil - } - - if replacement, ok := rules[original.ID()]; ok { - return replacement - } - return nil - } -} diff --git a/cmd/util/ledger/migrations/storage_used_migration.go b/cmd/util/ledger/migrations/storage_used_migration.go index cf0c710b5a4..d5a09e683cf 100644 --- a/cmd/util/ledger/migrations/storage_used_migration.go +++ b/cmd/util/ledger/migrations/storage_used_migration.go @@ -6,7 +6,7 @@ import ( "github.com/onflow/flow-go/cmd/util/ledger/reporters" - "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/common" "github.com/rs/zerolog" "github.com/rs/zerolog/log" diff --git a/cmd/util/ledger/migrations/storage_used_migration_test.go b/cmd/util/ledger/migrations/storage_used_migration_test.go index 46ea0c48b82..b0196dc96b8 100644 --- a/cmd/util/ledger/migrations/storage_used_migration_test.go +++ b/cmd/util/ledger/migrations/storage_used_migration_test.go @@ -5,7 +5,7 @@ import ( "testing" "github.com/onflow/atree" - "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/common" "github.com/rs/zerolog" "github.com/stretchr/testify/require" diff --git a/cmd/util/ledger/migrations/type_requirements_extractor.go b/cmd/util/ledger/migrations/type_requirements_extractor.go deleted file mode 100644 index 9fcfe70396d..00000000000 --- a/cmd/util/ledger/migrations/type_requirements_extractor.go +++ /dev/null @@ -1,135 +0,0 @@ -package migrations - -import ( - "encoding/json" - - "github.com/rs/zerolog" - - "github.com/onflow/cadence/runtime/common" - "github.com/onflow/cadence/runtime/old_parser" - - "github.com/onflow/flow-go/cmd/util/ledger/reporters" - "github.com/onflow/flow-go/cmd/util/ledger/util/registers" -) - -const typeRequirementExtractingReporterName = "type-requirements-extracting" - -type LegacyTypeRequirements struct { - typeRequirements []TypeRequirement -} - -type TypeRequirement struct { - Address common.Address - ContractName string - TypeName string -} - -func NewTypeRequirementsExtractingMigration( - log zerolog.Logger, - rwf reporters.ReportWriterFactory, - importantLocations map[common.AddressLocation]struct{}, - legacyTypeRequirements *LegacyTypeRequirements, -) RegistersMigration { - return func(registersByAccount *registers.ByAccount) error { - - reporter := rwf.ReportWriter(typeRequirementExtractingReporterName) - defer reporter.Close() - - // Gather all contracts - - contracts, err := gatherContractsFromRegisters(registersByAccount, log) - if err != nil { - return err - } - - // Extract type requirements from all contracts - - for _, contract := range contracts { - if _, isSystemContract := importantLocations[contract.Location]; isSystemContract { - // System contracts have their own type-changing rules. - // So do not add them here. - continue - } - - ExtractTypeRequirements( - contract, - log, - reporter, - legacyTypeRequirements, - ) - } - - return nil - } -} - -func ExtractTypeRequirements( - contract AddressContract, - log zerolog.Logger, - reporter reporters.ReportWriter, - legacyTypeRequirements *LegacyTypeRequirements, -) { - - // must be parsed with the old parser. - program, err := old_parser.ParseProgram( - nil, - contract.Code, - old_parser.Config{}, - ) - - if err != nil { - // If the old contract cannot be parsed, then ignore - return - } - - contractInterface := program.SoleContractInterfaceDeclaration() - if contractInterface == nil { - // Type requirements can only reside inside contract interfaces. - // Ignore all other programs. - return - } - - for _, composites := range contractInterface.DeclarationMembers().Composites() { - typeRequirement := TypeRequirement{ - Address: contract.Location.Address, - ContractName: contractInterface.Identifier.Identifier, - TypeName: composites.Identifier.Identifier, - } - - legacyTypeRequirements.typeRequirements = append(legacyTypeRequirements.typeRequirements, typeRequirement) - - reporter.Write(typeRequirementRemovalEntry{ - TypeRequirement: typeRequirement, - }) - } - - log.Info().Msgf("Collected %d type-requirements", len(legacyTypeRequirements.typeRequirements)) -} - -// cadenceValueMigrationFailureEntry - -type typeRequirementRemovalEntry struct { - TypeRequirement -} - -var _ valueMigrationReportEntry = typeRequirementRemovalEntry{} - -func (e typeRequirementRemovalEntry) accountAddress() common.Address { - return e.Address -} - -var _ json.Marshaler = typeRequirementRemovalEntry{} - -func (e typeRequirementRemovalEntry) MarshalJSON() ([]byte, error) { - return json.Marshal(struct { - Kind string `json:"kind"` - AccountAddress string `json:"account_address"` - ContractName string `json:"contract_name"` - TypeName string `json:"type_name"` - }{ - Kind: "cadence-type-requirement-remove", - AccountAddress: e.Address.HexWithPrefix(), - ContractName: e.ContractName, - TypeName: e.TypeName, - }) -} diff --git a/cmd/util/ledger/migrations/utils.go b/cmd/util/ledger/migrations/utils.go index d719a2fbff8..2bbf667b2e7 100644 --- a/cmd/util/ledger/migrations/utils.go +++ b/cmd/util/ledger/migrations/utils.go @@ -1,15 +1,20 @@ package migrations import ( + "github.com/onflow/cadence/common" "github.com/onflow/cadence/runtime" - "github.com/onflow/cadence/runtime/common" - "github.com/onflow/cadence/runtime/stdlib" + "github.com/onflow/cadence/stdlib" "github.com/onflow/flow-go/cmd/util/ledger/util/registers" ) type RegistersMigration func(registersByAccount *registers.ByAccount) error +type NamedMigration struct { + Name string + Migrate RegistersMigration +} + var AllStorageMapDomains = []string{ common.PathDomainStorage.Identifier(), common.PathDomainPrivate.Identifier(), diff --git a/cmd/util/ledger/reporters/account_reporter.go b/cmd/util/ledger/reporters/account_reporter.go index cc119a92b4e..859bb32ca83 100644 --- a/cmd/util/ledger/reporters/account_reporter.go +++ b/cmd/util/ledger/reporters/account_reporter.go @@ -9,8 +9,8 @@ import ( "github.com/schollz/progressbar/v3" "github.com/onflow/cadence" + "github.com/onflow/cadence/common" jsoncdc "github.com/onflow/cadence/encoding/json" - "github.com/onflow/cadence/runtime/common" "github.com/onflow/flow-go/fvm" "github.com/onflow/flow-go/fvm/environment" diff --git a/cmd/util/ledger/reporters/fungible_token_tracker.go b/cmd/util/ledger/reporters/fungible_token_tracker.go index 175c533e358..5e1fe270f43 100644 --- a/cmd/util/ledger/reporters/fungible_token_tracker.go +++ b/cmd/util/ledger/reporters/fungible_token_tracker.go @@ -9,9 +9,9 @@ import ( "github.com/rs/zerolog" "github.com/schollz/progressbar/v3" + "github.com/onflow/cadence/common" + "github.com/onflow/cadence/interpreter" cadenceRuntime "github.com/onflow/cadence/runtime" - "github.com/onflow/cadence/runtime/common" - "github.com/onflow/cadence/runtime/interpreter" "github.com/onflow/flow-go/cmd/util/ledger/util" "github.com/onflow/flow-go/fvm/environment" diff --git a/cmd/util/ledger/util/atree_util.go b/cmd/util/ledger/util/atree_util.go index 2b3b2195029..73fd30812e3 100644 --- a/cmd/util/ledger/util/atree_util.go +++ b/cmd/util/ledger/util/atree_util.go @@ -5,8 +5,8 @@ import ( "github.com/onflow/atree" + "github.com/onflow/cadence/common" "github.com/onflow/cadence/runtime" - "github.com/onflow/cadence/runtime/common" "github.com/onflow/flow-go/cmd/util/ledger/util/registers" "github.com/onflow/flow-go/ledger" diff --git a/cmd/util/ledger/util/migration_runtime_interface.go b/cmd/util/ledger/util/migration_runtime_interface.go index 8a43d7cff3b..12142eb15b4 100644 --- a/cmd/util/ledger/util/migration_runtime_interface.go +++ b/cmd/util/ledger/util/migration_runtime_interface.go @@ -4,10 +4,10 @@ import ( "errors" "fmt" + "github.com/onflow/cadence/ast" + "github.com/onflow/cadence/common" + "github.com/onflow/cadence/interpreter" "github.com/onflow/cadence/runtime" - "github.com/onflow/cadence/runtime/ast" - "github.com/onflow/cadence/runtime/common" - "github.com/onflow/cadence/runtime/interpreter" "github.com/onflow/flow-go/fvm/environment" "github.com/onflow/flow-go/fvm/storage/derived" @@ -38,6 +38,7 @@ type GerOrLoadProgramListenerFunc func( type MigrationRuntimeInterface struct { runtime.EmptyRuntimeInterface chainID flow.ChainID + CryptoContractAddress common.Address GetContractCodeFunc GetContractCodeFunc GetContractNamesFunc GetContractNamesFunc GetOrLoadProgramFunc GetOrLoadProgramFunc @@ -48,6 +49,7 @@ var _ runtime.Interface = &MigrationRuntimeInterface{} func NewMigrationRuntimeInterface( chainID flow.ChainID, + cryptoContractAddress common.Address, getCodeFunc GetContractCodeFunc, getContractNamesFunc GetContractNamesFunc, getOrLoadProgramFunc GetOrLoadProgramFunc, @@ -55,6 +57,7 @@ func NewMigrationRuntimeInterface( ) *MigrationRuntimeInterface { return &MigrationRuntimeInterface{ chainID: chainID, + CryptoContractAddress: cryptoContractAddress, GetContractCodeFunc: getCodeFunc, GetContractNamesFunc: getContractNamesFunc, GetOrLoadProgramFunc: getOrLoadProgramFunc, @@ -67,65 +70,12 @@ func (m *MigrationRuntimeInterface) ResolveLocation( location runtime.Location, ) ([]runtime.ResolvedLocation, error) { - addressLocation, isAddress := location.(common.AddressLocation) - - // if the location is not an address location, e.g. an identifier location (`import Crypto`), - // then return a single resolved location which declares all identifiers. - if !isAddress { - return []runtime.ResolvedLocation{ - { - Location: location, - Identifiers: identifiers, - }, - }, nil - } - - // if the location is an address, - // and no specific identifiers where requested in the import statement, - // then fetch all identifiers at this address - if len(identifiers) == 0 { - address := flow.Address(addressLocation.Address) - - getContractNames := m.GetContractNamesFunc - if getContractNames == nil { - return nil, errors.New("GetContractNamesFunc missing") - } - - contractNames, err := getContractNames(address) - if err != nil { - return nil, fmt.Errorf("ResolveLocation failed: %w", err) - } - - // if there are no contractNames deployed, - // then return no resolved locations - if len(contractNames) == 0 { - return nil, nil - } - - identifiers = make([]runtime.Identifier, len(contractNames)) - - for i := range identifiers { - identifiers[i] = runtime.Identifier{ - Identifier: contractNames[i], - } - } - } - - // return one resolved location per identifier. - // each resolved location is an address contract location - resolvedLocations := make([]runtime.ResolvedLocation, len(identifiers)) - for i := range resolvedLocations { - identifier := identifiers[i] - resolvedLocations[i] = runtime.ResolvedLocation{ - Location: common.AddressLocation{ - Address: addressLocation.Address, - Name: identifier.Identifier, - }, - Identifiers: []runtime.Identifier{identifier}, - } - } - - return resolvedLocations, nil + return environment.ResolveLocation( + identifiers, + location, + m.GetContractNamesFunc, + m.CryptoContractAddress, + ) } func (m *MigrationRuntimeInterface) GetCode(location runtime.Location) ([]byte, error) { diff --git a/cmd/util/ledger/util/nop_meter.go b/cmd/util/ledger/util/nop_meter.go index dc8a9c2ac6b..9541d1acfdb 100644 --- a/cmd/util/ledger/util/nop_meter.go +++ b/cmd/util/ledger/util/nop_meter.go @@ -1,7 +1,7 @@ package util import ( - "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/common" "github.com/onflow/flow-go/fvm/environment" "github.com/onflow/flow-go/fvm/meter" diff --git a/cmd/util/ledger/util/payload_file_test.go b/cmd/util/ledger/util/payload_file_test.go index 26b2092a623..84306ecc3e6 100644 --- a/cmd/util/ledger/util/payload_file_test.go +++ b/cmd/util/ledger/util/payload_file_test.go @@ -9,7 +9,7 @@ import ( "github.com/rs/zerolog" "github.com/stretchr/testify/require" - "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/common" "github.com/onflow/flow-go/cmd/util/ledger/util" "github.com/onflow/flow-go/ledger" diff --git a/cmd/util/ledger/util/programs.go b/cmd/util/ledger/util/programs.go index 3dc80dc03a1..a6a14f3f0c1 100644 --- a/cmd/util/ledger/util/programs.go +++ b/cmd/util/ledger/util/programs.go @@ -3,9 +3,9 @@ package util import ( "fmt" + "github.com/onflow/cadence/common" + "github.com/onflow/cadence/interpreter" "github.com/onflow/cadence/runtime" - "github.com/onflow/cadence/runtime/common" - "github.com/onflow/cadence/runtime/interpreter" "github.com/onflow/flow-go/fvm/environment" "github.com/onflow/flow-go/fvm/storage" diff --git a/cmd/util/ledger/util/state.go b/cmd/util/ledger/util/state.go index 9e56a461985..049573a2362 100644 --- a/cmd/util/ledger/util/state.go +++ b/cmd/util/ledger/util/state.go @@ -11,6 +11,7 @@ import ( "github.com/onflow/flow-go/ledger" "github.com/onflow/flow-go/ledger/common/pathfinder" "github.com/onflow/flow-go/ledger/complete" + mtrie "github.com/onflow/flow-go/ledger/complete/mtrie/trie" "github.com/onflow/flow-go/ledger/complete/wal" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module/metrics" @@ -78,10 +79,17 @@ func ReadTrie(dir string, targetHash flow.StateCommitment) ([]*ledger.Payload, e trie, err := led.Trie(ledger.RootHash(state)) if err != nil { - s, _ := led.MostRecentTouchedState() - log.Info(). - Str("hash", s.String()). - Msgf("Most recently touched state") + s, err2 := led.MostRecentTouchedState() + if err2 != nil { + log.Error().Err(err2). + Msgf("cannot get most recently touched state in %v, check the --execution-state-dir flag", dir) + } else if s == ledger.State(mtrie.NewEmptyMTrie().RootHash()) { + log.Error().Msgf("cannot find any trie in folder %v. check the --execution-state-dir flag", dir) + } else { + log.Info(). + Str("hash", s.String()). + Msgf("Most recently touched state") + } return nil, fmt.Errorf("cannot get trie at the given state commitment: %w", err) } diff --git a/cmd/util/ledger/util/util.go b/cmd/util/ledger/util/util.go index 148aae93432..14af2ae3b29 100644 --- a/cmd/util/ledger/util/util.go +++ b/cmd/util/ledger/util/util.go @@ -9,9 +9,9 @@ import ( "strings" "github.com/onflow/atree" + "github.com/onflow/cadence/common" "github.com/onflow/cadence/runtime" - "github.com/onflow/cadence/runtime/common" - "github.com/onflow/cadence/runtime/stdlib" + "github.com/onflow/cadence/stdlib" "github.com/onflow/flow-go/fvm/environment" "github.com/onflow/flow-go/ledger" diff --git a/cmd/utils.go b/cmd/utils.go index a3464bceb7b..536483cabc2 100644 --- a/cmd/utils.go +++ b/cmd/utils.go @@ -157,7 +157,7 @@ func CreatePublicIDTranslatorAndIdentifierProvider( if flowID, err := idTranslator.GetFlowID(pid); err != nil { // TODO: this is an instance of "log error and continue with best effort" anti-pattern - logger.Err(err).Str("peer", p2plogging.PeerId(pid)).Msg("failed to translate to Flow ID") + logger.Debug().Str("peer", p2plogging.PeerId(pid)).Msg("failed to translate to Flow ID") } else { result = append(result, flowID) } diff --git a/config/default-config.yml b/config/default-config.yml index 2e8422b4529..0d338492ab9 100644 --- a/config/default-config.yml +++ b/config/default-config.yml @@ -628,6 +628,14 @@ network-config: # keep the entire network's size. Otherwise, the local node's view of the network will be incomplete due to cache eviction. # Recommended size is 10x the number of peers in the network. cache-size: 10000 + # Enables or disables the libp2p peer gater. + peer-gater-enabled: false + # The per IP decay for all counters tracked by the peer gater for a peer. + peer-gater-source-decay: 10m + # The priority topic delivery weights. + peer-gater-topic-delivery-weights-override: | + consensus-committee: 1.5, sync-committee: .75 + # Application layer spam prevention alsp-spam-record-cache-size: 1000 alsp-spam-report-queue-size: 10_000 diff --git a/consensus/hotstuff/block_producer.go b/consensus/hotstuff/block_producer.go index 0721380f51f..eda63ae1bd0 100644 --- a/consensus/hotstuff/block_producer.go +++ b/consensus/hotstuff/block_producer.go @@ -4,9 +4,18 @@ import ( "github.com/onflow/flow-go/model/flow" ) -// BlockProducer builds a new block proposal by building a new block payload with the builder module, -// and uses VoteCollectorFactory to create a disposable VoteCollector for producing the proposal vote. -// BlockProducer assembles the new block proposal using the block payload, block header and the proposal vote. +// BlockProducer is responsible for producing new block proposals. It is a service component to HotStuff's +// main state machine (implemented in the EventHandler). The BlockProducer's central purpose is to mediate +// concurrent signing requests to its embedded `hotstuff.SafetyRules` during block production. The actual +// work of producing a block proposal is delegated to the embedded `module.Builder`. +// +// Context: BlockProducer is part of the `hostuff` package and can therefore be expected to comply with +// hotstuff-internal design patterns, such as there being a single dedicated thread executing the EventLoop, +// including EventHandler, SafetyRules, and BlockProducer. However, `module.Builder` lives in a different +// package! Therefore, we should make the least restrictive assumptions, and support concurrent signing requests +// within `module.Builder`. To minimize implementation dependencies and reduce the chance of safety-critical +// consensus bugs, BlockProducer wraps `SafetyRules` and mediates concurrent access. Furthermore, by supporting +// concurrent singing requests, we enable various optimizations of optimistic and/or upfront block production. type BlockProducer interface { // MakeBlockProposal builds a new HotStuff block proposal using the given view, // the given quorum certificate for its parent and [optionally] a timeout certificate for last view(could be nil). diff --git a/consensus/hotstuff/blockproducer/block_producer.go b/consensus/hotstuff/blockproducer/block_producer.go index 3238507393a..70dfff422f9 100644 --- a/consensus/hotstuff/blockproducer/block_producer.go +++ b/consensus/hotstuff/blockproducer/block_producer.go @@ -4,28 +4,38 @@ import ( "fmt" "github.com/onflow/flow-go/consensus/hotstuff" - "github.com/onflow/flow-go/consensus/hotstuff/model" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" ) -// BlockProducer is responsible for producing new block proposals +// BlockProducer is responsible for producing new block proposals. It is a service component to HotStuff's +// main state machine (implemented in the EventHandler). The BlockProducer's central purpose is to mediate +// concurrent signing requests to its embedded `hotstuff.SafetyRules` during block production. The actual +// work of producing a block proposal is delegated to the embedded `module.Builder`. +// +// Context: BlockProducer is part of the `hostuff` package and can therefore be expected to comply with +// hotstuff-internal design patterns, such as there being a single dedicated thread executing the EventLoop, +// including EventHandler, SafetyRules, and BlockProducer. However, `module.Builder` lives in a different +// package! Therefore, we should make the least restrictive assumptions, and support concurrent signing requests +// within `module.Builder`. To minimize implementation dependencies and reduce the chance of safety-critical +// consensus bugs, BlockProducer wraps `SafetyRules` and mediates concurrent access. Furthermore, by supporting +// concurrent singing requests, we enable various optimizations of optimistic and/or upfront block production. type BlockProducer struct { - signer hotstuff.Signer - committee hotstuff.Replicas - builder module.Builder + safetyRules hotstuff.SafetyRules + committee hotstuff.Replicas + builder module.Builder } var _ hotstuff.BlockProducer = (*BlockProducer)(nil) -// New creates a new BlockProducer which wraps the chain compliance layer block builder -// to provide hotstuff with block proposals. +// New creates a new BlockProducer, which mediates concurrent signing requests to the embedded +// `hotstuff.SafetyRules` during block production, delegated to `module.Builder`. // No errors are expected during normal operation. -func New(signer hotstuff.Signer, committee hotstuff.Replicas, builder module.Builder) (*BlockProducer, error) { +func New(safetyRules hotstuff.SafetyRules, committee hotstuff.Replicas, builder module.Builder) (*BlockProducer, error) { bp := &BlockProducer{ - signer: signer, - committee: committee, - builder: builder, + safetyRules: safetyRules, + committee: committee, + builder: builder, } return bp, nil } @@ -46,33 +56,14 @@ func (bp *BlockProducer) MakeBlockProposal(view uint64, qc *flow.QuorumCertifica return nil } - // TODO: We should utilize the `EventHandler`'s `SafetyRules` to generate the block signature instead of using an independent signing logic: https://github.com/dapperlabs/flow-go/issues/6892 - signProposal := func(header *flow.Header) error { - // turn the header into a block header proposal as known by hotstuff - block := model.Block{ - BlockID: header.ID(), - View: view, - ProposerID: header.ProposerID, - QC: qc, - PayloadHash: header.PayloadHash, - Timestamp: header.Timestamp, - } - - // then sign the proposal - proposal, err := bp.signer.CreateProposal(&block) - if err != nil { - return fmt.Errorf("could not sign block proposal: %w", err) - } - - header.ProposerSigData = proposal.SigData - return nil - } - - // retrieve a fully built block header from the builder - header, err := bp.builder.BuildOn(qc.BlockID, setHotstuffFields, signProposal) + signer := newSafetyRulesConcurrencyWrapper(bp.safetyRules) + header, err := bp.builder.BuildOn(qc.BlockID, setHotstuffFields, signer.Sign) if err != nil { return nil, fmt.Errorf("could not build block proposal on top of %v: %w", qc.BlockID, err) } + if !signer.IsSigningComplete() { + return nil, fmt.Errorf("signer has not yet completed signing") + } return header, nil } diff --git a/consensus/hotstuff/blockproducer/safety_rules_wrapper.go b/consensus/hotstuff/blockproducer/safety_rules_wrapper.go new file mode 100644 index 00000000000..1edd5c0c352 --- /dev/null +++ b/consensus/hotstuff/blockproducer/safety_rules_wrapper.go @@ -0,0 +1,89 @@ +package blockproducer + +import ( + "fmt" + + "go.uber.org/atomic" + + "github.com/onflow/flow-go/consensus/hotstuff" + "github.com/onflow/flow-go/consensus/hotstuff/model" + "github.com/onflow/flow-go/model/flow" +) + +// safetyRulesConcurrencyWrapper wraps `hotstuff.SafetyRules` to allow its usage in concurrent environments. +// Correctness requirements: +// +// (i) The wrapper's Sign function is called exactly once (wrapper errors on repeated Sign calls) +// (ii) SafetyRules is not accessed outside the wrapper concurrently. The wrapper cannot enforce this. +// +// The correctness condition (ii) holds because there is a single dedicated thread executing the Event Loop, +// including the EventHandler, that also runs the logic of `BlockProducer.MakeBlockProposal`. +// +// Concurrency safety: +// +// (a) There is one dedicated thread executing the Event Loop, including the EventHandler, that also runs the logic of +// `BlockProducer.MakeBlockProposal`. Hence, while the 'Event Loop Thread' is in `MakeBlockProposal`, we are guaranteed +// the only interactions with `SafetyRules` are in `module.Builder.BuildOn` +// (b) The Event Loop Thread instantiates the variable `signingStatus`. Furthermore, the `signer` call first reads `signingStatus`. +// Therefore, all operations in the EventHandler prior to calling `Builder.BuildOn(..)` happen before the call to `signer`. +// Hence, it is guaranteed that the `signer` uses the most recent state of `SafetyRules`, even if `Sign` is executed by a +// different thread. +// (c) Just before the `signer` call returns, it writes `signingStatus`. Furthermore, the Event Loop Thread reads `signingStatus` +// right after the `Builder.BuildOn(..)` call returns. Thereby, Event Loop Thread sees the most recent state of `SafetyRules` +// after completing the signing operation. +// +// With the transitivity of the 'Happens Before' relationship (-> go Memory Model https://go.dev/ref/mem#atomic), we have proven +// that concurrent access of the wrapped `safetyRules` is safe for the state transition: +// +// instantiate signingStatus to 0 ─► update signingStatus from 0 to 1 → signer → update signingStatus from 1 to 2 ─► confirm signingStatus has value 2 +// +// ╰──────────────┬───────────────╯ ╰──────────────────────────────────────┬─────────────────────────────────────╯ ╰────────────────┬────────────────╯ +// +// Event Loop Thread within the scope of Builder.BuildOn Event Loop Thread +// +// All state transitions _other_ than the one above yield exceptions without modifying `SafetyRules`. +type safetyRulesConcurrencyWrapper struct { + // signingStatus guarantees concurrency safety and encodes the progress of the signing process. + // We differentiate between 4 different states: + // - value 0: signing is not yet started + // - value 1: one thread has already entered the signing process, which is currently ongoing + // - value 2: the thread that set `signingStatus` to value 1 has completed the signing + signingStatus atomic.Uint32 + safetyRules hotstuff.SafetyRules +} + +func newSafetyRulesConcurrencyWrapper(safetyRules hotstuff.SafetyRules) *safetyRulesConcurrencyWrapper { + return &safetyRulesConcurrencyWrapper{safetyRules: safetyRules} +} + +// Sign modifies the given unsignedHeader by including the proposer's signature date. +// Safe under concurrent calls. Per convention, this method should be called exactly once. +// Only the first call will succeed, and subsequent calls error. The implementation is backed +// by `SafetyRules` and thereby guarantees consensus safety for singing block proposals. +// No errors expected during normal operations +func (w *safetyRulesConcurrencyWrapper) Sign(unsignedHeader *flow.Header) error { + if !w.signingStatus.CompareAndSwap(0, 1) { // value of `signingStatus` is something else than 0 + return fmt.Errorf("signer has already commenced signing; possibly repeated signer call") + } // signer is now in state 1, and this thread is the only one every going to execute the following logic + + // signature for own block is structurally a vote + vote, err := w.safetyRules.SignOwnProposal(model.ProposalFromFlow(unsignedHeader)) + if err != nil { + return fmt.Errorf("could not sign block proposal: %w", err) + } + unsignedHeader.ProposerSigData = vote.SigData + + // value of `signingStatus` is always 1, i.e. the following check always succeeds. + if !w.signingStatus.CompareAndSwap(1, 2) { // sanity check protects logic from future modifications accidentally breaking this invariant + panic("signer wrapper completed its work but encountered state other than 1") // never happens + } + return nil +} + +// IsSigningComplete atomically checks whether the Sign logic has concluded, and returns true only in this case. +// By reading the atomic `signingStatus` and confirming it has the expected value, it is guaranteed that any state +// changes of `safetyRules` that happened within `Sign` are visible to the Event Loop Thread. +// No errors expected during normal operations +func (w *safetyRulesConcurrencyWrapper) IsSigningComplete() bool { + return w.signingStatus.Load() == 2 +} diff --git a/consensus/hotstuff/consumer.go b/consensus/hotstuff/consumer.go index 1a5bfb175af..2fcf2e4703e 100644 --- a/consensus/hotstuff/consumer.go +++ b/consensus/hotstuff/consumer.go @@ -60,7 +60,7 @@ type VoteAggregationViolationConsumer interface { // Prerequisites: // Implementation must be concurrency safe; Non-blocking; // and must handle repetition of the same events (with some processing overhead). - OnVoteForInvalidBlockDetected(vote *model.Vote, invalidProposal *model.Proposal) + OnVoteForInvalidBlockDetected(vote *model.Vote, invalidProposal *model.SignedProposal) } // TimeoutAggregationViolationConsumer consumes outbound notifications about Active Pacemaker violations specifically @@ -138,7 +138,7 @@ type ParticipantConsumer interface { // Prerequisites: // Implementation must be concurrency safe; Non-blocking; // and must handle repetition of the same events (with some processing overhead). - OnReceiveProposal(currentView uint64, proposal *model.Proposal) + OnReceiveProposal(currentView uint64, proposal *model.SignedProposal) // OnReceiveQc notifications are produced by the EventHandler when it starts processing a // QuorumCertificate [QC] constructed by the node's internal vote aggregator. diff --git a/consensus/hotstuff/cruisectl/README.md b/consensus/hotstuff/cruisectl/README.md index bf0d961f2ac..1835fdac123 100644 --- a/consensus/hotstuff/cruisectl/README.md +++ b/consensus/hotstuff/cruisectl/README.md @@ -55,22 +55,22 @@ From our definition it follows that: - $e > 0$ implies that the estimated epoch switchover (assuming ideal system behaviour) happens too late. Therefore, to hit the desired epoch switchover time, the time we spend in views has to be *smaller* than $\tau_0$. - For $e < 0$ means that we estimate the epoch switchover to be too early. Therefore, we should be slowing down and spend more than $\tau_0$ in the following views. -**Reasoning:** +**Reasoning:** The desired idealized system behaviour would a constant view duration $\tau_0$ throughout the entire epoch. However, in the real-world system we have disturbances (varying message relay times, slow or offline nodes, etc) and measurement uncertainty (node can only observe its local view times, but not the committee’s collective swarm behaviour). - + -After a disturbance, we want the controller to drive the system back to a state, where it can closely follow the ideal behaviour from there on. +After a disturbance, we want the controller to drive the system back to a state, where it can closely follow the ideal behaviour from there on. - Simulations have shown that this approach produces *very* stable controller with the intended behaviour. - **Controller driving $e := \gamma - \Gamma \rightarrow 0$** + **Controller driving $e := \gamma - \Gamma \rightarrow 0$** - setting the differential term $K_d=0$, the controller responds as expected with damped oscillatory behaviour - to a singular strong disturbance. Setting $K_d=3$ suppresses oscillations and the controller's performance improves as it responds more effectively. + to a singular strong disturbance. Setting $K_d=3$ suppresses oscillations and the controller's performance improves as it responds more effectively. @@ -79,7 +79,7 @@ After a disturbance, we want the controller to drive the system back to a state, - controller very quickly compensates for moderate disturbances and observational noise in a well-behaved system: - + - controller compensates massive anomaly (100s network partition) effectively: @@ -87,9 +87,9 @@ After a disturbance, we want the controller to drive the system back to a state, - controller effectively stabilizes system with continued larger disturbances (20% of offline consensus participants) and notable observational noise: - - **References:** - + + **References:** + - statistical model for happy-path view durations: [ID controller for ``block-rate-delay``](https://www.notion.so/ID-controller-for-block-rate-delay-cc9c2d9785ac4708a37bb952557b5ef4?pvs=21) - For Python implementation with additional disturbances (offline nodes) and observational noise, see GitHub repo: [flow-internal/analyses/pacemaker_timing/2023-05_Blocktime_PID-controller](https://github.com/dapperlabs/flow-internal/tree/master/analyses/pacemaker_timing/2023-05_Blocktime_PID-controller) → [controller_tuning_v01.py](https://github.com/dapperlabs/flow-internal/blob/master/analyses/pacemaker_timing/2023-05_Blocktime_PID-controller/controller_tuning_v01.py) @@ -103,7 +103,7 @@ Each consensus participant runs a local instance of the controller described bel - $v$ is the node’s current view - ideal view time $\tau_0$ is computed solely based on the Epoch configuration: -$\tau_0 := \frac{<{\rm total\ epoch\ time}>}{<{\rm total\ views\ in\ epoch}>}$ (for mainnet 22, Epoch 75, we have $\tau_0 \simeq$ 1250ms). + $\tau_0 := \frac{<{\rm total\ epoch\ time}>}{<{\rm total\ views\ in\ epoch}>}$ (for mainnet 22, Epoch 75, we have $\tau_0 \simeq$ 1250ms). - $t[v]$ is the time the node entered view $v$ - $F[v]$ is the final view of the current epoch - $T[v]$ is the target end time of the current epoch @@ -116,9 +116,9 @@ $\tau_0 := \frac{<{\rm total\ epoch\ time}>}{<{\rm total\ views\ in\ epoch}>}$ ### Precise convention of View Timing -Upon observing block `B` with view $v$, the controller updates its internal state. +Upon observing block `B` with view $v$, the controller updates its internal state. -Note the '+1' term in the computation of the remaining views $k[v] := F[v] +1 - v$ . This is related to our convention that the epoch begins (happy path) when observing the first block of the epoch. Only by observing this block, the nodes transition to the first view of the epoch. Up to that point, the consensus replicas remain in the last view of the previous epoch, in the state of `having processed the last block of the old epoch and voted for it` (happy path). Replicas remain in this state until they see a confirmation of the view (either QC or TC for the last view of the previous epoch). +Note the '+1' term in the computation of the remaining views $k[v] := F[v] +1 - v$ . This is related to our convention that the epoch begins (happy path) when observing the first block of the epoch. Only by observing this block, the nodes transition to the first view of the epoch. Up to that point, the consensus replicas remain in the last view of the previous epoch, in the state of `having processed the last block of the old epoch and voted for it` (happy path). Replicas remain in this state until they see a confirmation of the view (either QC or TC for the last view of the previous epoch). @@ -126,7 +126,7 @@ In accordance with this convention, observing the proposal for the last view of ### Controller -The goal of the controller is to drive the system towards an error of zero, i.e. $e[v] \rightarrow 0$. For a [PID controller](https://en.wikipedia.org/wiki/PID_controller), the output $u$ for view $v$ has the form: +The goal of the controller is to drive the system towards an error of zero, i.e. $e[v] \rightarrow 0$. For a [PID controller](https://en.wikipedia.org/wiki/PID_controller), the output $u$ for view $v$ has the form: ```math u[v] = K_p \cdot e[v]+K_i \cdot \mathcal{I}[v] + K_d \cdot \Delta[v] @@ -135,13 +135,13 @@ u[v] = K_p \cdot e[v]+K_i \cdot \mathcal{I}[v] + K_d \cdot \Delta[v] With error terms (computed from observations) - $e[v]$ representing the *instantaneous* error as of view $v$ -(commonly referred to as ‘proportional term’) + (commonly referred to as ‘proportional term’) - $\mathcal{I} [v] = \sum_v e[v]$ the sum of the errors -(commonly referred to as ‘integral term’) + (commonly referred to as ‘integral term’) - $\Delta[v]=e[v]-e[v-1]$ the rate of change of the error -(commonly referred to as ‘derivative term’) + (commonly referred to as ‘derivative term’) -and controller parameters (values derived from controller tuning): +and controller parameters (values derived from controller tuning): - $K_p$ be the proportional coefficient - $K_i$ be the integral coefficient @@ -153,7 +153,7 @@ Each consensus participant observes the error $e[v]$ based on its local view evo ![](/docs/CruiseControl_BlockTimeController/ViewRate.png) -Therefore, we expect $e[v]$ to be very variable. Furthermore, note that a node uses its local view transition times as an estimator for the collective behaviour of the entire committee. Therefore, there is also observational noise obfuscating the underlying collective behaviour. Hence, we expect notable noise. +Therefore, we expect $e[v]$ to be very variable. Furthermore, note that a node uses its local view transition times as an estimator for the collective behaviour of the entire committee. Therefore, there is also observational noise obfuscating the underlying collective behaviour. Hence, we expect notable noise. ## Managing noise @@ -176,7 +176,7 @@ see also [Python `Ewma` implementation](https://github.com/dapperlabs/flow-inter ### **Managing noise in the integral term** -In particular systematic observation bias are a problem, as it leads to a diverging integral term. The commonly adopted approach is to use a ‘leaky integrator’ [[1](https://www.music.mcgill.ca/~gary/307/week2/node4.html), [2](https://engineering.stackexchange.com/questions/29833/limiting-the-integral-to-a-time-window-in-pid-controller)], which we denote as $\bar{\mathcal{I}}[v]$. +In particular systematic observation bias are a problem, as it leads to a diverging integral term. The commonly adopted approach is to use a ‘leaky integrator’ [[1](https://www.music.mcgill.ca/~gary/307/week2/node4.html), [2](https://engineering.stackexchange.com/questions/29833/limiting-the-integral-to-a-time-window-in-pid-controller)], which we denote as $\bar{\mathcal{I}}[v]$. ```math \eqalign{ @@ -185,7 +185,7 @@ In particular systematic observation bias are a problem, as it leads to a diverg } ``` -Intuitively, the loss factor $\lambda$ relates to the time window of the integrator. A factor of 0 means an infinite time horizon, while $\lambda =1$ makes the integrator only memorize the last input. Let $\lambda \equiv \frac{1}{N_\textnormal{itg}}$ and consider a constant input value $x$. Then $N_\textnormal{itg}$ relates to the number of past samples that the integrator remembers: +Intuitively, the loss factor $\lambda$ relates to the time window of the integrator. A factor of 0 means an infinite time horizon, while $\lambda =1$ makes the integrator only memorize the last input. Let $\lambda \equiv \frac{1}{N_\textnormal{itg}}$ and consider a constant input value $x$. Then $N_\textnormal{itg}$ relates to the number of past samples that the integrator remembers: - the integrators output will saturate at $x\cdot N_\textnormal{itg}$ - an integrator initialized with 0, reaches 2/3 of the saturation value $x\cdot N_\textnormal{itg}$ after consuming $N_\textnormal{itg}$ inputs @@ -223,21 +223,21 @@ with parameters: - $K_d = 3.0$ - $N_\textnormal{ewma} = 5$, i.e. $\alpha = \frac{1}{N_\textnormal{ewma}} = 0.2$ - $N_\textnormal{itg} = 50$, i.e. $\lambda = \frac{1}{N_\textnormal{itg}} = 0.02$ - + The controller output $u[v]$ represents the amount of time by which the controller wishes to deviate from the ideal view duration $\tau_0$. In other words, the duration of view $v$ that the controller wants to set is ```math \widehat{\tau}[v] = \tau_0 - u[v] ``` --- -### Limits of authority +### Limits of authority [Latest update: Crescendo Upgrade, June 2024] In general, there is no bound on the output of the controller output $u$. However, it is important to limit the controller’s influence to keep $u$ within a sensible range. - upper bound on view duration $\widehat{\tau}[v]$ that we allow the controller to set: - + The current timeout threshold is set to 1045ms and the largest view duration we want to allow the controller to set is $\tau_\textrm{max}$ = 910ms. Thereby, we have a buffer $\beta$ = 135ms remaining for message propagation and the replicas validating the proposal for view $v$. @@ -247,14 +247,14 @@ In general, there is no bound on the output of the controller output $u$. Howeve - lower bound on the view duration: - + Let $t_\textnormal{p}[v]$ denote the time when the primary for view $v$ has constructed its block proposal. On the happy path, a replica concludes view $v-1$ and transitions to view $v$, when it observes the proposal for view $v$. The duration $t_\textnormal{p}[v] - t[v-1]$ is the time between the primary observing the parent block (view $v-1$), collecting votes, constructing a QC for view $v-1$, and subsequently its own proposal for view $v$. This duration is the minimally required time to execute the protocol. The controller can only *delay* broadcasting the block, - but it cannot release the block before $t_\textnormal{p}[v]$ simply because the proposal isn’t ready any earlier. - + but it cannot release the block before $t_\textnormal{p}[v]$ simply because the proposal isn’t ready any earlier. + 👉 Let $\hat{t}[v]$ denote the time when the primary for view $v$ *broadcasts* its proposal. We assign: @@ -264,7 +264,7 @@ In general, there is no bound on the output of the controller output $u$. Howeve ``` This equation guarantees that the controller does not drive consensus into a timeout, as long as broadcasting the block and its validation together require less than time $\beta$. Currently, we have $\tau_\textrm{max}$ = 910ms as the upper bound for view durations that the controller can set. -In comparison, for HotStuff's timeout threshold we set $\texttt{hotstuff-min-timeout} = \tau_\textrm{max} + \beta$, with $\beta$ = 135ms. +In comparison, for HotStuff's timeout threshold we set $\texttt{hotstuff-min-timeout} = \tau_\textrm{max} + \beta$, with $\beta$ = 135ms. @@ -284,13 +284,13 @@ In comparison, for HotStuff's timeout threshold we set $\texttt{hotstuff-min-tim When a node is catching up, it observes the blocks significantly later than they were published. In other words, from the perspective of the node catching up, the blocks are too late. However, as it reaches the most recent blocks, also the observed timing error approaches zero (assuming approximately correct block publication by the honest supermajority). Nevertheless, due to its biased error observations, the node -catching up could still try to compensate for the network being behind, and publish its proposal as early as possible. +catching up could still try to compensate for the network being behind, and publish its proposal as early as possible. **Assumption:** With only a smaller fraction of nodes being offline or catching up, the effect is expected to be small and easily compensated for by the supermajority of online nodes. ### A node has a misconfigured clock -Cap the maximum deviation from the default delay (limits the general impact of error introduced by the `BlockTimeController`). The node with misconfigured clock will contribute to the error in a limited way, but as long as the majority of nodes have an accurate clock, they will offset this error. +Cap the maximum deviation from the default delay (limits the general impact of error introduced by the `BlockTimeController`). The node with misconfigured clock will contribute to the error in a limited way, but as long as the majority of nodes have an accurate clock, they will offset this error. **Assumption:** With only a smaller fraction of nodes having misconfigured clocks, the effect will be small enough to be easily compensated for by the supermajority of correct nodes. @@ -304,8 +304,102 @@ We might incorrectly compute high error in the target view rate, if local curren When the network is in EFM, epoch timing is anyway disrupted. The main thing we want to avoid is that the controller drives consensus into a timeout. This is largely guaranteed, due to the limits of authority. Beyond that, pretty much any block timing on the happy path is acceptable. -Through, the optimal solution would be a consistent view time throughout normal Epochs as well as EFM. +Through, the optimal solution would be a consistent view time throughout normal Epochs as well as EFM. + +# Implementation Aspects -## Testing +## Timing Reference Points -[Cruise Control: Benchnet Testing Notes](https://www.notion.so/Cruise-Control-Benchnet-Testing-Notes-ea08f49ba9d24ce2a158fca9358966df?pvs=21) + + + +* Under the hood, the controller outputs the unconstrained view time $\widehat{\tau}[v]= \tau_0 - u[v]$ (of type [`time.Duration`](https://pkg.go.dev/time)), + which is wrapped into a [`happyPathBlockTime`](https://github.com/onflow/flow-go/blob/d9f7522d6c502d7e148dab69c926279202677cf8/consensus/hotstuff/cruisectl/proposal_timing.go#L59-L74) + (👉 [code](https://github.com/onflow/flow-go/blob/d9f7522d6c502d7e148dab69c926279202677cf8/consensus/hotstuff/cruisectl/block_time_controller.go#L402-L404)). + The [`happyPathBlockTime`](https://github.com/onflow/flow-go/blob/d9f7522d6c502d7e148dab69c926279202677cf8/consensus/hotstuff/cruisectl/proposal_timing.go#L59-L74) + [applies the limits of authority](https://github.com/onflow/flow-go/blob/d9f7522d6c502d7e148dab69c926279202677cf8/consensus/hotstuff/cruisectl/proposal_timing.go#L94), + and the resulting `ConstrainedBlockTime` we capture by the metric `Average Target View Time` (blue dotted curve in figure above). +* From taking a look at the [`hotatuff.EventHandler`](https://github.com/onflow/flow-go/blob/d9f7522d6c502d7e148dab69c926279202677cf8/consensus/hotstuff/eventhandler/event_handler.go#L157-L171), + we can confirm that the `BlockTimeController` and the metric `Observed View Time` use practically the same reference time to determine view progression: + ```golang + func (e *EventHandler) OnReceiveProposal(proposal *model.Proposal) error { + â‹® + + // store the block. + err := e.forks.AddValidatedBlock(block) + if err != nil { + return fmt.Errorf("cannot add proposal to forks (%x): %w", block.BlockID, err) + } + + _, err = e.paceMaker.ProcessQC(proposal.Block.QC) + if err != nil { + return fmt.Errorf("could not process QC for block %x: %w", block.BlockID, err) + } + + _, err = e.paceMaker.ProcessTC(proposal.LastViewTC) + if err != nil { + return fmt.Errorf("could not process TC for block %x: %w", block.BlockID, err) + } + + â‹® + ``` + - The call to `forks.AddValidatedBlock` emits the `OnBlockIncorporated` notification for `block` with view $v$, which the `BlockTimeController` uses as [its starting point for the view](https://github.com/onflow/flow-go/blob/d9f7522d6c502d7e148dab69c926279202677cf8/consensus/hotstuff/cruisectl/block_time_controller.go#L476-L481): + ```golang + TimedBlock{Block: block, TimeObserved: time.Now().UTC()} + ``` + So for the `BlockTimeController`, the start of view `v` is the `TimeObserved` for proposal with view `v`. + - Right after, the PaceMaker ingests the QC for view `v-1`, which is included in the proposal for view `v`. + The call to `paceMaker.ProcessQC` updates the metric `consensus_hotstuff_cur_view`, based on which we calculate `Observed Average (10m) View Time [s]` (blue solid curve in figure above). +* As the `TargetPublicationTime` for block `v+1`, the `BlockTimeController` [calculates](https://github.com/onflow/flow-go/blob/d9f7522d6c502d7e148dab69c926279202677cf8/consensus/hotstuff/cruisectl/proposal_timing.go#L102-L112): + ```golang + targetPublicationTime := TimeObserved.Add(ConstrainedBlockTime) + ``` +* The [`EventHandler` triggers](https://github.com/onflow/flow-go/blob/d9f7522d6c502d7e148dab69c926279202677cf8/consensus/hotstuff/eventhandler/event_handler.go#L390-L417) the + [computation](https://github.com/onflow/flow-go/blob/d9f7522d6c502d7e148dab69c926279202677cf8/consensus/hotstuff/cruisectl/block_time_controller.go#L249-L262) of the + `Block Publication Delay` metric (dashed purple curve) right when it hands its proposal for view `v+1` to the [`MessageHub`](https://github.com/onflow/flow-go/blob/d9f7522d6c502d7e148dab69c926279202677cf8/engine/consensus/message_hub/message_hub.go#L428-L434) + ```golang + publicationDelay := time.Until(targetPublicationTime) + if publicationDelay < 0 { + publicationDelay = 0 // Controller can only delay publication of proposal. Hence, the delay is lower-bounded by zero. + } + metrics.ProposalPublicationDelay(publicationDelay) + ``` +* The [`MessageHub` repeats exactly that computation of `publicationDelay`](https://github.com/onflow/flow-go/blob/d9f7522d6c502d7e148dab69c926279202677cf8/engine/consensus/message_hub/message_hub.go#L433-L437) + to determine how long it should sleep before broadcasting the proposal. + +**Estimator for consensus runtime**: +- The `Observed Average View Time` measurement starts when we see the parent block while `Block Publication Delay` starts to measure when the child block is constructed. + `Observed Average View Time` and `Block Publication Delay` use nearly identical temporal reference points to stop their respective measurement (discrepancy is smaller than 1ms based on prior benchmarks of the `EventHandler`). + Therefore, the following is an estimator for how long it takes for the protocol to complete one view on the happy path (running it fast as it possibly can without any delays): + ```math + \texttt{Observed Average View Time} - \texttt{Block Publication Delay} + ``` +- There are some additional computation steps that we haven't accounted for, which could introduce errors. However, the `EventHandler` is very fast with execution times of single-digit milliseconds in practise. + Hence, the uncertainty of this estimator is minimal (expected order of single milliseconds). + + +## Controller's observation of blocks (`OnBlockIncorporated`) must approximately match the time when other replicas observe the proposal + +On Testnet (`devnet51`) August 14-18, 2024 we observed the following discrepancy for a consensus committee of 7 nodes: +* There was a significant discrepancy between the `Observed Average View Time` vs the `Average Target View Time`: + +* This resulted in the controller having reduced limits of authority: the limits of authority are computed based on the `Target View Time`, which the controller + set to the largest permitted value of 910ms (higher values were not allowed to prevent the controller from driving consensus into timeouts). However, in reality + the view progression was notably faster, meaning consensus would have tolerated larger delays without risk of timeouts. + +### Learning + +This discrepancy between what the controller was setting vs the networks real-world response was due to a systematic observation bias: +* For **small consensus committee sizes**, a node being selected as **leader for successive rounds** needs to be taken into account. In this scenario, + it is especially important that the leader "observes its own proposal" approximately at the same time when the other replicas receive it. +* Having the leader's cruise control "observes the own proposal" at the time when the proposal is constructed, before adding the controller's delay, would + introduce a significant observation bias. In this case, only the leader would assume that the view has started much earlier compared to the other replicas. + Therefore, it assumes it has much less time until other nodes would presumably trigger their timeout. Hence, the node erroneously restricts the possible delay + it can impose before broadcasting the child block. +* For larger consensus committee sizes, this systematic observation bias persists, but the probability of is much lower: The probability for a leader to be selected + as leader decreases with $\frac{1}{n}$ for $n$ the committee size. As the bias only affects the second of the two consecutive views, the overall impact of this + bias declines with increasing consensus committee size. + +## Initial Testing + +see [Cruise Control: Benchnet Testing Notes](https://www.notion.so/Cruise-Control-Benchnet-Testing-Notes-ea08f49ba9d24ce2a158fca9358966df?pvs=21) diff --git a/consensus/hotstuff/event_handler.go b/consensus/hotstuff/event_handler.go index a2134680389..6deda44eeca 100644 --- a/consensus/hotstuff/event_handler.go +++ b/consensus/hotstuff/event_handler.go @@ -39,7 +39,7 @@ type EventHandler interface { // consensus participant. // All inputs should be validated before feeding into this function. Assuming trusted data. // No errors are expected during normal operation. - OnReceiveProposal(proposal *model.Proposal) error + OnReceiveProposal(proposal *model.SignedProposal) error // OnLocalTimeout handles a local timeout event by creating a model.TimeoutObject and broadcasting it. // No errors are expected during normal operation. diff --git a/consensus/hotstuff/eventhandler/event_handler.go b/consensus/hotstuff/eventhandler/event_handler.go index 59320691a73..7b4c3d2a6f4 100644 --- a/consensus/hotstuff/eventhandler/event_handler.go +++ b/consensus/hotstuff/eventhandler/event_handler.go @@ -11,6 +11,7 @@ import ( "github.com/onflow/flow-go/consensus/hotstuff" "github.com/onflow/flow-go/consensus/hotstuff/model" "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/utils/logging" ) // EventHandler is the main handler for individual events that trigger state transition. @@ -44,6 +45,35 @@ type EventHandler struct { committee hotstuff.Replicas safetyRules hotstuff.SafetyRules notifier hotstuff.Consumer + + // myLastProposedView is the latest view that this node has created a proposal for + // CAUTION: in-memory only; information will be lost once the node reboots. This is fine for the following reason: + // 1. At the moment, the block construction logic persists its blocks in the database, _before_ returning the + // reference to the EventHandler, which subsequently publishes the block (via the `OnOwnProposal` notification). + // Therefore, for each view that this consensus participant published a block for, this block is also in the database. + // 2. Before constructing a proposal for view v, the node confirms that there is no block for view v already stored in + // its local Forks, and skips the block production otherwise. When the node boots up, it populates Forks with all + // unfinalized blocks. Hence, we conclude: + // Let v be a view for which this node constructed a proposal _before_ rebooting. Then, after rebooting it will never + // construct another proposal for view v. + // 3. What remains is to show that this node will not propose for views which it has previously proposed for without any + // reboots. Conceptually, this is guaranteed by the SafetyRules, but only if voting and signing proposals is _both_ + // done by safety rules. Unfortunately, the current implementation signs its own proposals _independently_ of safety rules. + // Hence, we add the following logic: + // - `myLastProposedView` is zero in case this node has not generated any proposal since its last reboot. Then, argument + // 2. guarantees that the node will not double-propose. + // - Whenever this node constructed a proposal for view v, it will set `myLastProposedView` to value `v`, _before_ + // publishing the proposal (via the `OnOwnProposal` notification). + // - Only if `v < myLastProposedView`, this node will engage its block production logic for view `v`. Therefore, it is + // guaranteed that this node has not generated any proposal for view v since its last reboot. + // In summary, argument 2. and 3. guarantee that this node will not double-propose (independently of whether the node + // restarted or not). Note that this holds, _without_ the node needing to store newly generated proposals right away in `Forks`. + // On the happy path (no restarts), updating `myLastProposedView` will suffice to prevent creating two proposals for the same view. + // The node's own proposal will be added to Forks _after_ the broadcast the same way as proposals from other nodes. + // On the unhappy path, the node's own proposals will be added to Forks along with unfinalized proposals from other nodes. + // TODO: use safety rules also for block signing, which produces the same guarantees of not double-proposing without this extra logic + // For further details, see issue https://github.com/onflow/flow-go/issues/6389 + myLastProposedView uint64 } var _ hotstuff.EventHandler = (*EventHandler)(nil) @@ -134,7 +164,7 @@ func (e *EventHandler) OnReceiveTc(tc *flow.TimeoutCertificate) error { // consensus participant. // All inputs should be validated before feeding into this function. Assuming trusted data. // No errors are expected during normal operation. -func (e *EventHandler) OnReceiveProposal(proposal *model.Proposal) error { +func (e *EventHandler) OnReceiveProposal(proposal *model.SignedProposal) error { block := proposal.Block curView := e.paceMaker.CurView() log := e.log.With(). @@ -313,6 +343,10 @@ func (e *EventHandler) broadcastTimeoutObjectIfAuthorized() error { // - after receiving a proposal (but not changing view), if that proposal is referenced by our highest known QC, // and the proposal was previously unknown, then we can propose a block in the current view // +// Enforced INVARIANTS: +// - There will at most be `OnOwnProposal` notification emitted for views where this node is the leader, and none +// if another node is the leader. This holds irrespective of restarts. Formally, this prevents proposal equivocation. +// // It reads the current view, and generates a proposal if we are the leader. // No errors are expected during normal operation. func (e *EventHandler) proposeForNewViewIfPrimary() error { @@ -330,10 +364,24 @@ func (e *EventHandler) proposeForNewViewIfPrimary() error { e.notifier.OnCurrentViewDetails(curView, finalizedView, currentLeader) - // check that I am the primary for this view and that I haven't already proposed; otherwise there is nothing to do + // check that I am the primary for this view if e.committee.Self() != currentLeader { return nil } + + // CASE A: Preventing proposal equivocation on the happy path + // We will never produce two proposals for the same view, _since_ the last reboot. This is because without a reboot, + // we can only proceed once beyond the following lines. + if curView <= e.myLastProposedView { + log.Debug().Msg("already proposed for current view") + return nil + } + e.myLastProposedView = curView + + // CASE B: Preventing proposal equivocation on the unhappy path + // We will never produce a proposal for view v, if we already constructed a proposal for the same view _before_ the + // most recent reboot. This is because during proposal construction (further below), (i) the block is saved in the database + // _before_ a reference is returned to the EventHandler and (ii) all unfinalized proposals are added to Forks during the reboot. for _, b := range e.forks.GetBlocksForView(curView) { // on the happy path, this slice is empty if b.ProposerID == e.committee.Self() { log.Debug().Msg("already proposed for current view") @@ -381,39 +429,60 @@ func (e *EventHandler) proposeForNewViewIfPrimary() error { lastViewTC = nil } + // Construct Own SignedProposal + // CAUTION, design constraints: + // (i) We cannot process our own proposal within the `EventHandler` right away. + // (ii) We cannot add our own proposal to Forks here right away. + // (iii) Metrics for the PaceMaker/CruiseControl assume that the EventHandler is the only caller of + // `TargetPublicationTime`. Technically, `TargetPublicationTime` records the publication delay + // relative to its _latest_ call. + // + // To satisfy all constraints, we construct the proposal here and query (once!) its `TargetPublicationTime`. Though, + // we do _not_ process our own blocks right away and instead ingest them into the EventHandler the same way as + // proposals from other consensus participants. Specifically, on the path through the HotStuff state machine leading + // to block construction, the node's own proposal is largely ephemeral. The proposal is handed to the `MessageHub` (via + // the `OnOwnProposal` notification including the `TargetPublicationTime`). The `MessageHub` waits until + // `TargetPublicationTime` and only then broadcast the proposal and puts it into the EventLoop's queue + // for inbound blocks. This is exactly the same way as proposals from other nodes are ingested by the `EventHandler`, + // except that we are skipping the ComplianceEngine (assuming that our own proposals are protocol-compliant). + // + // Context: + // • On constraint (i): We want to support consensus committees only consisting of a *single* node. If the EvenHandler + // internally processed the block right away via a direct message call, the call-stack would be ever-growing and + // the node would crash eventually (we experienced this with a very early HotStuff implementation). Specifically, + // if we wanted to process the block directly without taking a detour through the EventLoop's inbound queue, + // we would call `OnReceiveProposal` here. The function `OnReceiveProposal` would then end up calling + // then end up calling `proposeForNewViewIfPrimary` (this function) to generate the next proposal, which again + // would result in calling `OnReceiveProposal` and so on so forth until the call stack or memory limit is reached + // and the node crashes. This is only a problem for consensus committees of size 1. + // • On constraint (ii): When adding a proposal to Forks, Forks emits a `BlockIncorporatedEvent` notification, which + // is observed by Cruse Control and would change its state. However, note that Cruse Control is trying to estimate + // the point in time when _other_ nodes are observing the proposal. The time when we broadcast the proposal (i.e. + // `TargetPublicationTime`) is a reasonably good estimator, but *not* the time the proposer constructed the block + // (because there is potentially still a significant wait until `TargetPublicationTime`). + // + // The current approach is for a node to process its own proposals at the same time and through the same code path as + // proposals from other nodes. This satisfies constraints (i) and (ii) and generates very strong consistency, from a + // software design perspective. + // Just hypothetically, if we changed Cruise Control to be notified about own block proposals _only_ when they are + // broadcast (satisfying constraint (ii) without relying on the EventHandler), then we could add a proposal to Forks + // here right away. Nevertheless, the restriction remains that we cannot process that proposal right away within the + // EventHandler and instead need to put it into the EventLoop's inbound queue to support consensus committees of size 1. flowProposal, err := e.blockProducer.MakeBlockProposal(curView, newestQC, lastViewTC) if err != nil { return fmt.Errorf("can not make block proposal for curView %v: %w", curView, err) } - proposedBlock := model.BlockFromFlow(flowProposal) // turn the signed flow header into a proposal - - // determine target publication time - // CAUTION: - // • we must call `TargetPublicationTime` _before_ `AddValidatedBlock`, because `AddValidatedBlock` - // may emit a BlockIncorporatedEvent, which changes CruiseControl's state. - // • metrics for the PaceMaker/CruiseControl assume that the event handler is the only caller of - // `TargetPublicationTime`. Technically, `TargetPublicationTime` records the publication delay - // relative to its _latest_ call. - targetPublicationTime := e.paceMaker.TargetPublicationTime(flowProposal.View, start, flowProposal.ParentID) - - // we want to store created proposal in forks to make sure that we don't create more proposals for - // current view. Due to asynchronous nature of our design it's possible that after creating proposal - // we will be asked to propose again for same view. - err = e.forks.AddValidatedBlock(proposedBlock) - if err != nil { - return fmt.Errorf("could not add newly created proposal (%v): %w", proposedBlock.BlockID, err) - } - + targetPublicationTime := e.paceMaker.TargetPublicationTime(flowProposal.View, start, flowProposal.ParentID) // determine target publication time log.Debug(). - Uint64("block_view", proposedBlock.View). + Uint64("block_view", flowProposal.View). Time("target_publication", targetPublicationTime). - Hex("block_id", proposedBlock.BlockID[:]). + Hex("block_id", logging.ID(flowProposal.ID())). Uint64("parent_view", newestQC.View). Hex("parent_id", newestQC.BlockID[:]). - Hex("signer", proposedBlock.ProposerID[:]). + Hex("signer", flowProposal.ProposerID[:]). Msg("forwarding proposal to communicator for broadcasting") - // raise a notification with proposal (also triggers broadcast) + // emit notification with own proposal (also triggers broadcast) e.notifier.OnOwnProposal(flowProposal, targetPublicationTime) return nil } @@ -422,7 +491,7 @@ func (e *EventHandler) proposeForNewViewIfPrimary() error { // It is called AFTER the block has been stored or found in Forks // It checks whether to vote for this block. // No errors are expected during normal operation. -func (e *EventHandler) processBlockForCurrentView(proposal *model.Proposal) error { +func (e *EventHandler) processBlockForCurrentView(proposal *model.SignedProposal) error { // sanity check that block is really for the current view: curView := e.paceMaker.CurView() block := proposal.Block @@ -457,7 +526,7 @@ func (e *EventHandler) processBlockForCurrentView(proposal *model.Proposal) erro // ownVote generates and forwards the own vote, if we decide to vote. // Any errors are potential symptoms of uncovered edge cases or corrupted internal state (fatal). // No errors are expected during normal operation. -func (e *EventHandler) ownVote(proposal *model.Proposal, curView uint64, nextLeader flow.Identifier) error { +func (e *EventHandler) ownVote(proposal *model.SignedProposal, curView uint64, nextLeader flow.Identifier) error { block := proposal.Block log := e.log.With(). Uint64("block_view", block.View). diff --git a/consensus/hotstuff/eventhandler/event_handler_test.go b/consensus/hotstuff/eventhandler/event_handler_test.go index 1e4dbf08317..f01a7760e40 100644 --- a/consensus/hotstuff/eventhandler/event_handler_test.go +++ b/consensus/hotstuff/eventhandler/event_handler_test.go @@ -132,14 +132,14 @@ func NewSafetyRules(t *testing.T) *SafetyRules { // SafetyRules will not vote for any block, unless the blockID exists in votable map safetyRules.On("ProduceVote", mock.Anything, mock.Anything).Return( - func(block *model.Proposal, _ uint64) *model.Vote { + func(block *model.SignedProposal, _ uint64) *model.Vote { _, ok := safetyRules.votable[block.Block.BlockID] if !ok { return nil } return createVote(block.Block) }, - func(block *model.Proposal, _ uint64) error { + func(block *model.SignedProposal, _ uint64) error { _, ok := safetyRules.votable[block.Block.BlockID] if !ok { return model.NewNoVoteErrorf("block not found") @@ -179,7 +179,7 @@ func NewForks(t *testing.T, finalized uint64) *Forks { } f.On("AddValidatedBlock", mock.Anything).Return(func(proposal *model.Block) error { - log.Info().Msgf("forks.AddValidatedBlock received Proposal for view: %v, QC: %v\n", proposal.View, proposal.QC.View) + log.Info().Msgf("forks.AddValidatedBlock received Block proposal for view: %v, QC: %v\n", proposal.View, proposal.QC.View) return f.addProposal(proposal) }).Maybe() @@ -228,14 +228,12 @@ type BlockProducer struct { } func (b *BlockProducer) MakeBlockProposal(view uint64, qc *flow.QuorumCertificate, lastViewTC *flow.TimeoutCertificate) (*flow.Header, error) { - return model.ProposalToFlow(&model.Proposal{ - Block: helper.MakeBlock( + return helper.SignedProposalToFlow(helper.MakeSignedProposal(helper.WithProposal( + helper.MakeProposal(helper.WithBlock(helper.MakeBlock( helper.WithBlockView(view), helper.WithBlockQC(qc), - helper.WithBlockProposer(b.proposerID), - ), - LastViewTC: lastViewTC, - }), nil + helper.WithBlockProposer(b.proposerID))), + helper.WithLastViewTC(lastViewTC))))), nil } func TestEventHandler(t *testing.T) { @@ -258,8 +256,8 @@ type EventHandlerSuite struct { initView uint64 // the current view at the beginning of the test case endView uint64 // the expected current view at the end of the test case - parentProposal *model.Proposal - votingProposal *model.Proposal + parentProposal *model.SignedProposal + votingProposal *model.SignedProposal qc *flow.QuorumCertificate tc *flow.TimeoutCertificate newview *model.NewViewEvent @@ -670,7 +668,7 @@ func (es *EventHandlerSuite) TestOnReceiveTc_NextLeaderProposes() { // proposed block should contain valid newest QC and lastViewTC expectedNewestQC := es.paceMaker.NewestQC() - proposal := model.ProposalFromFlow(header) + proposal := model.SignedProposalFromFlow(header) require.Equal(es.T(), expectedNewestQC, proposal.Block.QC) require.Equal(es.T(), es.paceMaker.LastViewTC(), proposal.LastViewTC) }).Once() @@ -766,6 +764,8 @@ func (es *EventHandlerSuite) Test100Timeout() { // TestLeaderBuild100Blocks tests scenario where leader builds 100 proposals one after another func (es *EventHandlerSuite) TestLeaderBuild100Blocks() { + require.Equal(es.T(), 1, len(es.forks.proposals), "expect Forks to contain only root block") + // I'm the leader for the first view es.committee.leaders[es.initView] = struct{}{} @@ -805,7 +805,8 @@ func (es *EventHandlerSuite) TestLeaderBuild100Blocks() { } require.Equal(es.T(), es.endView, es.paceMaker.CurView(), "incorrect view change") - require.Equal(es.T(), totalView, (len(es.forks.proposals)-1)/2) + require.Equal(es.T(), totalView+1, len(es.forks.proposals), "expect Forks to contain root block + 100 proposed blocks") + es.notifier.AssertExpectations(es.T()) } // TestFollowerFollows100Blocks tests scenario where follower receives 100 proposals one after another @@ -1030,10 +1031,7 @@ func createVote(block *model.Block) *model.Vote { } } -func createProposal(view uint64, qcview uint64) *model.Proposal { +func createProposal(view uint64, qcview uint64) *model.SignedProposal { block := createBlockWithQC(view, qcview) - return &model.Proposal{ - Block: block, - SigData: nil, - } + return helper.MakeSignedProposal(helper.WithProposal(helper.MakeProposal(helper.WithBlock(block)))) } diff --git a/consensus/hotstuff/eventloop/event_loop.go b/consensus/hotstuff/eventloop/event_loop.go index d7bc478490b..8a91d214355 100644 --- a/consensus/hotstuff/eventloop/event_loop.go +++ b/consensus/hotstuff/eventloop/event_loop.go @@ -22,7 +22,7 @@ import ( // it contains an attached insertionTime that is used to measure how long we have waited between queening proposal and // actually processing by `EventHandler`. type queuedProposal struct { - proposal *model.Proposal + proposal *model.SignedProposal insertionTime time.Time } @@ -263,7 +263,7 @@ func (el *EventLoop) loop(ctx context.Context) error { } // SubmitProposal pushes the received block to the proposals channel -func (el *EventLoop) SubmitProposal(proposal *model.Proposal) { +func (el *EventLoop) SubmitProposal(proposal *model.SignedProposal) { queueItem := queuedProposal{ proposal: proposal, insertionTime: time.Now(), diff --git a/consensus/hotstuff/eventloop/event_loop_test.go b/consensus/hotstuff/eventloop/event_loop_test.go index 8b6eeed5b25..2c40cecf5c8 100644 --- a/consensus/hotstuff/eventloop/event_loop_test.go +++ b/consensus/hotstuff/eventloop/event_loop_test.go @@ -75,7 +75,7 @@ func (s *EventLoopTestSuite) TestReadyDone() { // Test_SubmitQC tests that submitted proposal is eventually sent to event handler for processing func (s *EventLoopTestSuite) Test_SubmitProposal() { - proposal := helper.MakeProposal() + proposal := helper.MakeSignedProposal() processed := atomic.NewBool(false) s.eh.On("OnReceiveProposal", proposal).Run(func(args mock.Arguments) { processed.Store(true) @@ -229,7 +229,7 @@ func TestEventLoop_Timeout(t *testing.T) { go func() { defer wg.Done() for !processed.Load() { - eventLoop.SubmitProposal(helper.MakeProposal()) + eventLoop.SubmitProposal(helper.MakeSignedProposal()) } }() @@ -258,7 +258,7 @@ func TestReadyDoneWithStartTime(t *testing.T) { require.NoError(t, err) done := make(chan struct{}) - eh.On("OnReceiveProposal", mock.AnythingOfType("*model.Proposal")).Run(func(args mock.Arguments) { + eh.On("OnReceiveProposal", mock.AnythingOfType("*model.SignedProposal")).Run(func(args mock.Arguments) { require.True(t, time.Now().After(startTime)) close(done) }).Return(nil).Once() @@ -271,7 +271,7 @@ func TestReadyDoneWithStartTime(t *testing.T) { parentBlock := unittest.BlockHeaderFixture() block := unittest.BlockHeaderWithParentFixture(parentBlock) - eventLoop.SubmitProposal(model.ProposalFromFlow(block)) + eventLoop.SubmitProposal(model.SignedProposalFromFlow(block)) unittest.RequireCloseBefore(t, done, startTimeDuration+100*time.Millisecond, "proposal wasn't received") cancel() diff --git a/consensus/hotstuff/forks/block_builder_test.go b/consensus/hotstuff/forks/block_builder_test.go index 03daec535c1..62b4bf8bce5 100644 --- a/consensus/hotstuff/forks/block_builder_test.go +++ b/consensus/hotstuff/forks/block_builder_test.go @@ -106,7 +106,6 @@ func (bb *BlockBuilder) Proposals() ([]*model.Proposal, error) { PayloadHash: payloadHash, }, LastViewTC: lastViewTC, - SigData: nil, } proposal.Block.BlockID = makeBlockID(proposal.Block) diff --git a/consensus/hotstuff/forks/forks.go b/consensus/hotstuff/forks/forks.go index aa4db7f9853..bf7ee881b6c 100644 --- a/consensus/hotstuff/forks/forks.go +++ b/consensus/hotstuff/forks/forks.go @@ -401,7 +401,7 @@ func (f *Forks) checkForAdvancingFinalization(certifiedBlock *model.CertifiedBlo parentBlock := parentVertex.(*BlockContainer).Block() // Note: we assume that all stored blocks pass Forks.EnsureBlockIsValidExtension(block); - // specifically, that Proposal's ViewNumber is strictly monotonically + // specifically, that block's ViewNumber is strictly monotonically // increasing which is enforced by LevelledForest.VerifyVertex(...) // We denote: // * a DIRECT 1-chain as '<-' diff --git a/consensus/hotstuff/forks/forks_test.go b/consensus/hotstuff/forks/forks_test.go index 9662533dd0d..7b9c7af9b50 100644 --- a/consensus/hotstuff/forks/forks_test.go +++ b/consensus/hotstuff/forks/forks_test.go @@ -261,7 +261,7 @@ func TestFinalize_Multiple2Chains(t *testing.T) { } // TestFinalize_OrphanedFork tests that we can finalize a block which causes a conflicting fork to be orphaned. -// We ingest the the following block tree: +// We ingest the following block tree: // // [â—„(1) 2] [â—„(2) 3] // [â—„(2) 4] [â—„(4) 5] [â—„(5) 6] @@ -389,7 +389,7 @@ func TestIgnoreBlocksBelowFinalizedView(t *testing.T) { } // TestDoubleProposal tests that the DoubleProposal notification is emitted when two different -// blocks for the same view are added. We ingest the the following block tree: +// blocks for the same view are added. We ingest the following block tree: // // / [â—„(1) 2] // [1] @@ -460,7 +460,7 @@ func TestConflictingQCs(t *testing.T) { } // TestConflictingFinalizedForks checks that finalizing 2 conflicting forks should return model.ByzantineThresholdExceededError -// We ingest the the following block tree: +// We ingest the following block tree: // // [â—„(1) 2] [â—„(2) 3] [â—„(3) 4] [â—„(4) 5] // [â—„(2) 6] [â—„(6) 7] [â—„(7) 8] diff --git a/consensus/hotstuff/helper/block.go b/consensus/hotstuff/helper/block.go index a3fa6f6e2e7..ce341f61476 100644 --- a/consensus/hotstuff/helper/block.go +++ b/consensus/hotstuff/helper/block.go @@ -56,10 +56,21 @@ func WithBlockQC(qc *flow.QuorumCertificate) func(*model.Block) { } } +func MakeSignedProposal(options ...func(*model.SignedProposal)) *model.SignedProposal { + proposal := &model.SignedProposal{ + Proposal: *MakeProposal(), + SigData: unittest.SignatureFixture(), + } + for _, option := range options { + option(proposal) + } + return proposal +} + func MakeProposal(options ...func(*model.Proposal)) *model.Proposal { proposal := &model.Proposal{ - Block: MakeBlock(), - SigData: unittest.SignatureFixture(), + Block: MakeBlock(), + LastViewTC: nil, } for _, option := range options { option(proposal) @@ -67,14 +78,20 @@ func MakeProposal(options ...func(*model.Proposal)) *model.Proposal { return proposal } +func WithProposal(proposal *model.Proposal) func(*model.SignedProposal) { + return func(signedProposal *model.SignedProposal) { + signedProposal.Proposal = *proposal + } +} + func WithBlock(block *model.Block) func(*model.Proposal) { return func(proposal *model.Proposal) { proposal.Block = block } } -func WithSigData(sigData []byte) func(*model.Proposal) { - return func(proposal *model.Proposal) { +func WithSigData(sigData []byte) func(*model.SignedProposal) { + return func(proposal *model.SignedProposal) { proposal.SigData = sigData } } @@ -84,3 +101,29 @@ func WithLastViewTC(lastViewTC *flow.TimeoutCertificate) func(*model.Proposal) { proposal.LastViewTC = lastViewTC } } + +// SignedProposalToFlow turns a block proposal into a flow header. +// +// CAUTION: This function is only suitable for TESTING purposes ONLY. +// In the conversion from `flow.Header` to HoStuff's `model.Block` we loose information +// (e.g. `ChainID` and `Height` are not included in `model.Block`) and hence the conversion +// is *not reversible*. This is on purpose, because we wanted to only expose data to +// HotStuff that HotStuff really needs. +func SignedProposalToFlow(proposal *model.SignedProposal) *flow.Header { + + block := proposal.Block + header := &flow.Header{ + ParentID: block.QC.BlockID, + PayloadHash: block.PayloadHash, + Timestamp: block.Timestamp, + View: block.View, + ParentView: block.QC.View, + ParentVoterIndices: block.QC.SignerIndices, + ParentVoterSigData: block.QC.SigData, + ProposerID: block.ProposerID, + ProposerSigData: proposal.SigData, + LastViewTC: proposal.LastViewTC, + } + + return header +} diff --git a/consensus/hotstuff/integration/connect_test.go b/consensus/hotstuff/integration/connect_test.go index a254e0f9f3c..177a8d0244b 100644 --- a/consensus/hotstuff/integration/connect_test.go +++ b/consensus/hotstuff/integration/connect_test.go @@ -37,7 +37,7 @@ func Connect(t *testing.T, instances []*Instance) { } // convert into proposal immediately - proposal := model.ProposalFromFlow(header) + proposal := model.SignedProposalFromFlow(header) // store locally and loop back to engine for processing sender.ProcessBlock(proposal) diff --git a/consensus/hotstuff/integration/filters_test.go b/consensus/hotstuff/integration/filters_test.go index 8d6ac067f48..8bf12fc6d3b 100644 --- a/consensus/hotstuff/integration/filters_test.go +++ b/consensus/hotstuff/integration/filters_test.go @@ -8,7 +8,7 @@ import ( ) // VoteFilter is a filter function for dropping Votes. -// Return value `true` implies that the the given Vote should be +// Return value `true` implies that the given Vote should be // dropped, while `false` indicates that the Vote should be received. type VoteFilter func(*model.Vote) bool @@ -34,34 +34,34 @@ func BlockVotesBy(voterID flow.Identifier) VoteFilter { } // ProposalFilter is a filter function for dropping Proposals. -// Return value `true` implies that the the given Proposal should be -// dropped, while `false` indicates that the Proposal should be received. -type ProposalFilter func(*model.Proposal) bool +// Return value `true` implies that the given SignedProposal should be +// dropped, while `false` indicates that the SignedProposal should be received. +type ProposalFilter func(*model.SignedProposal) bool -func BlockNoProposals(*model.Proposal) bool { +func BlockNoProposals(*model.SignedProposal) bool { return false } -func BlockAllProposals(*model.Proposal) bool { +func BlockAllProposals(*model.SignedProposal) bool { return true } // BlockProposalRandomly drops proposals randomly with a probability of `dropProbability` ∈ [0,1] func BlockProposalRandomly(dropProbability float64) ProposalFilter { - return func(*model.Proposal) bool { + return func(*model.SignedProposal) bool { return rand.Float64() < dropProbability } } // BlockProposalsBy drops all proposals originating from the specified `proposerID` func BlockProposalsBy(proposerID flow.Identifier) ProposalFilter { - return func(proposal *model.Proposal) bool { + return func(proposal *model.SignedProposal) bool { return proposal.Block.ProposerID == proposerID } } // TimeoutObjectFilter is a filter function for dropping TimeoutObjects. -// Return value `true` implies that the the given TimeoutObject should be +// Return value `true` implies that the given TimeoutObject should be // dropped, while `false` indicates that the TimeoutObject should be received. type TimeoutObjectFilter func(*model.TimeoutObject) bool diff --git a/consensus/hotstuff/integration/instance_test.go b/consensus/hotstuff/integration/instance_test.go index fa404c9bd78..49b8e6e68bd 100644 --- a/consensus/hotstuff/integration/instance_test.go +++ b/consensus/hotstuff/integration/instance_test.go @@ -59,7 +59,7 @@ type Instance struct { queue chan interface{} updatingBlocks sync.RWMutex headers map[flow.Identifier]*flow.Header - pendings map[flow.Identifier]*model.Proposal // indexed by parent ID + pendings map[flow.Identifier]*model.SignedProposal // indexed by parent ID // mocked dependencies committee *mocks.DynamicCommittee @@ -151,7 +151,7 @@ func NewInstance(t *testing.T, options ...Option) *Instance { stop: cfg.StopCondition, // instance data - pendings: make(map[flow.Identifier]*model.Proposal), + pendings: make(map[flow.Identifier]*model.SignedProposal), headers: make(map[flow.Identifier]*flow.Header), queue: make(chan interface{}, 1024), @@ -227,16 +227,6 @@ func NewInstance(t *testing.T, options ...Option) *Instance { in.persist.On("PutLivenessData", mock.Anything).Return(nil) // program the hotstuff signer behaviour - in.signer.On("CreateProposal", mock.Anything).Return( - func(block *model.Block) *model.Proposal { - proposal := &model.Proposal{ - Block: block, - SigData: nil, - } - return proposal - }, - nil, - ) in.signer.On("CreateVote", mock.Anything).Return( func(block *model.Block) *model.Vote { vote := &model.Vote{ @@ -304,7 +294,7 @@ func NewInstance(t *testing.T, options ...Option) *Instance { } // convert into proposal immediately - proposal := model.ProposalFromFlow(header) + proposal := model.SignedProposalFromFlow(header) // store locally and loop back to engine for processing in.ProcessBlock(proposal) @@ -365,10 +355,6 @@ func NewInstance(t *testing.T, options ...Option) *Instance { notifier.AddConsumer(logConsumer) notifier.AddConsumer(in.notifier) - // initialize the block producer - in.producer, err = blockproducer.New(in.signer, in.committee, in.builder) - require.NoError(t, err) - // initialize the finalizer rootBlock := model.BlockFromFlow(cfg.Root) @@ -417,20 +403,26 @@ func NewInstance(t *testing.T, options ...Option) *Instance { minRequiredWeight := committees.WeightThresholdToBuildQC(uint64(len(in.participants)) * weight) voteProcessorFactory := mocks.NewVoteProcessorFactory(t) voteProcessorFactory.On("Create", mock.Anything, mock.Anything).Return( - func(log zerolog.Logger, proposal *model.Proposal) hotstuff.VerifyingVoteProcessor { + func(log zerolog.Logger, proposal *model.SignedProposal) hotstuff.VerifyingVoteProcessor { stakingSigAggtor := helper.MakeWeightedSignatureAggregator(weight) stakingSigAggtor.On("Verify", mock.Anything, mock.Anything).Return(nil).Maybe() rbRector := helper.MakeRandomBeaconReconstructor(msig.RandomBeaconThreshold(len(in.participants))) rbRector.On("Verify", mock.Anything, mock.Anything).Return(nil).Maybe() - return votecollector.NewCombinedVoteProcessor( + processor := votecollector.NewCombinedVoteProcessor( log, proposal.Block, stakingSigAggtor, rbRector, onQCCreated, packer, minRequiredWeight, ) + + err := processor.Process(proposal.ProposerVote()) + if err != nil { + t.Fatalf("invalid vote for own proposal: %v", err) + } + return processor }, nil).Maybe() voteAggregationDistributor := pubsub.NewVoteAggregationDistributor() @@ -531,6 +523,10 @@ func NewInstance(t *testing.T, options ...Option) *Instance { in.safetyRules, err = safetyrules.New(in.signer, in.persist, in.committee) require.NoError(t, err) + // initialize the block producer + in.producer, err = blockproducer.New(in.safetyRules, in.committee, in.builder) + require.NoError(t, err) + // initialize the event handler in.handler, err = eventhandler.NewEventHandler( log, @@ -601,7 +597,7 @@ func (in *Instance) Run() error { } case msg := <-in.queue: switch m := msg.(type) { - case *model.Proposal: + case *model.SignedProposal: // add block to aggregator in.voteAggregator.AddBlock(m) // then pass to event handler @@ -633,7 +629,7 @@ func (in *Instance) Run() error { } } -func (in *Instance) ProcessBlock(proposal *model.Proposal) { +func (in *Instance) ProcessBlock(proposal *model.SignedProposal) { in.updatingBlocks.Lock() defer in.updatingBlocks.Unlock() _, parentExists := in.headers[proposal.Block.QC.BlockID] @@ -641,7 +637,7 @@ func (in *Instance) ProcessBlock(proposal *model.Proposal) { if parentExists { next := proposal for next != nil { - in.headers[next.Block.BlockID] = model.ProposalToFlow(next) + in.headers[next.Block.BlockID] = helper.SignedProposalToFlow(next) in.queue <- next // keep processing the pending blocks diff --git a/consensus/hotstuff/integration/integration_test.go b/consensus/hotstuff/integration/integration_test.go index e4f2e588ba9..7dccb30e529 100644 --- a/consensus/hotstuff/integration/integration_test.go +++ b/consensus/hotstuff/integration/integration_test.go @@ -21,7 +21,6 @@ const safeTimeout = 2 * time.Second const happyPathMaxRoundFailures = 6 func TestSingleInstance(t *testing.T) { - // set up a single instance to run // NOTE: currently, the HotStuff logic will infinitely call back on itself // with a single instance, leading to a boundlessly growing call stack, diff --git a/consensus/hotstuff/mocks/consumer.go b/consensus/hotstuff/mocks/consumer.go index cd2363a1b93..311ae5a8a29 100644 --- a/consensus/hotstuff/mocks/consumer.go +++ b/consensus/hotstuff/mocks/consumer.go @@ -79,7 +79,7 @@ func (_m *Consumer) OnQcTriggeredViewChange(oldView uint64, newView uint64, qc * } // OnReceiveProposal provides a mock function with given fields: currentView, proposal -func (_m *Consumer) OnReceiveProposal(currentView uint64, proposal *model.Proposal) { +func (_m *Consumer) OnReceiveProposal(currentView uint64, proposal *model.SignedProposal) { _m.Called(currentView, proposal) } diff --git a/consensus/hotstuff/mocks/event_handler.go b/consensus/hotstuff/mocks/event_handler.go index a74897fd303..7fac8c02d1e 100644 --- a/consensus/hotstuff/mocks/event_handler.go +++ b/consensus/hotstuff/mocks/event_handler.go @@ -57,7 +57,7 @@ func (_m *EventHandler) OnPartialTcCreated(partialTC *hotstuff.PartialTcCreated) } // OnReceiveProposal provides a mock function with given fields: proposal -func (_m *EventHandler) OnReceiveProposal(proposal *model.Proposal) error { +func (_m *EventHandler) OnReceiveProposal(proposal *model.SignedProposal) error { ret := _m.Called(proposal) if len(ret) == 0 { @@ -65,7 +65,7 @@ func (_m *EventHandler) OnReceiveProposal(proposal *model.Proposal) error { } var r0 error - if rf, ok := ret.Get(0).(func(*model.Proposal) error); ok { + if rf, ok := ret.Get(0).(func(*model.SignedProposal) error); ok { r0 = rf(proposal) } else { r0 = ret.Error(0) diff --git a/consensus/hotstuff/mocks/event_loop.go b/consensus/hotstuff/mocks/event_loop.go index 1d3943eb3c4..c27549181b2 100644 --- a/consensus/hotstuff/mocks/event_loop.go +++ b/consensus/hotstuff/mocks/event_loop.go @@ -98,7 +98,7 @@ func (_m *EventLoop) Start(_a0 irrecoverable.SignalerContext) { } // SubmitProposal provides a mock function with given fields: proposal -func (_m *EventLoop) SubmitProposal(proposal *model.Proposal) { +func (_m *EventLoop) SubmitProposal(proposal *model.SignedProposal) { _m.Called(proposal) } diff --git a/consensus/hotstuff/mocks/participant_consumer.go b/consensus/hotstuff/mocks/participant_consumer.go index 883380232b4..b6970bba788 100644 --- a/consensus/hotstuff/mocks/participant_consumer.go +++ b/consensus/hotstuff/mocks/participant_consumer.go @@ -42,7 +42,7 @@ func (_m *ParticipantConsumer) OnQcTriggeredViewChange(oldView uint64, newView u } // OnReceiveProposal provides a mock function with given fields: currentView, proposal -func (_m *ParticipantConsumer) OnReceiveProposal(currentView uint64, proposal *model.Proposal) { +func (_m *ParticipantConsumer) OnReceiveProposal(currentView uint64, proposal *model.SignedProposal) { _m.Called(currentView, proposal) } diff --git a/consensus/hotstuff/mocks/safety_rules.go b/consensus/hotstuff/mocks/safety_rules.go index 1fac041ba94..83f33269542 100644 --- a/consensus/hotstuff/mocks/safety_rules.go +++ b/consensus/hotstuff/mocks/safety_rules.go @@ -46,7 +46,7 @@ func (_m *SafetyRules) ProduceTimeout(curView uint64, newestQC *flow.QuorumCerti } // ProduceVote provides a mock function with given fields: proposal, curView -func (_m *SafetyRules) ProduceVote(proposal *model.Proposal, curView uint64) (*model.Vote, error) { +func (_m *SafetyRules) ProduceVote(proposal *model.SignedProposal, curView uint64) (*model.Vote, error) { ret := _m.Called(proposal, curView) if len(ret) == 0 { @@ -55,10 +55,10 @@ func (_m *SafetyRules) ProduceVote(proposal *model.Proposal, curView uint64) (*m var r0 *model.Vote var r1 error - if rf, ok := ret.Get(0).(func(*model.Proposal, uint64) (*model.Vote, error)); ok { + if rf, ok := ret.Get(0).(func(*model.SignedProposal, uint64) (*model.Vote, error)); ok { return rf(proposal, curView) } - if rf, ok := ret.Get(0).(func(*model.Proposal, uint64) *model.Vote); ok { + if rf, ok := ret.Get(0).(func(*model.SignedProposal, uint64) *model.Vote); ok { r0 = rf(proposal, curView) } else { if ret.Get(0) != nil { @@ -66,7 +66,7 @@ func (_m *SafetyRules) ProduceVote(proposal *model.Proposal, curView uint64) (*m } } - if rf, ok := ret.Get(1).(func(*model.Proposal, uint64) error); ok { + if rf, ok := ret.Get(1).(func(*model.SignedProposal, uint64) error); ok { r1 = rf(proposal, curView) } else { r1 = ret.Error(1) @@ -75,6 +75,36 @@ func (_m *SafetyRules) ProduceVote(proposal *model.Proposal, curView uint64) (*m return r0, r1 } +// SignOwnProposal provides a mock function with given fields: unsignedProposal +func (_m *SafetyRules) SignOwnProposal(unsignedProposal *model.Proposal) (*model.Vote, error) { + ret := _m.Called(unsignedProposal) + + if len(ret) == 0 { + panic("no return value specified for SignOwnProposal") + } + + var r0 *model.Vote + var r1 error + if rf, ok := ret.Get(0).(func(*model.Proposal) (*model.Vote, error)); ok { + return rf(unsignedProposal) + } + if rf, ok := ret.Get(0).(func(*model.Proposal) *model.Vote); ok { + r0 = rf(unsignedProposal) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.Vote) + } + } + + if rf, ok := ret.Get(1).(func(*model.Proposal) error); ok { + r1 = rf(unsignedProposal) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // NewSafetyRules creates a new instance of SafetyRules. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. func NewSafetyRules(t interface { diff --git a/consensus/hotstuff/mocks/signer.go b/consensus/hotstuff/mocks/signer.go index d2b006eb47a..4a7113d5f4c 100644 --- a/consensus/hotstuff/mocks/signer.go +++ b/consensus/hotstuff/mocks/signer.go @@ -15,36 +15,6 @@ type Signer struct { mock.Mock } -// CreateProposal provides a mock function with given fields: block -func (_m *Signer) CreateProposal(block *model.Block) (*model.Proposal, error) { - ret := _m.Called(block) - - if len(ret) == 0 { - panic("no return value specified for CreateProposal") - } - - var r0 *model.Proposal - var r1 error - if rf, ok := ret.Get(0).(func(*model.Block) (*model.Proposal, error)); ok { - return rf(block) - } - if rf, ok := ret.Get(0).(func(*model.Block) *model.Proposal); ok { - r0 = rf(block) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*model.Proposal) - } - } - - if rf, ok := ret.Get(1).(func(*model.Block) error); ok { - r1 = rf(block) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - // CreateTimeout provides a mock function with given fields: curView, newestQC, lastViewTC func (_m *Signer) CreateTimeout(curView uint64, newestQC *flow.QuorumCertificate, lastViewTC *flow.TimeoutCertificate) (*model.TimeoutObject, error) { ret := _m.Called(curView, newestQC, lastViewTC) diff --git a/consensus/hotstuff/mocks/validator.go b/consensus/hotstuff/mocks/validator.go index 9b97d23d762..728795ef209 100644 --- a/consensus/hotstuff/mocks/validator.go +++ b/consensus/hotstuff/mocks/validator.go @@ -16,7 +16,7 @@ type Validator struct { } // ValidateProposal provides a mock function with given fields: proposal -func (_m *Validator) ValidateProposal(proposal *model.Proposal) error { +func (_m *Validator) ValidateProposal(proposal *model.SignedProposal) error { ret := _m.Called(proposal) if len(ret) == 0 { @@ -24,7 +24,7 @@ func (_m *Validator) ValidateProposal(proposal *model.Proposal) error { } var r0 error - if rf, ok := ret.Get(0).(func(*model.Proposal) error); ok { + if rf, ok := ret.Get(0).(func(*model.SignedProposal) error); ok { r0 = rf(proposal) } else { r0 = ret.Error(0) diff --git a/consensus/hotstuff/mocks/vote_aggregation_consumer.go b/consensus/hotstuff/mocks/vote_aggregation_consumer.go index 23ceb89ee6a..52819fcb1f9 100644 --- a/consensus/hotstuff/mocks/vote_aggregation_consumer.go +++ b/consensus/hotstuff/mocks/vote_aggregation_consumer.go @@ -31,7 +31,7 @@ func (_m *VoteAggregationConsumer) OnQcConstructedFromVotes(_a0 *flow.QuorumCert } // OnVoteForInvalidBlockDetected provides a mock function with given fields: vote, invalidProposal -func (_m *VoteAggregationConsumer) OnVoteForInvalidBlockDetected(vote *model.Vote, invalidProposal *model.Proposal) { +func (_m *VoteAggregationConsumer) OnVoteForInvalidBlockDetected(vote *model.Vote, invalidProposal *model.SignedProposal) { _m.Called(vote, invalidProposal) } diff --git a/consensus/hotstuff/mocks/vote_aggregation_violation_consumer.go b/consensus/hotstuff/mocks/vote_aggregation_violation_consumer.go index acac87cdce9..dcb718532a2 100644 --- a/consensus/hotstuff/mocks/vote_aggregation_violation_consumer.go +++ b/consensus/hotstuff/mocks/vote_aggregation_violation_consumer.go @@ -23,7 +23,7 @@ func (_m *VoteAggregationViolationConsumer) OnInvalidVoteDetected(err model.Inva } // OnVoteForInvalidBlockDetected provides a mock function with given fields: vote, invalidProposal -func (_m *VoteAggregationViolationConsumer) OnVoteForInvalidBlockDetected(vote *model.Vote, invalidProposal *model.Proposal) { +func (_m *VoteAggregationViolationConsumer) OnVoteForInvalidBlockDetected(vote *model.Vote, invalidProposal *model.SignedProposal) { _m.Called(vote, invalidProposal) } diff --git a/consensus/hotstuff/mocks/vote_aggregator.go b/consensus/hotstuff/mocks/vote_aggregator.go index 545990086ea..9bf372ecb2c 100644 --- a/consensus/hotstuff/mocks/vote_aggregator.go +++ b/consensus/hotstuff/mocks/vote_aggregator.go @@ -15,7 +15,7 @@ type VoteAggregator struct { } // AddBlock provides a mock function with given fields: block -func (_m *VoteAggregator) AddBlock(block *model.Proposal) { +func (_m *VoteAggregator) AddBlock(block *model.SignedProposal) { _m.Called(block) } @@ -45,7 +45,7 @@ func (_m *VoteAggregator) Done() <-chan struct{} { } // InvalidBlock provides a mock function with given fields: block -func (_m *VoteAggregator) InvalidBlock(block *model.Proposal) error { +func (_m *VoteAggregator) InvalidBlock(block *model.SignedProposal) error { ret := _m.Called(block) if len(ret) == 0 { @@ -53,7 +53,7 @@ func (_m *VoteAggregator) InvalidBlock(block *model.Proposal) error { } var r0 error - if rf, ok := ret.Get(0).(func(*model.Proposal) error); ok { + if rf, ok := ret.Get(0).(func(*model.SignedProposal) error); ok { r0 = rf(block) } else { r0 = ret.Error(0) diff --git a/consensus/hotstuff/mocks/vote_collector.go b/consensus/hotstuff/mocks/vote_collector.go index 5165640a838..27db379c9a8 100644 --- a/consensus/hotstuff/mocks/vote_collector.go +++ b/consensus/hotstuff/mocks/vote_collector.go @@ -33,7 +33,7 @@ func (_m *VoteCollector) AddVote(vote *model.Vote) error { } // ProcessBlock provides a mock function with given fields: block -func (_m *VoteCollector) ProcessBlock(block *model.Proposal) error { +func (_m *VoteCollector) ProcessBlock(block *model.SignedProposal) error { ret := _m.Called(block) if len(ret) == 0 { @@ -41,7 +41,7 @@ func (_m *VoteCollector) ProcessBlock(block *model.Proposal) error { } var r0 error - if rf, ok := ret.Get(0).(func(*model.Proposal) error); ok { + if rf, ok := ret.Get(0).(func(*model.SignedProposal) error); ok { r0 = rf(block) } else { r0 = ret.Error(0) diff --git a/consensus/hotstuff/mocks/vote_processor_factory.go b/consensus/hotstuff/mocks/vote_processor_factory.go index cb87a8568c3..5c4925cce4d 100644 --- a/consensus/hotstuff/mocks/vote_processor_factory.go +++ b/consensus/hotstuff/mocks/vote_processor_factory.go @@ -17,7 +17,7 @@ type VoteProcessorFactory struct { } // Create provides a mock function with given fields: log, proposal -func (_m *VoteProcessorFactory) Create(log zerolog.Logger, proposal *model.Proposal) (hotstuff.VerifyingVoteProcessor, error) { +func (_m *VoteProcessorFactory) Create(log zerolog.Logger, proposal *model.SignedProposal) (hotstuff.VerifyingVoteProcessor, error) { ret := _m.Called(log, proposal) if len(ret) == 0 { @@ -26,10 +26,10 @@ func (_m *VoteProcessorFactory) Create(log zerolog.Logger, proposal *model.Propo var r0 hotstuff.VerifyingVoteProcessor var r1 error - if rf, ok := ret.Get(0).(func(zerolog.Logger, *model.Proposal) (hotstuff.VerifyingVoteProcessor, error)); ok { + if rf, ok := ret.Get(0).(func(zerolog.Logger, *model.SignedProposal) (hotstuff.VerifyingVoteProcessor, error)); ok { return rf(log, proposal) } - if rf, ok := ret.Get(0).(func(zerolog.Logger, *model.Proposal) hotstuff.VerifyingVoteProcessor); ok { + if rf, ok := ret.Get(0).(func(zerolog.Logger, *model.SignedProposal) hotstuff.VerifyingVoteProcessor); ok { r0 = rf(log, proposal) } else { if ret.Get(0) != nil { @@ -37,7 +37,7 @@ func (_m *VoteProcessorFactory) Create(log zerolog.Logger, proposal *model.Propo } } - if rf, ok := ret.Get(1).(func(zerolog.Logger, *model.Proposal) error); ok { + if rf, ok := ret.Get(1).(func(zerolog.Logger, *model.SignedProposal) error); ok { r1 = rf(log, proposal) } else { r1 = ret.Error(1) diff --git a/consensus/hotstuff/model/errors.go b/consensus/hotstuff/model/errors.go index 4244d0ac531..75f79de92fd 100644 --- a/consensus/hotstuff/model/errors.go +++ b/consensus/hotstuff/model/errors.go @@ -113,7 +113,7 @@ type MissingBlockError struct { } func (e MissingBlockError) Error() string { - return fmt.Sprintf("missing Proposal at view %d with ID %v", e.View, e.BlockID) + return fmt.Sprintf("missing block at view %d with ID %v", e.View, e.BlockID) } // IsMissingBlockError returns whether an error is MissingBlockError @@ -165,11 +165,11 @@ func (e InvalidTCError) Unwrap() error { // InvalidProposalError indicates that the proposal is invalid type InvalidProposalError struct { - InvalidProposal *Proposal + InvalidProposal *SignedProposal Err error } -func NewInvalidProposalErrorf(proposal *Proposal, msg string, args ...interface{}) error { +func NewInvalidProposalErrorf(proposal *SignedProposal, msg string, args ...interface{}) error { return InvalidProposalError{ InvalidProposal: proposal, Err: fmt.Errorf(msg, args...), diff --git a/consensus/hotstuff/model/proposal.go b/consensus/hotstuff/model/proposal.go index 6566de09a97..6dcca721583 100644 --- a/consensus/hotstuff/model/proposal.go +++ b/consensus/hotstuff/model/proposal.go @@ -4,16 +4,48 @@ import ( "github.com/onflow/flow-go/model/flow" ) -// Proposal represent a new proposed block within HotStuff (and thus a -// a header in the bigger picture), signed by the proposer. +// Proposal represents a block proposal under construction. +// In order to decide whether a proposal is safe to sign, HotStuff's Safety Rules require +// proof that the leader entered the respective view in a protocol-compliant manner. Specifically, +// we require a TimeoutCertificate [TC] if and only if the QC in the block is _not_ for the +// immediately preceding view. Thereby we protect the consensus process from malicious leaders +// attempting to skip views that haven't concluded yet (a form of front-running attack). +// However, LastViewTC is only relevant until a QC is known that certifies the correctness of +// the block. Thereafter, the QC attests that honest consensus participants have confirmed the +// validity of the fork up to the latest certified block (including protocol-compliant view transitions). +// +// By explicitly differentiating the Proposal from the SignedProposal (extending Proposal by +// adding the proposer's signature), we can unify the algorithmic path of signing block proposals. +// This codifies the important aspect that a proposer's signature for their own block +// is conceptually also just a vote (we explicitly use that for aggregating votes, including the +// proposer's own vote to a QC). In order to express this conceptual equivalence in code, the +// voting logic in Safety Rules must also operate on an unsigned Proposal. +// +// TODO: atm, the flow.Header embeds the LastViewTC. However, for HotStuff we have `model.Block` +// and `model.Proposal`, where the latter was introduced when we added the PaceMaker to +// vanilla HotStuff. It would be more consistent, if we added `LastViewTC` to `model.Block`, +// or even better, introduce an interface for HotStuff's notion of a block (exposing +// the fields in `model.Block` plus LastViewTC) type Proposal struct { Block *Block - SigData []byte LastViewTC *flow.TimeoutCertificate } +// SignedProposal represent a new proposed block within HotStuff (and thus +// a header in the bigger picture), signed by the proposer. +// +// CAUTION: the signature only covers the pair (Block.View, Block.BlockID). Therefore, only +// the data that is hashed into the BlockID is cryptographically secured by the proposer's +// signature. +// Specifically, the proposer's signature cannot be covered by the Block.BlockID, as the +// proposer _signs_ the Block.BlockID (otherwise we have a cyclic dependency). +type SignedProposal struct { + Proposal + SigData []byte +} + // ProposerVote extracts the proposer vote from the proposal -func (p *Proposal) ProposerVote() *Vote { +func (p *SignedProposal) ProposerVote() *Vote { vote := Vote{ View: p.Block.View, BlockID: p.Block.BlockID, @@ -23,32 +55,23 @@ func (p *Proposal) ProposerVote() *Vote { return &vote } -// ProposalFromFlow turns a flow header into a hotstuff block type. +// SignedProposalFromFlow turns a flow header into a hotstuff block type. +func SignedProposalFromFlow(header *flow.Header) *SignedProposal { + proposal := SignedProposal{ + Proposal: Proposal{ + Block: BlockFromFlow(header), + LastViewTC: header.LastViewTC, + }, + SigData: header.ProposerSigData, + } + return &proposal +} + +// ProposalFromFlow turns an unsigned flow header into a unsigned hotstuff block type. func ProposalFromFlow(header *flow.Header) *Proposal { proposal := Proposal{ Block: BlockFromFlow(header), - SigData: header.ProposerSigData, LastViewTC: header.LastViewTC, } return &proposal } - -// ProposalToFlow turns a block proposal into a flow header. -func ProposalToFlow(proposal *Proposal) *flow.Header { - - block := proposal.Block - header := &flow.Header{ - ParentID: block.QC.BlockID, - PayloadHash: block.PayloadHash, - Timestamp: block.Timestamp, - View: block.View, - ParentView: block.QC.View, - ParentVoterIndices: block.QC.SignerIndices, - ParentVoterSigData: block.QC.SigData, - ProposerID: block.ProposerID, - ProposerSigData: proposal.SigData, - LastViewTC: proposal.LastViewTC, - } - - return header -} diff --git a/consensus/hotstuff/notifications/log_consumer.go b/consensus/hotstuff/notifications/log_consumer.go index f8baea639dc..3e454b61272 100644 --- a/consensus/hotstuff/notifications/log_consumer.go +++ b/consensus/hotstuff/notifications/log_consumer.go @@ -69,7 +69,7 @@ func (lc *LogConsumer) OnDoubleProposeDetected(block *model.Block, alt *model.Bl Msg("double proposal detected") } -func (lc *LogConsumer) OnReceiveProposal(currentView uint64, proposal *model.Proposal) { +func (lc *LogConsumer) OnReceiveProposal(currentView uint64, proposal *model.SignedProposal) { logger := lc.logBasicBlockData(lc.log.Debug(), proposal.Block). Uint64("cur_view", currentView) lastViewTC := proposal.LastViewTC @@ -197,7 +197,7 @@ func (lc *LogConsumer) OnInvalidVoteDetected(err model.InvalidVoteError) { Msgf("invalid vote detected: %s", err.Error()) } -func (lc *LogConsumer) OnVoteForInvalidBlockDetected(vote *model.Vote, proposal *model.Proposal) { +func (lc *LogConsumer) OnVoteForInvalidBlockDetected(vote *model.Vote, proposal *model.SignedProposal) { lc.log.Warn(). Str(logging.KeySuspicious, "true"). Uint64("vote_view", vote.View). diff --git a/consensus/hotstuff/notifications/noop_consumer.go b/consensus/hotstuff/notifications/noop_consumer.go index d8ad3e66e4f..38433c72b1a 100644 --- a/consensus/hotstuff/notifications/noop_consumer.go +++ b/consensus/hotstuff/notifications/noop_consumer.go @@ -32,7 +32,7 @@ func (*NoopParticipantConsumer) OnEventProcessed() {} func (*NoopParticipantConsumer) OnStart(uint64) {} -func (*NoopParticipantConsumer) OnReceiveProposal(uint64, *model.Proposal) {} +func (*NoopParticipantConsumer) OnReceiveProposal(uint64, *model.SignedProposal) {} func (*NoopParticipantConsumer) OnReceiveQc(uint64, *flow.QuorumCertificate) {} @@ -116,7 +116,8 @@ func (*NoopProposalViolationConsumer) OnDoubleVotingDetected(*model.Vote, *model func (*NoopProposalViolationConsumer) OnInvalidVoteDetected(model.InvalidVoteError) {} -func (*NoopProposalViolationConsumer) OnVoteForInvalidBlockDetected(*model.Vote, *model.Proposal) {} +func (*NoopProposalViolationConsumer) OnVoteForInvalidBlockDetected(*model.Vote, *model.SignedProposal) { +} func (*NoopProposalViolationConsumer) OnDoubleTimeoutDetected(*model.TimeoutObject, *model.TimeoutObject) { } diff --git a/consensus/hotstuff/notifications/pubsub/participant_distributor.go b/consensus/hotstuff/notifications/pubsub/participant_distributor.go index f5047cd7a53..4285a96b258 100644 --- a/consensus/hotstuff/notifications/pubsub/participant_distributor.go +++ b/consensus/hotstuff/notifications/pubsub/participant_distributor.go @@ -46,7 +46,7 @@ func (d *ParticipantDistributor) OnStart(currentView uint64) { } } -func (d *ParticipantDistributor) OnReceiveProposal(currentView uint64, proposal *model.Proposal) { +func (d *ParticipantDistributor) OnReceiveProposal(currentView uint64, proposal *model.SignedProposal) { d.lock.RLock() defer d.lock.RUnlock() for _, subscriber := range d.consumers { diff --git a/consensus/hotstuff/notifications/pubsub/vote_aggregation_violation_consumer.go b/consensus/hotstuff/notifications/pubsub/vote_aggregation_violation_consumer.go index d9d1e9baa26..7b75bd933e1 100644 --- a/consensus/hotstuff/notifications/pubsub/vote_aggregation_violation_consumer.go +++ b/consensus/hotstuff/notifications/pubsub/vote_aggregation_violation_consumer.go @@ -43,7 +43,7 @@ func (d *VoteAggregationViolationDistributor) OnInvalidVoteDetected(err model.In } } -func (d *VoteAggregationViolationDistributor) OnVoteForInvalidBlockDetected(vote *model.Vote, invalidProposal *model.Proposal) { +func (d *VoteAggregationViolationDistributor) OnVoteForInvalidBlockDetected(vote *model.Vote, invalidProposal *model.SignedProposal) { d.lock.RLock() defer d.lock.RUnlock() for _, subscriber := range d.consumers { diff --git a/consensus/hotstuff/notifications/slashing_violation_consumer.go b/consensus/hotstuff/notifications/slashing_violation_consumer.go index c03347ece6f..940c8270198 100644 --- a/consensus/hotstuff/notifications/slashing_violation_consumer.go +++ b/consensus/hotstuff/notifications/slashing_violation_consumer.go @@ -78,7 +78,7 @@ func (c *SlashingViolationsConsumer) OnInvalidTimeoutDetected(err model.InvalidT Msg("OnInvalidTimeoutDetected") } -func (c *SlashingViolationsConsumer) OnVoteForInvalidBlockDetected(vote *model.Vote, proposal *model.Proposal) { +func (c *SlashingViolationsConsumer) OnVoteForInvalidBlockDetected(vote *model.Vote, proposal *model.SignedProposal) { c.log.Warn(). Uint64("vote_view", vote.View). Hex("voted_block_id", vote.BlockID[:]). diff --git a/consensus/hotstuff/notifications/telemetry.go b/consensus/hotstuff/notifications/telemetry.go index d6cc3852179..a636bac3789 100644 --- a/consensus/hotstuff/notifications/telemetry.go +++ b/consensus/hotstuff/notifications/telemetry.go @@ -60,7 +60,7 @@ func (t *TelemetryConsumer) OnStart(currentView uint64) { t.pathHandler.NextStep().Msg("OnStart") } -func (t *TelemetryConsumer) OnReceiveProposal(currentView uint64, proposal *model.Proposal) { +func (t *TelemetryConsumer) OnReceiveProposal(currentView uint64, proposal *model.SignedProposal) { block := proposal.Block t.pathHandler.StartNextPath(currentView) step := t.pathHandler.NextStep(). diff --git a/consensus/hotstuff/safety_rules.go b/consensus/hotstuff/safety_rules.go index d4db634c79a..7e6e29b392f 100644 --- a/consensus/hotstuff/safety_rules.go +++ b/consensus/hotstuff/safety_rules.go @@ -22,18 +22,26 @@ type SafetyData struct { // SafetyRules enforces all consensus rules that guarantee safety. It produces votes for // the given blocks or TimeoutObject for the given views, only if all safety rules are satisfied. +// In particular, SafetyRules guarantees a foundational security theorem for HotStuff (incl. +// the DiemBFT / Jolteon variant), which we utilize also outside of consensus (e.g. queuing pending +// blocks for execution, verification, sealing etc): +// +// THEOREM: For each view, there can be at most 1 certified block. +// +// Implementations are generally *not* concurrency safe. type SafetyRules interface { // ProduceVote takes a block proposal and current view, and decides whether to vote for the block. - // Voting is deterministic meaning voting for same proposal will always result in the same vote. + // Voting is deterministic, i.e. voting for same proposal will always result in the same vote. // Returns: // * (vote, nil): On the _first_ block for the current view that is safe to vote for. - // Subsequently, voter does _not_ vote for any _other_ block with the same (or lower) view. + // Subsequently, voter does _not_ vote for any _other_ block with the same (or lower) view. // SafetyRules internally caches and persists its latest vote. As long as the SafetyRules' internal // state remains unchanged, ProduceVote will return its cached for identical inputs. // * (nil, model.NoVoteError): If the safety module decides that it is not safe to vote for the given block. // This is a sentinel error and _expected_ during normal operation. // All other errors are unexpected and potential symptoms of uncovered edge cases or corrupted internal state (fatal). - ProduceVote(proposal *model.Proposal, curView uint64) (*model.Vote, error) + ProduceVote(proposal *model.SignedProposal, curView uint64) (*model.Vote, error) + // ProduceTimeout takes current view, highest locally known QC and TC (optional, must be nil if and // only if QC is for previous view) and decides whether to produce timeout for current view. // Returns: @@ -43,4 +51,19 @@ type SafetyRules interface { // normal operation, e.g. during the grace-period after Epoch switchover or after the replica self-ejected. // All other errors are unexpected and potential symptoms of uncovered edge cases or corrupted internal state (fatal). ProduceTimeout(curView uint64, newestQC *flow.QuorumCertificate, lastViewTC *flow.TimeoutCertificate) (*model.TimeoutObject, error) + + // SignOwnProposal takes an unsigned block proposal and produces a vote for it. Vote is a cryptographic commitment + // to the proposal. By adding the vote to an unsigned proposal, the caller constructs a signed block proposal. This + // method has to be used only by the leader, which must be the proposer of the block (or an exception is returned). + // Implementors must guarantee that: + // - vote on the proposal satisfies safety rules + // - maximum one proposal is signed per view + // Returns: + // * (vote, nil): the passed unsigned proposal is a valid one, and it's safe to make a proposal. + // Subsequently, leader does _not_ produce any _other_ proposal with the same (or lower) view. + // * (nil, model.NoVoteError): according to HotStuff's Safety Rules, it is not safe to sign the given proposal. + // This could happen because we have already proposed or timed out for the given view. + // This is a sentinel error and _expected_ during normal operation. + // All other errors are unexpected and potential symptoms of uncovered edge cases or corrupted internal state (fatal). + SignOwnProposal(unsignedProposal *model.Proposal) (*model.Vote, error) } diff --git a/consensus/hotstuff/safetyrules/safety_rules.go b/consensus/hotstuff/safetyrules/safety_rules.go index 5ce5da8198a..85778736a26 100644 --- a/consensus/hotstuff/safetyrules/safety_rules.go +++ b/consensus/hotstuff/safetyrules/safety_rules.go @@ -8,17 +8,27 @@ import ( "github.com/onflow/flow-go/model/flow" ) +// CAUTION Tech Debt: The current implementation does not use SafetyRules, when a leader signs their own proposal. +// This strongly complicates the safety argument, where enforcing the safety-critical property of only ever voting +// once per view is distributed across different layers in the software stack. +// For further details, see issue https://github.com/onflow/flow-go/issues/6389 + // SafetyRules is a dedicated module that enforces consensus safety. This component has the sole authority to generate // votes and timeouts. It follows voting and timeout rules for creating votes and timeouts respectively. // Caller can be sure that created vote or timeout doesn't break safety and can be used in consensus process. // SafetyRules relies on hotstuff.Persister to store latest state of hotstuff.SafetyData. // // The voting rules implemented by SafetyRules are: -// 1. Replicas vote strictly in increasing rounds -// 2. Each block has to include a TC or a QC from the previous round. -// a. [Happy path] If the previous round resulted in a QC then new QC should extend it. +// 1. Replicas vote in strictly increasing views. At most one vote can be signed per view. +// Caution: The leader's block signature is formally a vote for their own proposal. +// 2. Each block has to include a TC or a QC from the previous view. +// a. [Happy path] If the previous view resulted in a QC then the proposer should include it in their block. // b. [Recovery path] If the previous round did *not* result in a QC, the leader of the -// subsequent round *must* include a valid TC for the previous round in its block. +// subsequent round *must* include a valid TC for the previous view in its block. +// +// Condition 1 guarantees a foundational security theorem for HotStuff (incl. the DiemBFT / Jolteon variant): +// +// THEOREM: For each view, there can be at most 1 certified block. // // NOT safe for concurrent use. type SafetyRules struct { @@ -63,39 +73,86 @@ func New( // This is a sentinel error and _expected_ during normal operation. // // All other errors are unexpected and potential symptoms of uncovered edge cases or corrupted internal state (fatal). -func (r *SafetyRules) ProduceVote(proposal *model.Proposal, curView uint64) (*model.Vote, error) { +func (r *SafetyRules) ProduceVote(signedProposal *model.SignedProposal, curView uint64) (*model.Vote, error) { + return r.produceVote(&signedProposal.Proposal, curView) +} + +// produceVote implements the core Safety Rules to validate whether it is safe to vote. +// This method is to be used to vote for other leaders' blocks as well as this node's own proposals +// under construction. We explicitly codify the important aspect that a proposer's signature for their +// own block is conceptually also just a vote (we explicitly use that property when aggregating votes and +// including the proposer's own vote into a QC). In order to express this conceptual equivalence in code, the +// voting logic in Safety Rules must also operate on an unsigned Proposal. +// +// The curView is taken as input to ensure SafetyRules will only vote for proposals at current view and prevent double voting. +// Returns: +// - (vote, nil): On the _first_ block for the current view that is safe to vote for. +// Subsequently, voter does _not_ vote for any other block with the same (or lower) view. +// - (nil, model.NoVoteError): If the voter decides that it does not want to vote for the given block. +// This is a sentinel error and _expected_ during normal operation. +// +// All other errors are unexpected and potential symptoms of uncovered edge cases or corrupted internal state (fatal). +func (r *SafetyRules) produceVote(proposal *model.Proposal, curView uint64) (*model.Vote, error) { block := proposal.Block // sanity checks: if curView != block.View { return nil, fmt.Errorf("expecting block for current view %d, but block's view is %d", curView, block.View) } - err := r.IsSafeToVote(proposal) + err := r.isSafeToVote(proposal) if err != nil { return nil, fmt.Errorf("not safe to vote for proposal %x: %w", proposal.Block.BlockID, err) } - // we expect that only valid proposals are submitted for voting - // we need to make sure that proposer is not ejected to decide to vote or not - _, err = r.committee.IdentityByBlock(block.BlockID, block.ProposerID) - if model.IsInvalidSignerError(err) { - // the proposer must be ejected since the proposal has already been validated, - // which ensures that the proposer was a valid committee member at the start of the epoch - return nil, model.NewNoVoteErrorf("proposer ejected: %w", err) - } + currentLeader, err := r.committee.LeaderForView(block.View) if err != nil { - return nil, fmt.Errorf("internal error retrieving Identity of proposer %x at block %x: %w", block.ProposerID, block.BlockID, err) + return nil, fmt.Errorf("expect to have a valid leader for view %d: %w", curView, err) } - - // Do not produce a vote for blocks where we are not a valid committee member. - // HotStuff will ask for a vote for the first block of the next epoch, even if we - // have zero weight in the next epoch. Such vote can't be used to produce valid QCs. - _, err = r.committee.IdentityByBlock(block.BlockID, r.committee.Self()) - if model.IsInvalidSignerError(err) { - return nil, model.NewNoVoteErrorf("I am not authorized to vote for block %x: %w", block.BlockID, err) + // This sanity check confirms that the proposal is from the correct leader of this view. In case this sanity check + // fails, we return an exception, because the compliance layer should have verified this already. However, proposals + // from this node might not go through the compliance engine, and must be signed before anyway. Therefore, + // we still include this sanity check, but return an exception because signing a proposal should be only for views + // where this node is actually the leader. + if block.ProposerID != currentLeader { + return nil, fmt.Errorf("incorrect proposal, as proposer %x is different from the leader %x for view %d", block.ProposerID, currentLeader, curView) } - if err != nil { - return nil, fmt.Errorf("could not get self identity: %w", err) + + // In case this node is the leader, we can skip the following checks. + // • If this node is ejected (check (ii) would fail), voting for any blocks or signing own proposals is of no harm. + // This is because all other honest nodes should have terminated their connection to us, so we are not risking + // to use up the networking bandwidth of honest nodes. This is relevant in case of self-ejection: a node + // operator suspecting their node's keys to be compromised can request for their node to be ejected to prevent + // malicious actors impersonating their node, launching an attack on the network, and the stake being slashed. + // The self-ejection mechanism corresponds to key-revocation and reduces attack surface for the network and + // the node operator's stake. In case of self-ejection, a node is no longer part of the network, hence it cannot + // harm the network and is no longer subject to slashing for actions during the respective views. Therefore, + // voting or continuing to signing block proposals is of no concern. + // • In case this node is the leader, `block.ProposerID` and `r.committee.Self()` are identical. In other words, + // check (i) also verifies that this node itself is not ejected -- the same as check (ii). Hence, also check + // (i) can be skipped with the same reasoning. + if currentLeader != r.committee.Self() { + // (i): we need to make sure that proposer is not ejected to vote + _, err = r.committee.IdentityByBlock(block.BlockID, block.ProposerID) + if model.IsInvalidSignerError(err) { + // the proposer must be ejected since the proposal has already been validated, + // which ensures that the proposer was a valid committee member at the start of the epoch + return nil, model.NewNoVoteErrorf("proposer ejected: %w", err) + } + if err != nil { + return nil, fmt.Errorf("internal error retrieving Identity of proposer %x at block %x: %w", block.ProposerID, block.BlockID, err) + } + + // (ii) Do not produce a vote for blocks where we are not an active committee member. The HotStuff + // state machine may request to vote during grace periods outside the epochs, where the node is + // authorized to actively participate. If we voted during those grace periods, we would needlessly + // waste network bandwidth, as such votes can't be used to produce valid QCs. + _, err = r.committee.IdentityByBlock(block.BlockID, r.committee.Self()) + if model.IsInvalidSignerError(err) { + return nil, model.NewNoVoteErrorf("I am not authorized to vote for block %x: %w", block.BlockID, err) + } + if err != nil { + return nil, fmt.Errorf("could not get self identity: %w", err) + } } vote, err := r.signer.CreateVote(block) @@ -172,16 +229,39 @@ func (r *SafetyRules) ProduceTimeout(curView uint64, newestQC *flow.QuorumCertif return timeout, nil } -// IsSafeToVote checks if this proposal is valid in terms of voting rules, if voting for this proposal won't break safety rules. +// SignOwnProposal takes an unsigned block proposal and produces a vote for it. Vote is a cryptographic commitment +// to the proposal. By adding the vote to an unsigned proposal, the caller constructs a signed block proposal. This +// method has to be used only by the leader, which must be the proposer of the block (or an exception is returned). +// Implementors must guarantee that: +// - vote on the proposal satisfies safety rules +// - maximum one proposal is signed per view +// Returns: +// - (vote, nil): the passed unsigned proposal is a valid one, and it's safe to make a proposal. +// Subsequently, leader does _not_ produce any _other_ proposal with the same (or lower) view. +// - (nil, model.NoVoteError): according to HotStuff's Safety Rules, it is not safe to sign the given proposal. +// This could happen because we have already proposed or timed out for the given view. +// This is a sentinel error and _expected_ during normal operation. +// +// All other errors are unexpected and potential symptoms of uncovered edge cases or corrupted internal state (fatal). +func (r *SafetyRules) SignOwnProposal(unsignedProposal *model.Proposal) (*model.Vote, error) { + // check that the block is created by us + if unsignedProposal.Block.ProposerID != r.committee.Self() { + return nil, fmt.Errorf("can't sign proposal for someone else's block") + } + + return r.produceVote(unsignedProposal, unsignedProposal.Block.View) +} + +// isSafeToVote checks if this proposal is valid in terms of voting rules, if voting for this proposal won't break safety rules. // Expected errors during normal operations: // - NoVoteError if replica already acted during this view (either voted or generated timeout) -func (r *SafetyRules) IsSafeToVote(proposal *model.Proposal) error { +func (r *SafetyRules) isSafeToVote(proposal *model.Proposal) error { blockView := proposal.Block.View err := r.validateEvidenceForEnteringView(blockView, proposal.Block.QC, proposal.LastViewTC) if err != nil { // As we are expecting the blocks to be pre-validated, any failure here is a symptom of an internal bug. - return fmt.Errorf("proposal failed consensus validity check") + return fmt.Errorf("proposal failed consensus validity check: %w", err) } // This check satisfies voting rule 1 diff --git a/consensus/hotstuff/safetyrules/safety_rules_test.go b/consensus/hotstuff/safetyrules/safety_rules_test.go index 6309394e880..8034c76793b 100644 --- a/consensus/hotstuff/safetyrules/safety_rules_test.go +++ b/consensus/hotstuff/safetyrules/safety_rules_test.go @@ -31,7 +31,7 @@ type SafetyRulesTestSuite struct { suite.Suite bootstrapBlock *model.Block - proposal *model.Proposal + proposal *model.SignedProposal proposerIdentity *flow.Identity ourIdentity *flow.Identity signer *mocks.Signer @@ -50,15 +50,16 @@ func (s *SafetyRulesTestSuite) SetupTest() { // bootstrap at random bootstrapBlock s.bootstrapBlock = helper.MakeBlock(helper.WithBlockView(100)) - s.proposal = helper.MakeProposal( + s.proposal = helper.MakeSignedProposal(helper.WithProposal(helper.MakeProposal( helper.WithBlock( helper.MakeBlock( helper.WithParentBlock(s.bootstrapBlock), helper.WithBlockView(s.bootstrapBlock.View+1), helper.WithBlockProposer(s.proposerIdentity.NodeID)), - )) + )))) s.committee.On("Self").Return(s.ourIdentity.NodeID).Maybe() + s.committee.On("LeaderForView", mock.Anything).Return(s.proposerIdentity.NodeID, nil).Maybe() s.committee.On("IdentityByBlock", mock.Anything, s.ourIdentity.NodeID).Return(s.ourIdentity, nil).Maybe() s.committee.On("IdentityByBlock", s.proposal.Block.BlockID, s.proposal.Block.ProposerID).Return(s.proposerIdentity, nil).Maybe() s.committee.On("IdentityByEpoch", mock.Anything, s.ourIdentity.NodeID).Return(&s.ourIdentity.IdentitySkeleton, nil).Maybe() @@ -103,13 +104,13 @@ func (s *SafetyRulesTestSuite) TestProduceVote_ShouldVote() { helper.WithTCNewestQC(s.proposal.Block.QC)) // voting on proposal where last view ended with TC - proposalWithTC := helper.MakeProposal( + proposalWithTC := helper.MakeSignedProposal(helper.WithProposal(helper.MakeProposal( helper.WithBlock( helper.MakeBlock( helper.WithParentBlock(s.bootstrapBlock), helper.WithBlockView(s.proposal.Block.View+2), helper.WithBlockProposer(s.proposerIdentity.NodeID))), - helper.WithLastViewTC(lastViewTC)) + helper.WithLastViewTC(lastViewTC)))) expectedSafetyData = &hotstuff.SafetyData{ LockedOneChainView: s.proposal.Block.QC.View, @@ -138,13 +139,13 @@ func (s *SafetyRulesTestSuite) TestProduceVote_IncludedQCHigherThanTCsQC() { helper.WithTCNewestQC(s.proposal.Block.QC)) // voting on proposal where last view ended with TC - proposalWithTC := helper.MakeProposal( + proposalWithTC := helper.MakeSignedProposal(helper.WithProposal(helper.MakeProposal( helper.WithBlock( helper.MakeBlock( helper.WithParentBlock(s.proposal.Block), helper.WithBlockView(s.proposal.Block.View+2), helper.WithBlockProposer(s.proposerIdentity.NodeID))), - helper.WithLastViewTC(lastViewTC)) + helper.WithLastViewTC(lastViewTC)))) expectedSafetyData := &hotstuff.SafetyData{ LockedOneChainView: proposalWithTC.Block.QC.View, @@ -209,13 +210,13 @@ func (s *SafetyRulesTestSuite) TestProduceVote_InvalidCurrentView() { }) s.Run("view-not-monotonously-increasing", func() { // create block with view < HighestAcknowledgedView - proposal := helper.MakeProposal( + proposal := helper.MakeSignedProposal(helper.WithProposal(helper.MakeProposal( helper.WithBlock( helper.MakeBlock( func(block *model.Block) { block.QC = helper.MakeQC(helper.WithQCView(s.safetyData.HighestAcknowledgedView - 2)) }, - helper.WithBlockView(s.safetyData.HighestAcknowledgedView-1)))) + helper.WithBlockView(s.safetyData.HighestAcknowledgedView-1)))))) vote, err := s.safety.ProduceVote(proposal, proposal.Block.View) require.Nil(s.T(), vote) require.Error(s.T(), err) @@ -225,10 +226,43 @@ func (s *SafetyRulesTestSuite) TestProduceVote_InvalidCurrentView() { s.persister.AssertNotCalled(s.T(), "PutSafetyData") } +// TestProduceVote_CommitteeLeaderException verifies that SafetyRules handles unexpected error returns from +// the DynamicCommittee correctly. Specifically, generic exceptions and `model.ErrViewForUnknownEpoch` +// returned by the committee when requesting the leader for the block's view is propagated up the call stack. +// SafetyRules should *not* wrap unexpected exceptions into an expected NoVoteError. +func (s *SafetyRulesTestSuite) TestProduceVote_CommitteeLeaderException() { + *s.committee = mocks.DynamicCommittee{} + for _, exception := range []error{ + errors.New("invalid-leader-identity"), + model.ErrViewForUnknownEpoch, + } { + s.committee.On("LeaderForView", s.proposal.Block.View).Return(nil, exception).Once() + vote, err := s.safety.ProduceVote(s.proposal, s.proposal.Block.View) + require.Nil(s.T(), vote) + require.ErrorIs(s.T(), err, exception) + require.False(s.T(), model.IsNoVoteError(err)) + s.persister.AssertNotCalled(s.T(), "PutSafetyData") + } +} + +// TestProduceVote_DifferentProposerFromLeader tests that no vote is created if the proposer is different from the leader for +// current view. This is a byzantine behavior and should be handled by the compliance layer but nevertheless we want to +// have a sanity check for other code paths like voting on an own proposal created by the current leader. +func (s *SafetyRulesTestSuite) TestProduceVote_DifferentProposerFromLeader() { + s.proposal.Block.ProposerID = unittest.IdentifierFixture() // different proposer + vote, err := s.safety.ProduceVote(s.proposal, s.proposal.Block.View) + require.Error(s.T(), err) + require.False(s.T(), model.IsNoVoteError(err)) + require.Nil(s.T(), vote) + s.persister.AssertNotCalled(s.T(), "PutSafetyData") +} + // TestProduceVote_NodeEjected tests that no vote is created if block proposer is ejected func (s *SafetyRulesTestSuite) TestProduceVote_ProposerEjected() { *s.committee = mocks.DynamicCommittee{} + s.committee.On("Self").Return(s.ourIdentity.NodeID).Maybe() s.committee.On("IdentityByBlock", s.proposal.Block.BlockID, s.proposal.Block.ProposerID).Return(nil, model.NewInvalidSignerErrorf("node-ejected")).Once() + s.committee.On("LeaderForView", s.proposal.Block.View).Return(s.proposerIdentity.NodeID, nil).Once() vote, err := s.safety.ProduceVote(s.proposal, s.proposal.Block.View) require.Nil(s.T(), vote) @@ -242,6 +276,8 @@ func (s *SafetyRulesTestSuite) TestProduceVote_ProposerEjected() { func (s *SafetyRulesTestSuite) TestProduceVote_InvalidProposerIdentity() { *s.committee = mocks.DynamicCommittee{} exception := errors.New("invalid-signer-identity") + s.committee.On("Self").Return(s.ourIdentity.NodeID).Maybe() + s.committee.On("LeaderForView", s.proposal.Block.View).Return(s.proposerIdentity.NodeID, nil).Once() s.committee.On("IdentityByBlock", s.proposal.Block.BlockID, s.proposal.Block.ProposerID).Return(nil, exception).Once() vote, err := s.safety.ProduceVote(s.proposal, s.proposal.Block.View) @@ -258,8 +294,9 @@ func (s *SafetyRulesTestSuite) TestProduceVote_InvalidProposerIdentity() { func (s *SafetyRulesTestSuite) TestProduceVote_NodeEjected() { *s.committee = mocks.DynamicCommittee{} s.committee.On("Self").Return(s.ourIdentity.NodeID) - s.committee.On("IdentityByBlock", s.proposal.Block.BlockID, s.ourIdentity.NodeID).Return(nil, model.NewInvalidSignerErrorf("node-ejected")).Once() + s.committee.On("LeaderForView", s.proposal.Block.View).Return(s.proposerIdentity.NodeID, nil).Once() s.committee.On("IdentityByBlock", s.proposal.Block.BlockID, s.proposal.Block.ProposerID).Return(s.proposerIdentity, nil).Maybe() + s.committee.On("IdentityByBlock", s.proposal.Block.BlockID, s.ourIdentity.NodeID).Return(nil, model.NewInvalidSignerErrorf("node-ejected")).Once() vote, err := s.safety.ProduceVote(s.proposal, s.proposal.Block.View) require.Nil(s.T(), vote) @@ -274,6 +311,7 @@ func (s *SafetyRulesTestSuite) TestProduceVote_InvalidVoterIdentity() { *s.committee = mocks.DynamicCommittee{} s.committee.On("Self").Return(s.ourIdentity.NodeID) exception := errors.New("invalid-signer-identity") + s.committee.On("LeaderForView", s.proposal.Block.View).Return(s.proposerIdentity.NodeID, nil).Once() s.committee.On("IdentityByBlock", s.proposal.Block.BlockID, s.proposal.Block.ProposerID).Return(s.proposerIdentity, nil).Maybe() s.committee.On("IdentityByBlock", s.proposal.Block.BlockID, s.ourIdentity.NodeID).Return(nil, exception).Once() @@ -324,12 +362,12 @@ func (s *SafetyRulesTestSuite) TestProduceVote_VotingOnInvalidProposals() { // a proposal which includes a QC for the previous round should not contain a TC s.Run("proposal-includes-last-view-qc-and-tc", func() { - proposal := helper.MakeProposal( + proposal := helper.MakeSignedProposal(helper.WithProposal(helper.MakeProposal( helper.WithBlock( helper.MakeBlock( helper.WithParentBlock(s.bootstrapBlock), helper.WithBlockView(s.bootstrapBlock.View+1))), - helper.WithLastViewTC(helper.MakeTC())) + helper.WithLastViewTC(helper.MakeTC())))) s.committee.On("IdentityByBlock", proposal.Block.BlockID, proposal.Block.ProposerID).Return(s.proposerIdentity, nil).Maybe() vote, err := s.safety.ProduceVote(proposal, proposal.Block.View) require.Error(s.T(), err) @@ -338,11 +376,11 @@ func (s *SafetyRulesTestSuite) TestProduceVote_VotingOnInvalidProposals() { }) s.Run("no-last-view-tc", func() { // create block where Block.View != Block.QC.View+1 and LastViewTC = nil - proposal := helper.MakeProposal( + proposal := helper.MakeSignedProposal(helper.WithProposal(helper.MakeProposal( helper.WithBlock( helper.MakeBlock( helper.WithParentBlock(s.bootstrapBlock), - helper.WithBlockView(s.bootstrapBlock.View+2)))) + helper.WithBlockView(s.bootstrapBlock.View+2)))))) vote, err := s.safety.ProduceVote(proposal, proposal.Block.View) require.Error(s.T(), err) require.False(s.T(), model.IsNoVoteError(err)) @@ -351,14 +389,14 @@ func (s *SafetyRulesTestSuite) TestProduceVote_VotingOnInvalidProposals() { s.Run("last-view-tc-invalid-view", func() { // create block where Block.View != Block.QC.View+1 and // Block.View != LastViewTC.View+1 - proposal := helper.MakeProposal( + proposal := helper.MakeSignedProposal(helper.WithProposal(helper.MakeProposal( helper.WithBlock( helper.MakeBlock( helper.WithParentBlock(s.bootstrapBlock), helper.WithBlockView(s.bootstrapBlock.View+2))), helper.WithLastViewTC( helper.MakeTC( - helper.WithTCView(s.bootstrapBlock.View)))) + helper.WithTCView(s.bootstrapBlock.View)))))) vote, err := s.safety.ProduceVote(proposal, proposal.Block.View) require.Error(s.T(), err) require.False(s.T(), model.IsNoVoteError(err)) @@ -368,7 +406,7 @@ func (s *SafetyRulesTestSuite) TestProduceVote_VotingOnInvalidProposals() { // create block where Block.View != Block.QC.View+1 and // Block.View == LastViewTC.View+1 and Block.QC.View >= Block.View // in this case block is not safe to extend since proposal includes QC which is newer than the proposal itself. - proposal := helper.MakeProposal( + proposal := helper.MakeSignedProposal(helper.WithProposal(helper.MakeProposal( helper.WithBlock( helper.MakeBlock( helper.WithParentBlock(s.bootstrapBlock), @@ -378,7 +416,7 @@ func (s *SafetyRulesTestSuite) TestProduceVote_VotingOnInvalidProposals() { })), helper.WithLastViewTC( helper.MakeTC( - helper.WithTCView(s.bootstrapBlock.View+1)))) + helper.WithTCView(s.bootstrapBlock.View+1)))))) vote, err := s.safety.ProduceVote(proposal, proposal.Block.View) require.Error(s.T(), err) require.False(s.T(), model.IsNoVoteError(err)) @@ -390,7 +428,7 @@ func (s *SafetyRulesTestSuite) TestProduceVote_VotingOnInvalidProposals() { // in this case block is not safe to extend since proposal is built on top of QC, which is lower // than QC presented in LastViewTC. TONewestQC := helper.MakeQC(helper.WithQCView(s.bootstrapBlock.View + 1)) - proposal := helper.MakeProposal( + proposal := helper.MakeSignedProposal(helper.WithProposal(helper.MakeProposal( helper.WithBlock( helper.MakeBlock( helper.WithParentBlock(s.bootstrapBlock), @@ -398,7 +436,7 @@ func (s *SafetyRulesTestSuite) TestProduceVote_VotingOnInvalidProposals() { helper.WithLastViewTC( helper.MakeTC( helper.WithTCView(s.bootstrapBlock.View+1), - helper.WithTCNewestQC(TONewestQC)))) + helper.WithTCNewestQC(TONewestQC)))))) vote, err := s.safety.ProduceVote(proposal, proposal.Block.View) require.Error(s.T(), err) require.False(s.T(), model.IsNoVoteError(err)) @@ -426,18 +464,25 @@ func (s *SafetyRulesTestSuite) TestProduceVote_VoteEquivocation() { require.NotNil(s.T(), vote) require.Equal(s.T(), expectedVote, vote) - equivocatingProposal := helper.MakeProposal( + equivocatingProposal := helper.MakeSignedProposal(helper.WithProposal(helper.MakeProposal( helper.WithBlock( helper.MakeBlock( helper.WithParentBlock(s.bootstrapBlock), helper.WithBlockView(s.bootstrapBlock.View+1), helper.WithBlockProposer(s.proposerIdentity.NodeID)), - )) + )))) - // voting at same view(event different proposal) should result in NoVoteError + // voting at same view(even different proposal) should result in NoVoteError vote, err = s.safety.ProduceVote(equivocatingProposal, s.proposal.Block.View) require.True(s.T(), model.IsNoVoteError(err)) require.Nil(s.T(), vote) + + s.proposal.Block.ProposerID = s.ourIdentity.NodeID + + // proposing at the same view should result in NoVoteError since we have already voted + vote, err = s.safety.SignOwnProposal(&s.proposal.Proposal) + require.True(s.T(), model.IsNoVoteError(err)) + require.Nil(s.T(), vote) } // TestProduceVote_AfterTimeout tests a scenario where we first timeout for view and then try to produce a vote for @@ -707,6 +752,82 @@ func (s *SafetyRulesTestSuite) TestProduceTimeout_NodeEjected() { s.persister.AssertNotCalled(s.T(), "PutSafetyData") } +// TestSignOwnProposal tests a happy path scenario where leader can sign his own proposal. +func (s *SafetyRulesTestSuite) TestSignOwnProposal() { + s.proposal.Block.ProposerID = s.ourIdentity.NodeID + expectedSafetyData := &hotstuff.SafetyData{ + LockedOneChainView: s.proposal.Block.QC.View, + HighestAcknowledgedView: s.proposal.Block.View, + } + expectedVote := makeVote(s.proposal.Block) + s.committee.On("LeaderForView").Unset() + s.committee.On("LeaderForView", s.proposal.Block.View).Return(s.ourIdentity.NodeID, nil).Once() + s.signer.On("CreateVote", s.proposal.Block).Return(expectedVote, nil).Once() + s.persister.On("PutSafetyData", expectedSafetyData).Return(nil).Once() + vote, err := s.safety.SignOwnProposal(&s.proposal.Proposal) + require.NoError(s.T(), err) + require.Equal(s.T(), vote, expectedVote) +} + +// TestSignOwnProposal_ProposalNotSelf tests that we cannot sign a proposal that is not ours. We +// verify that SafetyRules returns an exception and not the benign sentinel error NoVoteError. +func (s *SafetyRulesTestSuite) TestSignOwnProposal_ProposalNotSelf() { + vote, err := s.safety.SignOwnProposal(&s.proposal.Proposal) + require.Error(s.T(), err) + require.False(s.T(), model.IsNoVoteError(err)) + require.Nil(s.T(), vote) +} + +// TestSignOwnProposal_SelfInvalidLeader tests that we cannot sign a proposal if we are not the leader for the view. +// We verify that SafetyRules returns and exception and does not the benign sentinel error NoVoteError. +func (s *SafetyRulesTestSuite) TestSignOwnProposal_SelfInvalidLeader() { + s.proposal.Block.ProposerID = s.ourIdentity.NodeID + otherID := unittest.IdentifierFixture() + require.NotEqual(s.T(), otherID, s.ourIdentity.NodeID) + s.committee.On("LeaderForView").Unset() + s.committee.On("LeaderForView", s.proposal.Block.View).Return(otherID, nil).Once() + vote, err := s.safety.SignOwnProposal(&s.proposal.Proposal) + require.Error(s.T(), err) + require.False(s.T(), model.IsNoVoteError(err)) + require.Nil(s.T(), vote) +} + +// TestSignOwnProposal_ProposalEquivocation verifies that SafetyRules will refuse to sign multiple proposals for the same view. +// We require that leader complies with the following next rules: +// - leader proposes once per view +// - leader's proposals follow safety rules +// +// Signing repeatedly for one view (either proposals or voting) can lead to equivocating (byzantine behavior). +// Expect a `model.NoVoteError` sentinel in such scenario. +func (s *SafetyRulesTestSuite) TestSignOwnProposal_ProposalEquivocation() { + s.proposal.Block.ProposerID = s.ourIdentity.NodeID + expectedSafetyData := &hotstuff.SafetyData{ + LockedOneChainView: s.proposal.Block.QC.View, + HighestAcknowledgedView: s.proposal.Block.View, + } + expectedVote := makeVote(s.proposal.Block) + s.committee.On("LeaderForView").Unset() + s.committee.On("LeaderForView", s.proposal.Block.View).Return(s.ourIdentity.NodeID, nil).Once() + s.signer.On("CreateVote", s.proposal.Block).Return(expectedVote, nil).Once() + s.persister.On("PutSafetyData", expectedSafetyData).Return(nil).Once() + + vote, err := s.safety.SignOwnProposal(&s.proposal.Proposal) + require.NoError(s.T(), err) + require.Equal(s.T(), expectedVote, vote) + + // signing same proposal again should return an error since we have already created a proposal for this view + vote, err = s.safety.SignOwnProposal(&s.proposal.Proposal) + require.Error(s.T(), err) + require.True(s.T(), model.IsNoVoteError(err)) + require.Nil(s.T(), vote) + + // voting for same view should also return an error since we have already proposed + vote, err = s.safety.ProduceVote(s.proposal, s.proposal.Block.View) + require.Error(s.T(), err) + require.True(s.T(), model.IsNoVoteError(err)) + require.Nil(s.T(), vote) +} + func makeVote(block *model.Block) *model.Vote { return &model.Vote{ BlockID: block.BlockID, diff --git a/consensus/hotstuff/signer.go b/consensus/hotstuff/signer.go index 48d74c2bee4..94cbdc9b936 100644 --- a/consensus/hotstuff/signer.go +++ b/consensus/hotstuff/signer.go @@ -7,10 +7,6 @@ import ( // Signer is responsible for creating votes, proposals for a given block. type Signer interface { - // CreateProposal creates a proposal for the given block. No error returns - // are expected during normal operations (incl. presence of byz. actors). - CreateProposal(block *model.Block) (*model.Proposal, error) - // CreateVote creates a vote for the given block. No error returns are // expected during normal operations (incl. presence of byz. actors). CreateVote(block *model.Block) (*model.Vote, error) diff --git a/consensus/hotstuff/timeoutcollector/timeout_processor_test.go b/consensus/hotstuff/timeoutcollector/timeout_processor_test.go index e5f443d6898..309d1351b98 100644 --- a/consensus/hotstuff/timeoutcollector/timeout_processor_test.go +++ b/consensus/hotstuff/timeoutcollector/timeout_processor_test.go @@ -575,8 +575,9 @@ func createRealQC( block *model.Block, ) *flow.QuorumCertificate { leader := signers[0] - proposal, err := signerObjects[leader.NodeID].CreateProposal(block) + leaderVote, err := signerObjects[leader.NodeID].CreateVote(block) require.NoError(t, err) + proposal := helper.MakeSignedProposal(helper.WithProposal(helper.MakeProposal(helper.WithBlock(block))), helper.WithSigData(leaderVote.SigData)) var createdQC *flow.QuorumCertificate onQCCreated := func(qc *flow.QuorumCertificate) { diff --git a/consensus/hotstuff/validator.go b/consensus/hotstuff/validator.go index be3313e9f26..ff40c550a2a 100644 --- a/consensus/hotstuff/validator.go +++ b/consensus/hotstuff/validator.go @@ -24,7 +24,7 @@ type Validator interface { // During normal operations, the following error returns are expected: // * model.InvalidProposalError if the block is invalid // * model.ErrViewForUnknownEpoch if the proposal refers unknown epoch - ValidateProposal(proposal *model.Proposal) error + ValidateProposal(proposal *model.SignedProposal) error // ValidateVote checks the validity of a vote. // Returns the full entity for the voter. During normal operations, diff --git a/consensus/hotstuff/validator/metrics_wrapper.go b/consensus/hotstuff/validator/metrics_wrapper.go index 8876acef248..5bd2aad9bec 100644 --- a/consensus/hotstuff/validator/metrics_wrapper.go +++ b/consensus/hotstuff/validator/metrics_wrapper.go @@ -40,7 +40,7 @@ func (w ValidatorMetricsWrapper) ValidateTC(tc *flow.TimeoutCertificate) error { return err } -func (w ValidatorMetricsWrapper) ValidateProposal(proposal *model.Proposal) error { +func (w ValidatorMetricsWrapper) ValidateProposal(proposal *model.SignedProposal) error { processStart := time.Now() err := w.validator.ValidateProposal(proposal) w.metrics.ValidatorProcessingDuration(time.Since(processStart)) diff --git a/consensus/hotstuff/validator/validator.go b/consensus/hotstuff/validator/validator.go index 933c3751619..597f0b5360f 100644 --- a/consensus/hotstuff/validator/validator.go +++ b/consensus/hotstuff/validator/validator.go @@ -201,7 +201,7 @@ func (v *Validator) ValidateQC(qc *flow.QuorumCertificate) error { // - model.ErrViewForUnknownEpoch if the proposal refers unknown epoch // // Any other error should be treated as exception -func (v *Validator) ValidateProposal(proposal *model.Proposal) error { +func (v *Validator) ValidateProposal(proposal *model.SignedProposal) error { qc := proposal.Block.QC block := proposal.Block diff --git a/consensus/hotstuff/validator/validator_test.go b/consensus/hotstuff/validator/validator_test.go index 7683d7cbe0b..6c7e91ad0fa 100644 --- a/consensus/hotstuff/validator/validator_test.go +++ b/consensus/hotstuff/validator/validator_test.go @@ -35,7 +35,7 @@ type ProposalSuite struct { parent *model.Block block *model.Block voters flow.IdentitySkeletonList - proposal *model.Proposal + proposal *model.SignedProposal vote *model.Vote voter *flow.IdentitySkeleton committee *mocks.Replicas @@ -70,7 +70,7 @@ func (ps *ProposalSuite) SetupTest() { require.NoError(ps.T(), err) ps.voters = ps.participants.Filter(filter.HasNodeID[flow.Identity](voterIDs...)).ToSkeleton() - ps.proposal = &model.Proposal{Block: ps.block} + ps.proposal = helper.MakeSignedProposal(helper.WithProposal(helper.MakeProposal(helper.WithBlock(ps.block)))) ps.vote = ps.proposal.ProposerVote() ps.voter = ps.leader @@ -256,7 +256,7 @@ func (ps *ProposalSuite) TestProposalWithLastViewTC() { ps.committee.On("LeaderForView", mock.Anything).Return(ps.leader.NodeID, nil) ps.Run("happy-path", func() { - proposal := helper.MakeProposal( + proposal := helper.MakeSignedProposal(helper.WithProposal(helper.MakeProposal( helper.WithBlock(helper.MakeBlock( helper.WithBlockView(ps.block.View+2), helper.WithBlockProposer(ps.leader.NodeID), @@ -267,14 +267,14 @@ func (ps *ProposalSuite) TestProposalWithLastViewTC() { helper.WithTCSigners(ps.indices), helper.WithTCView(ps.block.View+1), helper.WithTCNewestQC(ps.block.QC))), - ) + ))) ps.verifier.On("VerifyTC", ps.voters, []byte(proposal.LastViewTC.SigData), proposal.LastViewTC.View, proposal.LastViewTC.NewestQCViews).Return(nil).Once() err := ps.validator.ValidateProposal(proposal) require.NoError(ps.T(), err) }) ps.Run("no-tc", func() { - proposal := helper.MakeProposal( + proposal := helper.MakeSignedProposal(helper.WithProposal(helper.MakeProposal( helper.WithBlock(helper.MakeBlock( helper.WithBlockView(ps.block.View+2), helper.WithBlockProposer(ps.leader.NodeID), @@ -282,14 +282,14 @@ func (ps *ProposalSuite) TestProposalWithLastViewTC() { helper.WithBlockQC(ps.block.QC)), ), // in this case proposal without LastViewTC is considered invalid - ) + ))) err := ps.validator.ValidateProposal(proposal) require.True(ps.T(), model.IsInvalidProposalError(err)) ps.verifier.AssertNotCalled(ps.T(), "VerifyQC") ps.verifier.AssertNotCalled(ps.T(), "VerifyTC") }) ps.Run("tc-for-wrong-view", func() { - proposal := helper.MakeProposal( + proposal := helper.MakeSignedProposal(helper.WithProposal(helper.MakeProposal( helper.WithBlock(helper.MakeBlock( helper.WithBlockView(ps.block.View+2), helper.WithBlockProposer(ps.leader.NodeID), @@ -300,14 +300,14 @@ func (ps *ProposalSuite) TestProposalWithLastViewTC() { helper.WithTCSigners(ps.indices), helper.WithTCView(ps.block.View+10), // LastViewTC.View must be equal to Block.View-1 helper.WithTCNewestQC(ps.block.QC))), - ) + ))) err := ps.validator.ValidateProposal(proposal) require.True(ps.T(), model.IsInvalidProposalError(err)) ps.verifier.AssertNotCalled(ps.T(), "VerifyQC") ps.verifier.AssertNotCalled(ps.T(), "VerifyTC") }) ps.Run("proposal-not-safe-to-extend", func() { - proposal := helper.MakeProposal( + proposal := helper.MakeSignedProposal(helper.WithProposal(helper.MakeProposal( helper.WithBlock(helper.MakeBlock( helper.WithBlockView(ps.block.View+2), helper.WithBlockProposer(ps.leader.NodeID), @@ -319,14 +319,14 @@ func (ps *ProposalSuite) TestProposalWithLastViewTC() { helper.WithTCView(ps.block.View+1), // proposal is not safe to extend because included QC.View is higher that Block.QC.View helper.WithTCNewestQC(helper.MakeQC(helper.WithQCView(ps.block.View+1))))), - ) + ))) err := ps.validator.ValidateProposal(proposal) require.True(ps.T(), model.IsInvalidProposalError(err)) ps.verifier.AssertNotCalled(ps.T(), "VerifyQC") ps.verifier.AssertNotCalled(ps.T(), "VerifyTC") }) ps.Run("included-tc-highest-qc-not-highest", func() { - proposal := helper.MakeProposal( + proposal := helper.MakeSignedProposal(helper.WithProposal(helper.MakeProposal( helper.WithBlock(helper.MakeBlock( helper.WithBlockView(ps.block.View+2), helper.WithBlockProposer(ps.leader.NodeID), @@ -338,7 +338,7 @@ func (ps *ProposalSuite) TestProposalWithLastViewTC() { helper.WithTCView(ps.block.View+1), helper.WithTCNewestQC(ps.block.QC), )), - ) + ))) ps.verifier.On("VerifyTC", ps.voters, []byte(proposal.LastViewTC.SigData), proposal.LastViewTC.View, mock.Anything).Return(nil).Once() @@ -352,7 +352,7 @@ func (ps *ProposalSuite) TestProposalWithLastViewTC() { // TC is signed by only one signer - insufficient to reach weight threshold insufficientSignerIndices, err := signature.EncodeSignersToIndices(ps.participants.NodeIDs(), ps.participants.NodeIDs()[:1]) require.NoError(ps.T(), err) - proposal := helper.MakeProposal( + proposal := helper.MakeSignedProposal(helper.WithProposal(helper.MakeProposal( helper.WithBlock(helper.MakeBlock( helper.WithBlockView(ps.block.View+2), helper.WithBlockProposer(ps.leader.NodeID), @@ -364,7 +364,7 @@ func (ps *ProposalSuite) TestProposalWithLastViewTC() { helper.WithTCView(ps.block.View+1), helper.WithTCNewestQC(ps.block.QC), )), - ) + ))) err = ps.validator.ValidateProposal(proposal) require.True(ps.T(), model.IsInvalidProposalError(err) && model.IsInvalidTCError(err)) ps.verifier.AssertNotCalled(ps.T(), "VerifyTC") @@ -375,7 +375,7 @@ func (ps *ProposalSuite) TestProposalWithLastViewTC() { helper.WithQCView(ps.block.QC.View-1), helper.WithQCSigners(ps.indices)) - proposal := helper.MakeProposal( + proposal := helper.MakeSignedProposal(helper.WithProposal(helper.MakeProposal( helper.WithBlock(helper.MakeBlock( helper.WithBlockView(ps.block.View+2), helper.WithBlockProposer(ps.leader.NodeID), @@ -386,7 +386,7 @@ func (ps *ProposalSuite) TestProposalWithLastViewTC() { helper.WithTCSigners(ps.indices), helper.WithTCView(ps.block.View+1), helper.WithTCNewestQC(qc))), - ) + ))) ps.verifier.On("VerifyTC", ps.voters, []byte(proposal.LastViewTC.SigData), proposal.LastViewTC.View, proposal.LastViewTC.NewestQCViews).Return(nil).Once() ps.verifier.On("VerifyQC", ps.voters, qc.SigData, @@ -399,7 +399,7 @@ func (ps *ProposalSuite) TestProposalWithLastViewTC() { helper.WithQCView(ps.block.QC.View-2), helper.WithQCSigners(ps.indices)) - proposal := helper.MakeProposal( + proposal := helper.MakeSignedProposal(helper.WithProposal(helper.MakeProposal( helper.WithBlock(helper.MakeBlock( helper.WithBlockView(ps.block.View+2), helper.WithBlockProposer(ps.leader.NodeID), @@ -410,7 +410,7 @@ func (ps *ProposalSuite) TestProposalWithLastViewTC() { helper.WithTCSigners(ps.indices), helper.WithTCView(ps.block.View+1), helper.WithTCNewestQC(newestQC))), - ) + ))) ps.verifier.On("VerifyTC", ps.voters, []byte(proposal.LastViewTC.SigData), proposal.LastViewTC.View, proposal.LastViewTC.NewestQCViews).Return(nil).Once() // Validating QC included in TC returns ErrViewForUnknownEpoch @@ -423,7 +423,7 @@ func (ps *ProposalSuite) TestProposalWithLastViewTC() { require.NotErrorIs(ps.T(), err, model.ErrViewForUnknownEpoch) }) ps.Run("included-tc-invalid-sig", func() { - proposal := helper.MakeProposal( + proposal := helper.MakeSignedProposal(helper.WithProposal(helper.MakeProposal( helper.WithBlock(helper.MakeBlock( helper.WithBlockView(ps.block.View+2), helper.WithBlockProposer(ps.leader.NodeID), @@ -434,7 +434,7 @@ func (ps *ProposalSuite) TestProposalWithLastViewTC() { helper.WithTCSigners(ps.indices), helper.WithTCView(ps.block.View+1), helper.WithTCNewestQC(ps.block.QC))), - ) + ))) ps.verifier.On("VerifyTC", ps.voters, []byte(proposal.LastViewTC.SigData), proposal.LastViewTC.View, proposal.LastViewTC.NewestQCViews).Return(model.ErrInvalidSignature).Once() err := ps.validator.ValidateProposal(proposal) @@ -443,7 +443,7 @@ func (ps *ProposalSuite) TestProposalWithLastViewTC() { proposal.LastViewTC.View, proposal.LastViewTC.NewestQCViews) }) ps.Run("last-view-successful-but-includes-tc", func() { - proposal := helper.MakeProposal( + proposal := helper.MakeSignedProposal(helper.WithProposal(helper.MakeProposal( helper.WithBlock(helper.MakeBlock( helper.WithBlockView(ps.finalized+1), helper.WithBlockProposer(ps.leader.NodeID), @@ -451,7 +451,7 @@ func (ps *ProposalSuite) TestProposalWithLastViewTC() { helper.WithParentBlock(ps.parent)), ), helper.WithLastViewTC(helper.MakeTC()), - ) + ))) err := ps.validator.ValidateProposal(proposal) require.True(ps.T(), model.IsInvalidProposalError(err)) ps.verifier.AssertNotCalled(ps.T(), "VerifyTC") diff --git a/consensus/hotstuff/verification/combined_signer_v2.go b/consensus/hotstuff/verification/combined_signer_v2.go index 49691eaaf79..afed7073f94 100644 --- a/consensus/hotstuff/verification/combined_signer_v2.go +++ b/consensus/hotstuff/verification/combined_signer_v2.go @@ -52,29 +52,6 @@ func NewCombinedSigner( return sc } -// CreateProposal will create a proposal with a combined signature for the given block. -func (c *CombinedSigner) CreateProposal(block *model.Block) (*model.Proposal, error) { - - // check that the block is created by us - if block.ProposerID != c.staking.NodeID() { - return nil, fmt.Errorf("can't create proposal for someone else's block") - } - - // create the signature data - sigData, err := c.genSigData(block) - if err != nil { - return nil, fmt.Errorf("signing my proposal failed: %w", err) - } - - // create the proposal - proposal := &model.Proposal{ - Block: block, - SigData: sigData, - } - - return proposal, nil -} - // CreateVote will create a vote with a combined signature for the given block. func (c *CombinedSigner) CreateVote(block *model.Block) (*model.Vote, error) { diff --git a/consensus/hotstuff/verification/combined_signer_v2_test.go b/consensus/hotstuff/verification/combined_signer_v2_test.go index b736e1ebb27..4f31f730e72 100644 --- a/consensus/hotstuff/verification/combined_signer_v2_test.go +++ b/consensus/hotstuff/verification/combined_signer_v2_test.go @@ -8,8 +8,10 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + "github.com/onflow/flow-go/consensus/hotstuff" "github.com/onflow/flow-go/consensus/hotstuff/mocks" "github.com/onflow/flow-go/consensus/hotstuff/model" + "github.com/onflow/flow-go/consensus/hotstuff/safetyrules" "github.com/onflow/flow-go/consensus/hotstuff/signature" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" @@ -28,23 +30,26 @@ func TestCombinedSignWithBeaconKey(t *testing.T) { // prepare data beaconKey := unittest.RandomBeaconPriv() pk := beaconKey.PublicKey() - view := uint64(20) + proposerView := uint64(20) + proposerIdentity := identities[0] fblock := unittest.BlockFixture() - fblock.Header.ProposerID = identities[0].NodeID - fblock.Header.View = view - block := model.BlockFromFlow(fblock.Header) + fblock.Header.ProposerID = proposerIdentity.NodeID + fblock.Header.View = proposerView + fblock.Header.ParentView = proposerView - 1 + fblock.Header.LastViewTC = nil + proposal := model.ProposalFromFlow(fblock.Header) signerID := fblock.Header.ProposerID beaconKeyStore := modulemock.NewRandomBeaconKeyStore(t) - beaconKeyStore.On("ByView", view).Return(beaconKey, nil) + beaconKeyStore.On("ByView", proposerView).Return(beaconKey, nil) + ourIdentity := unittest.IdentityFixture() stakingPriv := unittest.StakingPrivKeyFixture() - nodeID := &unittest.IdentityFixture().IdentitySkeleton - nodeID.NodeID = signerID - nodeID.StakingPubKey = stakingPriv.PublicKey() + ourIdentity.NodeID = signerID + ourIdentity.StakingPubKey = stakingPriv.PublicKey() - me, err := local.New(*nodeID, stakingPriv) + me, err := local.New(ourIdentity.IdentitySkeleton, stakingPriv) require.NoError(t, err) signer := NewCombinedSigner(me, beaconKeyStore) @@ -53,19 +58,32 @@ func TestCombinedSignWithBeaconKey(t *testing.T) { committee := &mocks.DynamicCommittee{} committee.On("DKG", mock.Anything).Return(dkg, nil) + committee.On("Self").Return(me.NodeID()) + committee.On("IdentityByBlock", fblock.Header.ID(), fblock.Header.ProposerID).Return(proposerIdentity, nil) + committee.On("LeaderForView", proposerView).Return(signerID, nil).Maybe() packer := signature.NewConsensusSigDataPacker(committee) verifier := NewCombinedVerifier(committee, packer) - // check that a created proposal can be verified by a verifier - proposal, err := signer.CreateProposal(block) + persist := mocks.NewPersister(t) + safetyData := &hotstuff.SafetyData{ + LockedOneChainView: fblock.Header.ParentView, + HighestAcknowledgedView: fblock.Header.ParentView, + } + persist.On("GetSafetyData", mock.Anything).Return(safetyData, nil).Once() + persist.On("PutSafetyData", mock.Anything).Return(nil) + safetyRules, err := safetyrules.New(signer, persist, committee) + require.NoError(t, err) + + // check that the proposer's vote for their own block (i.e. the proposer signature in the header) passes verification + vote, err := safetyRules.SignOwnProposal(proposal) require.NoError(t, err) - vote := proposal.ProposerVote() - err = verifier.VerifyVote(nodeID, vote.SigData, proposal.Block.View, proposal.Block.BlockID) + err = verifier.VerifyVote(&ourIdentity.IdentitySkeleton, vote.SigData, proposal.Block.View, proposal.Block.BlockID) require.NoError(t, err) // check that a created proposal's signature is a combined staking sig and random beacon sig + block := proposal.Block msg := MakeVoteMessage(block.View, block.BlockID) stakingSig, err := stakingPriv.Sign(msg, msig.NewBLSHasher(msig.ConsensusVoteTag)) require.NoError(t, err) @@ -74,25 +92,25 @@ func TestCombinedSignWithBeaconKey(t *testing.T) { require.NoError(t, err) expectedSig := msig.EncodeDoubleSig(stakingSig, beaconSig) - require.Equal(t, expectedSig, proposal.SigData) + require.Equal(t, expectedSig, vote.SigData) // vote should be valid vote, err = signer.CreateVote(block) require.NoError(t, err) - err = verifier.VerifyVote(nodeID, vote.SigData, proposal.Block.View, proposal.Block.BlockID) + err = verifier.VerifyVote(&ourIdentity.IdentitySkeleton, vote.SigData, proposal.Block.View, proposal.Block.BlockID) require.NoError(t, err) // vote on different block should be invalid blockWrongID := *block blockWrongID.BlockID[0]++ - err = verifier.VerifyVote(nodeID, vote.SigData, blockWrongID.View, blockWrongID.BlockID) + err = verifier.VerifyVote(&ourIdentity.IdentitySkeleton, vote.SigData, blockWrongID.View, blockWrongID.BlockID) require.ErrorIs(t, err, model.ErrInvalidSignature) - // vote with a wrong view should be invalid + // vote with a wrong proposerView should be invalid blockWrongView := *block blockWrongView.View++ - err = verifier.VerifyVote(nodeID, vote.SigData, blockWrongID.View, blockWrongID.BlockID) + err = verifier.VerifyVote(&ourIdentity.IdentitySkeleton, vote.SigData, blockWrongID.View, blockWrongID.BlockID) require.ErrorIs(t, err, model.ErrInvalidSignature) // vote by different signer should be invalid @@ -104,7 +122,7 @@ func TestCombinedSignWithBeaconKey(t *testing.T) { // vote with changed signature should be invalid brokenSig := append([]byte{}, vote.SigData...) // copy brokenSig[4]++ - err = verifier.VerifyVote(nodeID, brokenSig, block.View, block.BlockID) + err = verifier.VerifyVote(&ourIdentity.IdentitySkeleton, brokenSig, block.View, block.BlockID) require.ErrorIs(t, err, model.ErrInvalidSignature) // Vote from a node that is _not_ part of the Random Beacon committee should be rejected. @@ -112,7 +130,7 @@ func TestCombinedSignWithBeaconKey(t *testing.T) { // as a sign of an invalid vote and wraps it into a `model.InvalidSignerError`. *dkg = mocks.DKG{} // overwrite DKG mock with a new one dkg.On("KeyShare", signerID).Return(nil, protocol.IdentityNotFoundError{NodeID: signerID}) - err = verifier.VerifyVote(nodeID, vote.SigData, proposal.Block.View, proposal.Block.BlockID) + err = verifier.VerifyVote(&ourIdentity.IdentitySkeleton, vote.SigData, proposal.Block.View, proposal.Block.BlockID) require.True(t, model.IsInvalidSignerError(err)) } @@ -122,22 +140,24 @@ func TestCombinedSignWithNoBeaconKey(t *testing.T) { // prepare data beaconKey := unittest.RandomBeaconPriv() pk := beaconKey.PublicKey() - view := uint64(20) + proposerView := uint64(20) fblock := unittest.BlockFixture() - fblock.Header.View = view - block := model.BlockFromFlow(fblock.Header) + fblock.Header.View = proposerView + fblock.Header.ParentView = proposerView - 1 + fblock.Header.LastViewTC = nil + proposal := model.ProposalFromFlow(fblock.Header) signerID := fblock.Header.ProposerID beaconKeyStore := modulemock.NewRandomBeaconKeyStore(t) - beaconKeyStore.On("ByView", view).Return(nil, module.ErrNoBeaconKeyForEpoch) + beaconKeyStore.On("ByView", proposerView).Return(nil, module.ErrNoBeaconKeyForEpoch) + ourIdentity := unittest.IdentityFixture() stakingPriv := unittest.StakingPrivKeyFixture() - nodeID := &unittest.IdentityFixture().IdentitySkeleton - nodeID.NodeID = signerID - nodeID.StakingPubKey = stakingPriv.PublicKey() + ourIdentity.NodeID = signerID + ourIdentity.StakingPubKey = stakingPriv.PublicKey() - me, err := local.New(*nodeID, stakingPriv) + me, err := local.New(ourIdentity.IdentitySkeleton, stakingPriv) require.NoError(t, err) signer := NewCombinedSigner(me, beaconKeyStore) @@ -150,25 +170,38 @@ func TestCombinedSignWithNoBeaconKey(t *testing.T) { // for this failed node, which can be used to verify signature from // this failed node. committee.On("DKG", mock.Anything).Return(dkg, nil) + committee.On("Self").Return(me.NodeID()) + committee.On("IdentityByBlock", fblock.Header.ID(), signerID).Return(ourIdentity, nil) + committee.On("LeaderForView", mock.Anything).Return(signerID, nil).Maybe() packer := signature.NewConsensusSigDataPacker(committee) verifier := NewCombinedVerifier(committee, packer) - proposal, err := signer.CreateProposal(block) + persist := mocks.NewPersister(t) + safetyData := &hotstuff.SafetyData{ + LockedOneChainView: fblock.Header.ParentView, + HighestAcknowledgedView: fblock.Header.ParentView, + } + persist.On("GetSafetyData", mock.Anything).Return(safetyData, nil).Once() + persist.On("PutSafetyData", mock.Anything).Return(nil) + safetyRules, err := safetyrules.New(signer, persist, committee) + require.NoError(t, err) + + // check that the proposer's vote for their own block (i.e. the proposer signature in the header) passes verification + vote, err := safetyRules.SignOwnProposal(proposal) require.NoError(t, err) - vote := proposal.ProposerVote() - err = verifier.VerifyVote(nodeID, vote.SigData, proposal.Block.View, proposal.Block.BlockID) + err = verifier.VerifyVote(&ourIdentity.IdentitySkeleton, vote.SigData, proposal.Block.View, proposal.Block.BlockID) require.NoError(t, err) // As the proposer does not have a Random Beacon Key, it should sign solely with its staking key. // In this case, the SigData should be identical to the staking sig. expectedStakingSig, err := stakingPriv.Sign( - MakeVoteMessage(block.View, block.BlockID), + MakeVoteMessage(proposal.Block.View, proposal.Block.BlockID), msig.NewBLSHasher(msig.ConsensusVoteTag), ) require.NoError(t, err) - require.Equal(t, expectedStakingSig, crypto.Signature(proposal.SigData)) + require.Equal(t, expectedStakingSig, crypto.Signature(vote.SigData)) } // Test_VerifyQC_EmptySigners checks that Verifier returns an `model.InsufficientSignaturesError` diff --git a/consensus/hotstuff/verification/combined_signer_v3.go b/consensus/hotstuff/verification/combined_signer_v3.go index 09651fc4925..a496af91387 100644 --- a/consensus/hotstuff/verification/combined_signer_v3.go +++ b/consensus/hotstuff/verification/combined_signer_v3.go @@ -53,29 +53,6 @@ func NewCombinedSignerV3( return sc } -// CreateProposal will create a proposal with a combined signature for the given block. -func (c *CombinedSignerV3) CreateProposal(block *model.Block) (*model.Proposal, error) { - - // check that the block is created by us - if block.ProposerID != c.staking.NodeID() { - return nil, fmt.Errorf("can't create proposal for someone else's block") - } - - // create the signature data - sigData, err := c.genSigData(block) - if err != nil { - return nil, fmt.Errorf("signing my proposal failed: %w", err) - } - - // create the proposal - proposal := &model.Proposal{ - Block: block, - SigData: sigData, - } - - return proposal, nil -} - // CreateVote will create a vote with a combined signature for the given block. func (c *CombinedSignerV3) CreateVote(block *model.Block) (*model.Vote, error) { diff --git a/consensus/hotstuff/verification/combined_signer_v3_test.go b/consensus/hotstuff/verification/combined_signer_v3_test.go index eaab5d6ac47..9317268ce59 100644 --- a/consensus/hotstuff/verification/combined_signer_v3_test.go +++ b/consensus/hotstuff/verification/combined_signer_v3_test.go @@ -29,15 +29,15 @@ func TestCombinedSignWithBeaconKeyV3(t *testing.T) { // prepare data beaconKey := unittest.RandomBeaconPriv() pk := beaconKey.PublicKey() - view := uint64(20) + proposerView := uint64(20) fblock := unittest.BlockFixture() - fblock.Header.View = view + fblock.Header.View = proposerView block := model.BlockFromFlow(fblock.Header) signerID := fblock.Header.ProposerID beaconKeyStore := modulemock.NewRandomBeaconKeyStore(t) - beaconKeyStore.On("ByView", view).Return(beaconKey, nil) + beaconKeyStore.On("ByView", proposerView).Return(beaconKey, nil) stakingPriv := unittest.StakingPrivKeyFixture() nodeID := &unittest.IdentityFixture().IdentitySkeleton @@ -57,12 +57,11 @@ func TestCombinedSignWithBeaconKeyV3(t *testing.T) { packer := signature.NewConsensusSigDataPacker(committee) verifier := NewCombinedVerifierV3(committee, packer) - // check that a created proposal can be verified by a verifier - proposal, err := signer.CreateProposal(block) + // check that the proposer's vote for their own block (i.e. the proposer signature in the header) passes verification + vote, err := signer.CreateVote(block) require.NoError(t, err) - vote := proposal.ProposerVote() - err = verifier.VerifyVote(nodeID, vote.SigData, proposal.Block.View, proposal.Block.BlockID) + err = verifier.VerifyVote(nodeID, vote.SigData, block.View, block.BlockID) require.NoError(t, err) // check that a created proposal's signature is a combined staking sig and random beacon sig @@ -72,14 +71,14 @@ func TestCombinedSignWithBeaconKeyV3(t *testing.T) { require.NoError(t, err) expectedSig := msig.EncodeSingleSig(encoding.SigTypeRandomBeacon, beaconSig) - require.Equal(t, expectedSig, proposal.SigData) + require.Equal(t, expectedSig, vote.SigData) // Vote from a node that is _not_ part of the Random Beacon committee should be rejected. // Specifically, we expect that the verifier recognizes the `protocol.IdentityNotFoundError` // as a sign of an invalid vote and wraps it into a `model.InvalidSignerError`. *dkg = mocks.DKG{} // overwrite DKG mock with a new one dkg.On("KeyShare", signerID).Return(nil, protocol.IdentityNotFoundError{NodeID: signerID}) - err = verifier.VerifyVote(nodeID, vote.SigData, proposal.Block.View, proposal.Block.BlockID) + err = verifier.VerifyVote(nodeID, vote.SigData, block.View, block.BlockID) require.True(t, model.IsInvalidSignerError(err)) } @@ -89,15 +88,15 @@ func TestCombinedSignWithNoBeaconKeyV3(t *testing.T) { // prepare data beaconKey := unittest.RandomBeaconPriv() pk := beaconKey.PublicKey() - view := uint64(20) + proposerView := uint64(20) fblock := unittest.BlockFixture() - fblock.Header.View = view + fblock.Header.View = proposerView block := model.BlockFromFlow(fblock.Header) signerID := fblock.Header.ProposerID beaconKeyStore := modulemock.NewRandomBeaconKeyStore(t) - beaconKeyStore.On("ByView", view).Return(nil, module.ErrNoBeaconKeyForEpoch) + beaconKeyStore.On("ByView", proposerView).Return(nil, module.ErrNoBeaconKeyForEpoch) stakingPriv := unittest.StakingPrivKeyFixture() nodeID := &unittest.IdentityFixture().IdentitySkeleton @@ -121,11 +120,11 @@ func TestCombinedSignWithNoBeaconKeyV3(t *testing.T) { packer := signature.NewConsensusSigDataPacker(committee) verifier := NewCombinedVerifierV3(committee, packer) - proposal, err := signer.CreateProposal(block) + // check that the proposer's vote for their own block (i.e. the proposer signature in the header) passes verification + vote, err := signer.CreateVote(block) require.NoError(t, err) - vote := proposal.ProposerVote() - err = verifier.VerifyVote(nodeID, vote.SigData, proposal.Block.View, proposal.Block.BlockID) + err = verifier.VerifyVote(nodeID, vote.SigData, block.View, block.BlockID) require.NoError(t, err) // check that a created proposal's signature is a combined staking sig and random beacon sig @@ -136,7 +135,7 @@ func TestCombinedSignWithNoBeaconKeyV3(t *testing.T) { expectedSig := msig.EncodeSingleSig(encoding.SigTypeStaking, stakingSig) // check the signature only has staking sig - require.Equal(t, expectedSig, proposal.SigData) + require.Equal(t, expectedSig, vote.SigData) } // Test_VerifyQC checks that a QC where either signer list is empty is rejected as invalid diff --git a/consensus/hotstuff/verification/common.go b/consensus/hotstuff/verification/common.go index 4febede7412..04c355f4390 100644 --- a/consensus/hotstuff/verification/common.go +++ b/consensus/hotstuff/verification/common.go @@ -1,6 +1,7 @@ package verification import ( + "encoding/binary" "fmt" "github.com/onflow/crypto" @@ -8,8 +9,6 @@ import ( "github.com/onflow/flow-go/consensus/hotstuff/model" "github.com/onflow/flow-go/model/flow" - - "encoding/binary" ) // MakeVoteMessage generates the message we have to sign in order to be able diff --git a/consensus/hotstuff/verification/metrics_wrapper.go b/consensus/hotstuff/verification/metrics_wrapper.go index 7c929e361ef..1f5b7691492 100644 --- a/consensus/hotstuff/verification/metrics_wrapper.go +++ b/consensus/hotstuff/verification/metrics_wrapper.go @@ -29,28 +29,6 @@ func NewMetricsWrapper(signer hotstuff.Signer, metrics module.HotstuffMetrics) * } } -// TODO: to be moved to VerifierMetricsWrapper -// func (w SignerMetricsWrapper) VerifyVote(voter *flow.Identity, sigData []byte, block *model.Block) (bool, error) { -// processStart := time.Now() -// valid, err := w.signer.VerifyVote(voter, sigData, block) -// w.metrics.SignerProcessingDuration(time.Since(processStart)) -// return valid, err -// } -// -// func (w SignerMetricsWrapper) VerifyQC(signers flow.IdentityList, sigData []byte, block *model.Block) (bool, error) { -// processStart := time.Now() -// valid, err := w.signer.VerifyQC(signers, sigData, block) -// w.metrics.SignerProcessingDuration(time.Since(processStart)) -// return valid, err -// } - -func (w SignerMetricsWrapper) CreateProposal(block *model.Block) (*model.Proposal, error) { - processStart := time.Now() - proposal, err := w.signer.CreateProposal(block) - w.metrics.SignerProcessingDuration(time.Since(processStart)) - return proposal, err -} - func (w SignerMetricsWrapper) CreateVote(block *model.Block) (*model.Vote, error) { processStart := time.Now() vote, err := w.signer.CreateVote(block) @@ -66,10 +44,3 @@ func (w SignerMetricsWrapper) CreateTimeout(curView uint64, w.metrics.SignerProcessingDuration(time.Since(processStart)) return timeout, err } - -// func (w SignerMetricsWrapper) CreateQC(votes []*model.Vote) (*flow.QuorumCertificate, error) { -// processStart := time.Now() -// qc, err := w.signer.CreateQC(votes) -// w.metrics.SignerProcessingDuration(time.Since(processStart)) -// return qc, err -// } diff --git a/consensus/hotstuff/verification/staking_signer.go b/consensus/hotstuff/verification/staking_signer.go index bbc590d2e07..cc1b9ca1291 100644 --- a/consensus/hotstuff/verification/staking_signer.go +++ b/consensus/hotstuff/verification/staking_signer.go @@ -40,29 +40,6 @@ func NewStakingSigner( return sc } -// CreateProposal will create a proposal with a staking signature for the given block. -func (c *StakingSigner) CreateProposal(block *model.Block) (*model.Proposal, error) { - - // check that the block is created by us - if block.ProposerID != c.signerID { - return nil, fmt.Errorf("can't create proposal for someone else's block") - } - - // create the signature data - sigData, err := c.genSigData(block) - if err != nil { - return nil, fmt.Errorf("signing my proposal failed: %w", err) - } - - // create the proposal - proposal := &model.Proposal{ - Block: block, - SigData: sigData, - } - - return proposal, nil -} - // CreateVote will create a vote with a staking signature for the given block. func (c *StakingSigner) CreateVote(block *model.Block) (*model.Vote, error) { diff --git a/consensus/hotstuff/verification/staking_signer_test.go b/consensus/hotstuff/verification/staking_signer_test.go index 69f31bdfed3..6fc4d14fdc5 100644 --- a/consensus/hotstuff/verification/staking_signer_test.go +++ b/consensus/hotstuff/verification/staking_signer_test.go @@ -15,57 +15,6 @@ import ( "github.com/onflow/flow-go/utils/unittest" ) -// TestStakingSigner_CreateProposal verifies that StakingSigner can produce correctly signed proposal -// that can be verified later using StakingVerifier. -// Additionally, we check cases where errors during signing are happening. -func TestStakingSigner_CreateProposal(t *testing.T) { - stakingPriv := unittest.StakingPrivKeyFixture() - signer := unittest.IdentityFixture() - signerID := signer.NodeID - signer.StakingPubKey = stakingPriv.PublicKey() - - t.Run("invalid-signer-id", func(t *testing.T) { - me := &modulemock.Local{} - me.On("NodeID").Return(signerID) - signer := NewStakingSigner(me) - - block := helper.MakeBlock() - proposal, err := signer.CreateProposal(block) - require.Error(t, err) - require.Nil(t, proposal) - }) - t.Run("could-not-sign", func(t *testing.T) { - signException := errors.New("sign-exception") - me := &modulemock.Local{} - me.On("NodeID").Return(signerID) - me.On("Sign", mock.Anything, mock.Anything).Return(nil, signException).Once() - signer := NewStakingSigner(me) - - block := helper.MakeBlock() - proposal, err := signer.CreateProposal(block) - require.ErrorAs(t, err, &signException) - require.Nil(t, proposal) - }) - t.Run("created-proposal", func(t *testing.T) { - me, err := local.New(signer.IdentitySkeleton, stakingPriv) - require.NoError(t, err) - - signerIdentity := &unittest.IdentityFixture(unittest.WithNodeID(signerID), - unittest.WithStakingPubKey(stakingPriv.PublicKey())).IdentitySkeleton - - signer := NewStakingSigner(me) - - block := helper.MakeBlock(helper.WithBlockProposer(signerID)) - proposal, err := signer.CreateProposal(block) - require.NoError(t, err) - require.NotNil(t, proposal) - - verifier := NewStakingVerifier() - err = verifier.VerifyVote(signerIdentity, proposal.SigData, proposal.Block.View, proposal.Block.BlockID) - require.NoError(t, err) - }) -} - // TestStakingSigner_CreateVote verifies that StakingSigner can produce correctly signed vote // that can be verified later using StakingVerifier. // Additionally, we check cases where errors during signing are happening. @@ -83,7 +32,7 @@ func TestStakingSigner_CreateVote(t *testing.T) { signer := NewStakingSigner(me) block := helper.MakeBlock() - proposal, err := signer.CreateProposal(block) + proposal, err := signer.CreateVote(block) require.ErrorAs(t, err, &signException) require.Nil(t, proposal) }) diff --git a/consensus/hotstuff/vote_aggregator.go b/consensus/hotstuff/vote_aggregator.go index 14dc4f7dc2f..7c9bbcaad01 100644 --- a/consensus/hotstuff/vote_aggregator.go +++ b/consensus/hotstuff/vote_aggregator.go @@ -25,12 +25,12 @@ type VoteAggregator interface { // CAUTION: we expect that the input block's validity has been confirmed prior to calling AddBlock, // including the proposer's signature. Otherwise, VoteAggregator might crash or exhibit undefined // behaviour. - AddBlock(block *model.Proposal) + AddBlock(block *model.SignedProposal) // InvalidBlock notifies the VoteAggregator about an invalid proposal, so that it // can process votes for the invalid block and slash the voters. // No errors are expected during normal operations - InvalidBlock(block *model.Proposal) error + InvalidBlock(block *model.SignedProposal) error // PruneUpToView deletes all votes _below_ to the given view, as well as // related indices. We only retain and process whose view is equal or larger diff --git a/consensus/hotstuff/vote_collector.go b/consensus/hotstuff/vote_collector.go index 157ef5338a7..3a259808dc4 100644 --- a/consensus/hotstuff/vote_collector.go +++ b/consensus/hotstuff/vote_collector.go @@ -61,7 +61,7 @@ type VoteCollector interface { // It returns nil if the block is valid. // It returns model.InvalidProposalError if block is invalid. // It returns other error if there is exception processing the block. - ProcessBlock(block *model.Proposal) error + ProcessBlock(block *model.SignedProposal) error // AddVote adds a vote to the collector // When enough votes have been added to produce a QC, the QC will be created asynchronously, and @@ -116,5 +116,5 @@ type VoteProcessorFactory interface { // Caller can be sure that proposal vote was successfully verified and processed. // Expected error returns during normal operations: // * model.InvalidProposalError - proposal has invalid proposer vote - Create(log zerolog.Logger, proposal *model.Proposal) (VerifyingVoteProcessor, error) + Create(log zerolog.Logger, proposal *model.SignedProposal) (VerifyingVoteProcessor, error) } diff --git a/consensus/hotstuff/voteaggregator/vote_aggregator.go b/consensus/hotstuff/voteaggregator/vote_aggregator.go index 6471cc6ada6..efb2e476bfc 100644 --- a/consensus/hotstuff/voteaggregator/vote_aggregator.go +++ b/consensus/hotstuff/voteaggregator/vote_aggregator.go @@ -156,7 +156,7 @@ func (va *VoteAggregator) processQueuedMessages(ctx context.Context) error { msg, ok := va.queuedBlocks.Pop() if ok { - block := msg.(*model.Proposal) + block := msg.(*model.SignedProposal) err := va.processQueuedBlock(block) if err != nil { return fmt.Errorf("could not process pending block %v: %w", block.Block.BlockID, err) @@ -224,7 +224,7 @@ func (va *VoteAggregator) processQueuedVote(vote *model.Vote) error { // including the proposer's signature. Otherwise, VoteAggregator might crash or exhibit undefined // behaviour. // No errors are expected during normal operation. -func (va *VoteAggregator) processQueuedBlock(block *model.Proposal) error { +func (va *VoteAggregator) processQueuedBlock(block *model.SignedProposal) error { // check if the block is for a view that has already been pruned (and is thus stale) if block.Block.View < va.lowestRetainedView.Value() { return nil @@ -293,7 +293,7 @@ func (va *VoteAggregator) AddVote(vote *model.Vote) { // CAUTION: we expect that the input block's validity has been confirmed prior to calling AddBlock, // including the proposer's signature. Otherwise, VoteAggregator might crash or exhibit undefined // behaviour. -func (va *VoteAggregator) AddBlock(block *model.Proposal) { +func (va *VoteAggregator) AddBlock(block *model.SignedProposal) { // It's ok to silently drop blocks in case our processing pipeline is full. // It means that we are probably catching up. if ok := va.queuedBlocks.Push(block); ok { @@ -306,7 +306,7 @@ func (va *VoteAggregator) AddBlock(block *model.Proposal) { // InvalidBlock notifies the VoteAggregator about an invalid proposal, so that it // can process votes for the invalid block and slash the voters. // No errors are expected during normal operations -func (va *VoteAggregator) InvalidBlock(proposal *model.Proposal) error { +func (va *VoteAggregator) InvalidBlock(proposal *model.SignedProposal) error { slashingVoteConsumer := func(vote *model.Vote) { if proposal.Block.BlockID == vote.BlockID { va.notifier.OnVoteForInvalidBlockDetected(vote, proposal) diff --git a/consensus/hotstuff/voteaggregator/vote_aggregator_test.go b/consensus/hotstuff/voteaggregator/vote_aggregator_test.go index 006ab52b744..acc88729eb1 100644 --- a/consensus/hotstuff/voteaggregator/vote_aggregator_test.go +++ b/consensus/hotstuff/voteaggregator/vote_aggregator_test.go @@ -84,13 +84,13 @@ func (s *VoteAggregatorTestSuite) TestOnFinalizedBlock() { // an input to AddBlock (only expects _valid_ blocks per API contract). // The exception should be propagated to the VoteAggregator's internal `ComponentManager`. func (s *VoteAggregatorTestSuite) TestProcessInvalidBlock() { - block := helper.MakeProposal( + block := helper.MakeSignedProposal(helper.WithProposal(helper.MakeProposal( helper.WithBlock( helper.MakeBlock( helper.WithBlockView(100), ), ), - ) + ))) processed := make(chan struct{}) collector := mocks.NewVoteCollector(s.T()) collector.On("ProcessBlock", block).Run(func(_ mock.Arguments) { diff --git a/consensus/hotstuff/votecollector/combined_vote_processor_v2_test.go b/consensus/hotstuff/votecollector/combined_vote_processor_v2_test.go index c5cf0a02326..74c07af04bc 100644 --- a/consensus/hotstuff/votecollector/combined_vote_processor_v2_test.go +++ b/consensus/hotstuff/votecollector/combined_vote_processor_v2_test.go @@ -19,6 +19,7 @@ import ( "github.com/onflow/flow-go/consensus/hotstuff/helper" mockhotstuff "github.com/onflow/flow-go/consensus/hotstuff/mocks" "github.com/onflow/flow-go/consensus/hotstuff/model" + "github.com/onflow/flow-go/consensus/hotstuff/safetyrules" "github.com/onflow/flow-go/consensus/hotstuff/signature" hsig "github.com/onflow/flow-go/consensus/hotstuff/signature" hotstuffvalidator "github.com/onflow/flow-go/consensus/hotstuff/validator" @@ -56,7 +57,7 @@ func (s *CombinedVoteProcessorV2TestSuite) SetupTest() { s.reconstructor = &mockhotstuff.RandomBeaconReconstructor{} s.packer = &mockhotstuff.Packer{} - s.proposal = helper.MakeProposal() + s.proposal = helper.MakeSignedProposal() s.minRequiredShares = 9 // we require 9 RB shares to reconstruct signature s.rbSharesTotal = 0 @@ -781,8 +782,8 @@ func TestCombinedVoteProcessorV2_PropertyCreatingQCLiveness(testifyT *testing.T) func TestCombinedVoteProcessorV2_BuildVerifyQC(t *testing.T) { epochCounter := uint64(3) epochLookup := &modulemock.EpochLookup{} - view := uint64(20) - epochLookup.On("EpochForView", view).Return(epochCounter, nil) + proposerView := uint64(20) + epochLookup.On("EpochForView", proposerView).Return(epochCounter, nil) // all committee members run DKG dkgData, err := bootstrapDKG.RandomBeaconKG(11, unittest.RandomBytes(32)) @@ -845,9 +846,14 @@ func TestCombinedVoteProcessorV2_BuildVerifyQC(t *testing.T) { } leader := stakingSigners[0] - - block := helper.MakeBlock(helper.WithBlockView(view), - helper.WithBlockProposer(leader.NodeID)) + parentBlock := helper.MakeBlock(helper.WithBlockView(proposerView - 1)) + proposal := helper.MakeProposal( + helper.WithBlock( + helper.MakeBlock( + helper.WithBlockView(proposerView), + helper.WithParentBlock(parentBlock), + helper.WithBlockProposer(leader.NodeID)))) + block := proposal.Block inmemDKG, err := inmem.DKGFromEncodable(inmem.EncodableDKG{ GroupKey: encodable.RandomBeaconPubKey{ @@ -858,10 +864,13 @@ func TestCombinedVoteProcessorV2_BuildVerifyQC(t *testing.T) { require.NoError(t, err) committee := &mockhotstuff.DynamicCommittee{} + committee.On("LeaderForView", block.View).Return(leader.NodeID, nil).Maybe() committee.On("QuorumThresholdForView", mock.Anything).Return(committees.WeightThresholdToBuildQC(allIdentities.ToSkeleton().TotalWeight()), nil) committee.On("IdentitiesByEpoch", block.View).Return(allIdentities.ToSkeleton(), nil) committee.On("IdentitiesByBlock", block.BlockID).Return(allIdentities, nil) + committee.On("IdentityByBlock", block.BlockID, leader.NodeID).Return(leader, nil) committee.On("DKG", block.View).Return(inmemDKG, nil) + committee.On("Self").Return(leader.NodeID) votes := make([]*model.Vote, 0, len(allIdentities)) @@ -874,8 +883,18 @@ func TestCombinedVoteProcessorV2_BuildVerifyQC(t *testing.T) { } // create and sign proposal - proposal, err := signers[leader.NodeID].CreateProposal(block) + persist := mockhotstuff.NewPersister(t) + safetyData := &hotstuff.SafetyData{ + LockedOneChainView: parentBlock.View, + HighestAcknowledgedView: parentBlock.View, + } + persist.On("GetSafetyData", mock.Anything).Return(safetyData, nil).Once() + persist.On("PutSafetyData", mock.Anything).Return(nil) + safetyRules, err := safetyrules.New(signers[leader.NodeID], persist, committee) + require.NoError(t, err) + vote, err := safetyRules.SignOwnProposal(proposal) require.NoError(t, err) + signedProposal := helper.MakeSignedProposal(helper.WithProposal(proposal), helper.WithSigData(vote.SigData)) qcCreated := false onQCCreated := func(qc *flow.QuorumCertificate) { @@ -893,7 +912,7 @@ func TestCombinedVoteProcessorV2_BuildVerifyQC(t *testing.T) { } voteProcessorFactory := NewCombinedVoteProcessorFactory(committee, onQCCreated) - voteProcessor, err := voteProcessorFactory.Create(unittest.Logger(), proposal) + voteProcessor, err := voteProcessorFactory.Create(unittest.Logger(), signedProposal) require.NoError(t, err) // process votes by new leader, this will result in producing new QC diff --git a/consensus/hotstuff/votecollector/combined_vote_processor_v3_test.go b/consensus/hotstuff/votecollector/combined_vote_processor_v3_test.go index 8cd54fb7b70..f68f5dedaff 100644 --- a/consensus/hotstuff/votecollector/combined_vote_processor_v3_test.go +++ b/consensus/hotstuff/votecollector/combined_vote_processor_v3_test.go @@ -58,7 +58,7 @@ func (s *CombinedVoteProcessorV3TestSuite) SetupTest() { s.rbSigAggregator = &mockhotstuff.WeightedSignatureAggregator{} s.reconstructor = &mockhotstuff.RandomBeaconReconstructor{} s.packer = &mockhotstuff.Packer{} - s.proposal = helper.MakeProposal() + s.proposal = helper.MakeSignedProposal() s.minRequiredShares = 9 // we require 9 RB shares to reconstruct signature s.thresholdTotalWeight, s.rbSharesTotal = atomic.Uint64{}, atomic.Uint64{} @@ -918,8 +918,8 @@ func TestCombinedVoteProcessorV3_PropertyCreatingQCLiveness(testifyT *testing.T) func TestCombinedVoteProcessorV3_BuildVerifyQC(t *testing.T) { epochCounter := uint64(3) epochLookup := &modulemock.EpochLookup{} - view := uint64(20) - epochLookup.On("EpochForView", view).Return(epochCounter, nil) + proposerView := uint64(20) + epochLookup.On("EpochForView", proposerView).Return(epochCounter, nil) dkgData, err := bootstrapDKG.RandomBeaconKG(11, unittest.RandomBytes(32)) require.NoError(t, err) @@ -981,9 +981,7 @@ func TestCombinedVoteProcessorV3_BuildVerifyQC(t *testing.T) { } leader := stakingSigners[0] - - block := helper.MakeBlock(helper.WithBlockView(view), - helper.WithBlockProposer(leader.NodeID)) + block := helper.MakeBlock(helper.WithBlockView(proposerView), helper.WithBlockProposer(leader.NodeID)) inmemDKG, err := inmem.DKGFromEncodable(inmem.EncodableDKG{ GroupKey: encodable.RandomBeaconPubKey{ @@ -1010,8 +1008,9 @@ func TestCombinedVoteProcessorV3_BuildVerifyQC(t *testing.T) { } // create and sign proposal - proposal, err := signers[leader.NodeID].CreateProposal(block) + leaderVote, err := signers[leader.NodeID].CreateVote(block) require.NoError(t, err) + proposal := helper.MakeSignedProposal(helper.WithProposal(helper.MakeProposal(helper.WithBlock(block))), helper.WithSigData(leaderVote.SigData)) qcCreated := false onQCCreated := func(qc *flow.QuorumCertificate) { diff --git a/consensus/hotstuff/votecollector/factory.go b/consensus/hotstuff/votecollector/factory.go index 2c515fc052c..b444bc35ca7 100644 --- a/consensus/hotstuff/votecollector/factory.go +++ b/consensus/hotstuff/votecollector/factory.go @@ -16,7 +16,7 @@ import ( // CAUTION: the baseFactory creates the VerifyingVoteProcessor for the given block. It // does _not_ check the proposer's vote for its own block. The API reflects this by // expecting a `model.Block` as input (which does _not_ contain the proposer vote) as -// opposed to `model.Proposal` (combines block with proposer's vote). +// opposed to `model.SignedProposal` (combines block with proposer's vote). // Therefore, baseFactory does _not_ implement `hotstuff.VoteProcessorFactory` by itself. // The VoteProcessorFactory adds the missing logic to verify the proposer's vote, by // wrapping the baseFactory (decorator pattern). @@ -40,7 +40,7 @@ var _ hotstuff.VoteProcessorFactory = (*VoteProcessorFactory)(nil) // A VerifyingVoteProcessor are only created for proposals with valid proposer votes. // Expected error returns during normal operations: // * model.InvalidProposalError - proposal has invalid proposer vote -func (f *VoteProcessorFactory) Create(log zerolog.Logger, proposal *model.Proposal) (hotstuff.VerifyingVoteProcessor, error) { +func (f *VoteProcessorFactory) Create(log zerolog.Logger, proposal *model.SignedProposal) (hotstuff.VerifyingVoteProcessor, error) { processor, err := f.baseFactory(log, proposal.Block) if err != nil { return nil, fmt.Errorf("instantiating vote processor for block %v failed: %w", proposal.Block.BlockID, err) diff --git a/consensus/hotstuff/votecollector/factory_test.go b/consensus/hotstuff/votecollector/factory_test.go index 9adeaef98f8..40207150e86 100644 --- a/consensus/hotstuff/votecollector/factory_test.go +++ b/consensus/hotstuff/votecollector/factory_test.go @@ -19,7 +19,7 @@ import ( func TestVoteProcessorFactory_CreateWithValidProposal(t *testing.T) { mockedFactory := mockhotstuff.VoteProcessorFactory{} - proposal := helper.MakeProposal() + proposal := helper.MakeSignedProposal() mockedProcessor := &mockhotstuff.VerifyingVoteProcessor{} mockedProcessor.On("Process", proposal.ProposerVote()).Return(nil).Once() mockedFactory.On("Create", unittest.Logger(), proposal).Return(mockedProcessor, nil).Once() @@ -44,7 +44,7 @@ func TestVoteProcessorFactory_CreateWithInvalidVote(t *testing.T) { mockedFactory := mockhotstuff.VoteProcessorFactory{} t.Run("invalid-vote", func(t *testing.T) { - proposal := helper.MakeProposal() + proposal := helper.MakeSignedProposal() mockedProcessor := &mockhotstuff.VerifyingVoteProcessor{} mockedProcessor.On("Process", proposal.ProposerVote()).Return(model.NewInvalidVoteErrorf(proposal.ProposerVote(), "")).Once() mockedFactory.On("Create", unittest.Logger(), proposal).Return(mockedProcessor, nil).Once() @@ -63,7 +63,7 @@ func TestVoteProcessorFactory_CreateWithInvalidVote(t *testing.T) { mockedProcessor.AssertExpectations(t) }) t.Run("process-vote-exception", func(t *testing.T) { - proposal := helper.MakeProposal() + proposal := helper.MakeSignedProposal() mockedProcessor := &mockhotstuff.VerifyingVoteProcessor{} exception := errors.New("process-exception") mockedProcessor.On("Process", proposal.ProposerVote()).Return(exception).Once() @@ -93,7 +93,7 @@ func TestVoteProcessorFactory_CreateWithInvalidVote(t *testing.T) { func TestVoteProcessorFactory_CreateProcessException(t *testing.T) { mockedFactory := mockhotstuff.VoteProcessorFactory{} - proposal := helper.MakeProposal() + proposal := helper.MakeSignedProposal() exception := errors.New("create-exception") mockedFactory.On("Create", unittest.Logger(), proposal).Return(nil, exception).Once() diff --git a/consensus/hotstuff/votecollector/staking_vote_processor_test.go b/consensus/hotstuff/votecollector/staking_vote_processor_test.go index 0b30fd46bd4..1b096419c4d 100644 --- a/consensus/hotstuff/votecollector/staking_vote_processor_test.go +++ b/consensus/hotstuff/votecollector/staking_vote_processor_test.go @@ -250,8 +250,8 @@ func (s *StakingVoteProcessorTestSuite) TestProcess_ConcurrentCreatingQC() { func TestStakingVoteProcessorV2_BuildVerifyQC(t *testing.T) { epochCounter := uint64(3) epochLookup := &modulemock.EpochLookup{} - view := uint64(20) - epochLookup.On("EpochForView", view).Return(epochCounter, nil) + proposerView := uint64(20) + epochLookup.On("EpochForView", proposerView).Return(epochCounter, nil) // signers hold objects that are created with private key and can sign votes and proposals signers := make(map[flow.Identifier]*verification.StakingSigner) @@ -267,7 +267,7 @@ func TestStakingVoteProcessorV2_BuildVerifyQC(t *testing.T) { }).Sort(flow.Canonical[flow.Identity]) leader := stakingSigners[0] - block := helper.MakeBlock(helper.WithBlockView(view), helper.WithBlockProposer(leader.NodeID)) + block := helper.MakeBlock(helper.WithBlockView(proposerView), helper.WithBlockProposer(leader.NodeID)) committee := &mockhotstuff.DynamicCommittee{} committee.On("IdentitiesByEpoch", block.View).Return(stakingSigners.ToSkeleton(), nil) @@ -285,8 +285,10 @@ func TestStakingVoteProcessorV2_BuildVerifyQC(t *testing.T) { } // create and sign proposal - proposal, err := signers[leader.NodeID].CreateProposal(block) + leaderVote, err := signers[leader.NodeID].CreateVote(block) require.NoError(t, err) + proposal := helper.MakeSignedProposal(helper.WithProposal( + helper.MakeProposal(helper.WithBlock(block))), helper.WithSigData(leaderVote.SigData)) qcCreated := false onQCCreated := func(qc *flow.QuorumCertificate) { diff --git a/consensus/hotstuff/votecollector/statemachine.go b/consensus/hotstuff/votecollector/statemachine.go index d62159ea9ef..60558cf2aaf 100644 --- a/consensus/hotstuff/votecollector/statemachine.go +++ b/consensus/hotstuff/votecollector/statemachine.go @@ -18,7 +18,7 @@ var ( ) // VerifyingVoteProcessorFactory generates hotstuff.VerifyingVoteCollector instances -type VerifyingVoteProcessorFactory = func(log zerolog.Logger, proposal *model.Proposal) (hotstuff.VerifyingVoteProcessor, error) +type VerifyingVoteProcessorFactory = func(log zerolog.Logger, proposal *model.SignedProposal) (hotstuff.VerifyingVoteProcessor, error) // VoteCollector implements a state machine for transition between different states of vote collector type VoteCollector struct { @@ -175,7 +175,7 @@ func (m *VoteCollector) View() uint64 { // CachingVotes -> VerifyingVotes // CachingVotes -> Invalid // VerifyingVotes -> Invalid -func (m *VoteCollector) ProcessBlock(proposal *model.Proposal) error { +func (m *VoteCollector) ProcessBlock(proposal *model.SignedProposal) error { if proposal.Block.View != m.View() { return fmt.Errorf("this VoteCollector requires a proposal for view %d but received block %v with view %d", @@ -243,7 +243,7 @@ func (m *VoteCollector) RegisterVoteConsumer(consumer hotstuff.VoteConsumer) { // Error returns: // * ErrDifferentCollectorState if the VoteCollector's state is _not_ `CachingVotes` // * all other errors are unexpected and potential symptoms of internal bugs or state corruption (fatal) -func (m *VoteCollector) caching2Verifying(proposal *model.Proposal) error { +func (m *VoteCollector) caching2Verifying(proposal *model.SignedProposal) error { blockID := proposal.Block.BlockID newProc, err := m.createVerifyingProcessor(m.log, proposal) if err != nil { diff --git a/consensus/hotstuff/votecollector/statemachine_test.go b/consensus/hotstuff/votecollector/statemachine_test.go index 007dcce1fe2..1f6409c3136 100644 --- a/consensus/hotstuff/votecollector/statemachine_test.go +++ b/consensus/hotstuff/votecollector/statemachine_test.go @@ -51,7 +51,7 @@ func (s *StateMachineTestSuite) SetupTest() { s.mockedProcessors = make(map[flow.Identifier]*mocks.VerifyingVoteProcessor) s.notifier = mocks.NewVoteAggregationConsumer(s.T()) - s.factoryMethod = func(log zerolog.Logger, block *model.Proposal) (hotstuff.VerifyingVoteProcessor, error) { + s.factoryMethod = func(log zerolog.Logger, block *model.SignedProposal) (hotstuff.VerifyingVoteProcessor, error) { if processor, found := s.mockedProcessors[block.Block.BlockID]; found { return processor, nil } @@ -64,7 +64,7 @@ func (s *StateMachineTestSuite) SetupTest() { // prepareMockedProcessor prepares a mocked processor and stores it in map, later it will be used // to mock behavior of verifying vote processor. -func (s *StateMachineTestSuite) prepareMockedProcessor(proposal *model.Proposal) *mocks.VerifyingVoteProcessor { +func (s *StateMachineTestSuite) prepareMockedProcessor(proposal *model.SignedProposal) *mocks.VerifyingVoteProcessor { processor := &mocks.VerifyingVoteProcessor{} processor.On("Block").Return(func() *model.Block { return proposal.Block @@ -78,7 +78,7 @@ func (s *StateMachineTestSuite) prepareMockedProcessor(proposal *model.Proposal) // when proposal processing can possibly change state of collector func (s *StateMachineTestSuite) TestStatus_StateTransitions() { block := helper.MakeBlock(helper.WithBlockView(s.view)) - proposal := helper.MakeProposal(helper.WithBlock(block)) + proposal := helper.MakeSignedProposal(helper.WithProposal(helper.MakeProposal(helper.WithBlock(block)))) s.prepareMockedProcessor(proposal) // by default, we should create in caching status @@ -90,9 +90,7 @@ func (s *StateMachineTestSuite) TestStatus_StateTransitions() { require.Equal(s.T(), hotstuff.VoteCollectorStatusVerifying, s.collector.Status()) // after submitting double proposal we should transfer into invalid state - err = s.collector.ProcessBlock(helper.MakeProposal( - helper.WithBlock( - helper.MakeBlock(helper.WithBlockView(s.view))))) + err = s.collector.ProcessBlock(makeSignedProposalWithView(s.view)) require.NoError(s.T(), err) require.Equal(s.T(), hotstuff.VoteCollectorStatusInvalid, s.collector.Status()) } @@ -101,13 +99,14 @@ func (s *StateMachineTestSuite) TestStatus_StateTransitions() { // factory are handed through (potentially wrapped), but are not replaced. func (s *StateMachineTestSuite) Test_FactoryErrorPropagation() { factoryError := errors.New("factory error") - factory := func(log zerolog.Logger, block *model.Proposal) (hotstuff.VerifyingVoteProcessor, error) { + factory := func(log zerolog.Logger, block *model.SignedProposal) (hotstuff.VerifyingVoteProcessor, error) { return nil, factoryError } s.collector.createVerifyingProcessor = factory // failing to create collector has to result in error and won't change state - err := s.collector.ProcessBlock(helper.MakeProposal(helper.WithBlock(helper.MakeBlock(helper.WithBlockView(s.view))))) + proposal := makeSignedProposalWithView(s.view) + err := s.collector.ProcessBlock(proposal) require.ErrorIs(s.T(), err, factoryError) require.Equal(s.T(), hotstuff.VoteCollectorStatusCaching, s.collector.Status()) } @@ -115,8 +114,8 @@ func (s *StateMachineTestSuite) Test_FactoryErrorPropagation() { // TestAddVote_VerifyingState tests that AddVote correctly process valid and invalid votes as well // as repeated, invalid and double votes in verifying state func (s *StateMachineTestSuite) TestAddVote_VerifyingState() { - block := helper.MakeBlock(helper.WithBlockView(s.view)) - proposal := helper.MakeProposal(helper.WithBlock(block)) + proposal := makeSignedProposalWithView(s.view) + block := proposal.Block processor := s.prepareMockedProcessor(proposal) err := s.collector.ProcessBlock(proposal) require.NoError(s.T(), err) @@ -203,8 +202,8 @@ func (s *StateMachineTestSuite) TestAddVote_VerifyingState() { // are sent to vote processor func (s *StateMachineTestSuite) TestProcessBlock_ProcessingOfCachedVotes() { votes := 10 - block := helper.MakeBlock(helper.WithBlockView(s.view)) - proposal := helper.MakeProposal(helper.WithBlock(block)) + proposal := makeSignedProposalWithView(s.view) + block := proposal.Block processor := s.prepareMockedProcessor(proposal) for i := 0; i < votes; i++ { vote := unittest.VoteForBlockFixture(block) @@ -226,11 +225,12 @@ func (s *StateMachineTestSuite) TestProcessBlock_ProcessingOfCachedVotes() { // Test_VoteProcessorErrorPropagation verifies that unexpected errors from the `VoteProcessor` // are propagated up the call stack (potentially wrapped), but are not replaced. func (s *StateMachineTestSuite) Test_VoteProcessorErrorPropagation() { - block := helper.MakeBlock(helper.WithBlockView(s.view)) - proposal := helper.MakeProposal(helper.WithBlock(block)) + proposal := makeSignedProposalWithView(s.view) + block := proposal.Block processor := s.prepareMockedProcessor(proposal) - err := s.collector.ProcessBlock(helper.MakeProposal(helper.WithBlock(block))) + err := s.collector.ProcessBlock(helper.MakeSignedProposal( + helper.WithProposal(helper.MakeProposal(helper.WithBlock(block))))) require.NoError(s.T(), err) unexpectedError := errors.New("some unexpected error") @@ -244,8 +244,8 @@ func (s *StateMachineTestSuite) Test_VoteProcessorErrorPropagation() { // in strict ordering of arrival. func (s *StateMachineTestSuite) RegisterVoteConsumer() { votes := 10 - block := helper.MakeBlock(helper.WithBlockView(s.view)) - proposal := helper.MakeProposal(helper.WithBlock(block)) + proposal := makeSignedProposalWithView(s.view) + block := proposal.Block processor := s.prepareMockedProcessor(proposal) expectedVotes := make([]*model.Vote, 0) for i := 0; i < votes; i++ { @@ -273,3 +273,7 @@ func (s *StateMachineTestSuite) RegisterVoteConsumer() { require.Equal(s.T(), expectedVotes, actualVotes) } + +func makeSignedProposalWithView(view uint64) *model.SignedProposal { + return helper.MakeSignedProposal(helper.WithProposal(helper.MakeProposal(helper.WithBlock(helper.MakeBlock(helper.WithBlockView(view)))))) +} diff --git a/consensus/hotstuff/votecollector/testutil.go b/consensus/hotstuff/votecollector/testutil.go index e36aca23170..4c9f2d288e2 100644 --- a/consensus/hotstuff/votecollector/testutil.go +++ b/consensus/hotstuff/votecollector/testutil.go @@ -22,12 +22,12 @@ type VoteProcessorTestSuiteBase struct { stakingAggregator *mockhotstuff.WeightedSignatureAggregator minRequiredWeight uint64 - proposal *model.Proposal + proposal *model.SignedProposal } func (s *VoteProcessorTestSuiteBase) SetupTest() { s.stakingAggregator = &mockhotstuff.WeightedSignatureAggregator{} - s.proposal = helper.MakeProposal() + s.proposal = helper.MakeSignedProposal() // let's assume we have 19 nodes each with weight 100 s.sigWeight = 100 diff --git a/consensus/integration/signer_test.go b/consensus/integration/signer_test.go deleted file mode 100644 index ea443394d20..00000000000 --- a/consensus/integration/signer_test.go +++ /dev/null @@ -1,44 +0,0 @@ -package integration_test - -import ( - "github.com/onflow/flow-go/consensus/hotstuff/model" - "github.com/onflow/flow-go/model/flow" -) - -type Signer struct { - localID flow.Identifier -} - -func (*Signer) CreateProposal(block *model.Block) (*model.Proposal, error) { - proposal := &model.Proposal{ - Block: block, - SigData: nil, - } - return proposal, nil -} -func (s *Signer) CreateVote(block *model.Block) (*model.Vote, error) { - vote := &model.Vote{ - View: block.View, - BlockID: block.BlockID, - SignerID: s.localID, - SigData: nil, - } - return vote, nil -} -func (*Signer) CreateQC(votes []*model.Vote) (*flow.QuorumCertificate, error) { - qc := &flow.QuorumCertificate{ - View: votes[0].View, - BlockID: votes[0].BlockID, - SignerIndices: nil, - SigData: nil, - } - return qc, nil -} - -func (*Signer) VerifyVote(voterID *flow.Identity, sigData []byte, block *model.Block) error { - return nil -} - -func (*Signer) VerifyQC(voters flow.IdentityList, sigData []byte, block *model.Block) error { - return nil -} diff --git a/consensus/participant.go b/consensus/participant.go index b6b65cb06b5..963d6a67dd6 100644 --- a/consensus/participant.go +++ b/consensus/participant.go @@ -74,18 +74,18 @@ func NewParticipant( return nil, fmt.Errorf("could not initialize flow pacemaker: %w", err) } - // initialize block producer - producer, err := blockproducer.New(modules.Signer, modules.Committee, builder) - if err != nil { - return nil, fmt.Errorf("could not initialize block producer: %w", err) - } - // initialize the safetyRules safetyRules, err := safetyrules.New(modules.Signer, modules.Persist, modules.Committee) if err != nil { return nil, fmt.Errorf("could not initialize safety rules: %w", err) } + // initialize block producer + producer, err := blockproducer.New(safetyRules, modules.Committee, builder) + if err != nil { + return nil, fmt.Errorf("could not initialize block producer: %w", err) + } + // initialize the event handler eventHandler, err := eventhandler.NewEventHandler( log, diff --git a/consensus/recovery/recover.go b/consensus/recovery/recover.go index a470aedc3ce..1d85eeab65e 100644 --- a/consensus/recovery/recover.go +++ b/consensus/recovery/recover.go @@ -12,7 +12,7 @@ import ( // BlockScanner describes a function for ingesting pending blocks. // Any returned errors are considered fatal. -type BlockScanner func(proposal *model.Proposal) error +type BlockScanner func(proposal *model.SignedProposal) error // Recover is a utility method for recovering the HotStuff state after a restart. // It receives the list `pending` containing _all_ blocks that @@ -27,7 +27,7 @@ func Recover(log zerolog.Logger, pending []*flow.Header, scanners ...BlockScanne // add all pending blocks to forks for _, header := range pending { - proposal := model.ProposalFromFlow(header) // convert the header into a proposal + proposal := model.SignedProposalFromFlow(header) // convert the header into a proposal for _, s := range scanners { err := s(proposal) if err != nil { @@ -48,7 +48,7 @@ func Recover(log zerolog.Logger, pending []*flow.Header, scanners ...BlockScanne // finalized block. Caution, input blocks must be valid and in parent-first order // (unless parent is the latest finalized block). func ForksState(forks hotstuff.Forks) BlockScanner { - return func(proposal *model.Proposal) error { + return func(proposal *model.SignedProposal) error { err := forks.AddValidatedBlock(proposal.Block) if err != nil { return fmt.Errorf("could not add block %v to forks: %w", proposal.Block.BlockID, err) @@ -63,7 +63,7 @@ func ForksState(forks hotstuff.Forks) BlockScanner { // // Caution: input blocks must be valid. func VoteAggregatorState(voteAggregator hotstuff.VoteAggregator) BlockScanner { - return func(proposal *model.Proposal) error { + return func(proposal *model.SignedProposal) error { voteAggregator.AddBlock(proposal) return nil } @@ -72,7 +72,7 @@ func VoteAggregatorState(voteAggregator hotstuff.VoteAggregator) BlockScanner { // CollectParentQCs collects all parent QCs included in the blocks descending from the // latest finalized block. Caution, input blocks must be valid. func CollectParentQCs(collector Collector[*flow.QuorumCertificate]) BlockScanner { - return func(proposal *model.Proposal) error { + return func(proposal *model.SignedProposal) error { qc := proposal.Block.QC if qc != nil { collector.Append(qc) @@ -84,7 +84,7 @@ func CollectParentQCs(collector Collector[*flow.QuorumCertificate]) BlockScanner // CollectTCs collect all TCs included in the blocks descending from the // latest finalized block. Caution, input blocks must be valid. func CollectTCs(collector Collector[*flow.TimeoutCertificate]) BlockScanner { - return func(proposal *model.Proposal) error { + return func(proposal *model.SignedProposal) error { tc := proposal.LastViewTC if tc != nil { collector.Append(tc) diff --git a/consensus/recovery/recover_test.go b/consensus/recovery/recover_test.go index ac0fb0c3d4f..f3db1a6c42b 100644 --- a/consensus/recovery/recover_test.go +++ b/consensus/recovery/recover_test.go @@ -19,8 +19,8 @@ func TestRecover(t *testing.T) { } // Recover with `pending` blocks and record what blocks are forwarded to `onProposal` - recovered := make([]*model.Proposal, 0) - scanner := func(block *model.Proposal) error { + recovered := make([]*model.SignedProposal, 0) + scanner := func(block *model.SignedProposal) error { recovered = append(recovered, block) return nil } @@ -30,12 +30,12 @@ func TestRecover(t *testing.T) { // should forward blocks in exact order, just converting flow.Header to pending block require.Len(t, recovered, len(pending)) for i, r := range recovered { - require.Equal(t, model.ProposalFromFlow(pending[i]), r) + require.Equal(t, model.SignedProposalFromFlow(pending[i]), r) } } func TestRecoverEmptyInput(t *testing.T) { - scanner := func(block *model.Proposal) error { + scanner := func(block *model.SignedProposal) error { require.Fail(t, "no proposal expected") return nil } diff --git a/engine/access/access_test.go b/engine/access/access_test.go index 4aa8fc047ca..7d12c70cdcf 100644 --- a/engine/access/access_test.go +++ b/engine/access/access_test.go @@ -26,6 +26,7 @@ import ( "github.com/onflow/flow-go/engine/access/rpc/backend" connectionmock "github.com/onflow/flow-go/engine/access/rpc/connection/mock" "github.com/onflow/flow-go/engine/access/subscription" + commonrpc "github.com/onflow/flow-go/engine/common/rpc" "github.com/onflow/flow-go/engine/common/rpc/convert" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/model/flow/factory" @@ -267,10 +268,6 @@ func (suite *Suite) TestSendExpiredTransaction() { }) } -type mockCloser struct{} - -func (mc *mockCloser) Close() error { return nil } - // TestSendTransactionToRandomCollectionNode tests that collection nodes are chosen from the appropriate cluster when // forwarding transactions by sending two transactions bound for two different collection clusters. func (suite *Suite) TestSendTransactionToRandomCollectionNode() { @@ -325,20 +322,19 @@ func (suite *Suite) TestSendTransactionToRandomCollectionNode() { // create a mock connection factory connFactory := connectionmock.NewConnectionFactory(suite.T()) - connFactory.On("GetAccessAPIClient", collNode1.Address, nil).Return(col1ApiClient, &mockCloser{}, nil) - connFactory.On("GetAccessAPIClient", collNode2.Address, nil).Return(col2ApiClient, &mockCloser{}, nil) + connFactory.On("GetAccessAPIClient", collNode1.Address, nil).Return(col1ApiClient, &mocks.MockCloser{}, nil) + connFactory.On("GetAccessAPIClient", collNode2.Address, nil).Return(col2ApiClient, &mocks.MockCloser{}, nil) bnd, err := backend.New(backend.Params{State: suite.state, - Collections: collections, - Transactions: transactions, - ChainID: suite.chainID, - AccessMetrics: metrics, - ConnFactory: connFactory, - MaxHeightRange: backend.DefaultMaxHeightRange, - Log: suite.log, - SnapshotHistoryLimit: backend.DefaultSnapshotHistoryLimit, - Communicator: backend.NewNodeCommunicator(false), - TxErrorMessagesCacheSize: 1000, + Collections: collections, + Transactions: transactions, + ChainID: suite.chainID, + AccessMetrics: metrics, + ConnFactory: connFactory, + MaxHeightRange: backend.DefaultMaxHeightRange, + Log: suite.log, + SnapshotHistoryLimit: backend.DefaultSnapshotHistoryLimit, + Communicator: backend.NewNodeCommunicator(false), }) require.NoError(suite.T(), err) @@ -630,7 +626,7 @@ func (suite *Suite) TestGetSealedTransaction() { // create a mock connection factory connFactory := connectionmock.NewConnectionFactory(suite.T()) - connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mockCloser{}, nil) + connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mocks.MockCloser{}, nil) // initialize storage metrics := metrics.NewNoopCollector() @@ -642,25 +638,35 @@ func (suite *Suite) TestGetSealedTransaction() { require.NoError(suite.T(), err) blocksToMarkExecuted, err := stdmap.NewTimes(100) require.NoError(suite.T(), err) + blockTransactions, err := stdmap.NewIdentifierMap(100) + require.NoError(suite.T(), err) - bnd, err := backend.New(backend.Params{State: suite.state, - CollectionRPC: suite.collClient, - Blocks: all.Blocks, - Headers: all.Headers, - Collections: collections, - Transactions: transactions, - ExecutionReceipts: receipts, - ExecutionResults: results, - ChainID: suite.chainID, - AccessMetrics: suite.metrics, - ConnFactory: connFactory, - MaxHeightRange: backend.DefaultMaxHeightRange, - PreferredExecutionNodeIDs: enNodeIDs.Strings(), - Log: suite.log, - SnapshotHistoryLimit: backend.DefaultSnapshotHistoryLimit, - Communicator: backend.NewNodeCommunicator(false), - TxErrorMessagesCacheSize: 1000, - TxResultQueryMode: backend.IndexQueryModeExecutionNodesOnly, + execNodeIdentitiesProvider := commonrpc.NewExecutionNodeIdentitiesProvider( + suite.log, + suite.state, + receipts, + enNodeIDs, + nil, + ) + + bnd, err := backend.New(backend.Params{ + State: suite.state, + CollectionRPC: suite.collClient, + Blocks: all.Blocks, + Headers: all.Headers, + Collections: collections, + Transactions: transactions, + ExecutionReceipts: receipts, + ExecutionResults: results, + ChainID: suite.chainID, + AccessMetrics: suite.metrics, + ConnFactory: connFactory, + MaxHeightRange: backend.DefaultMaxHeightRange, + Log: suite.log, + SnapshotHistoryLimit: backend.DefaultSnapshotHistoryLimit, + Communicator: backend.NewNodeCommunicator(false), + TxResultQueryMode: backend.IndexQueryModeExecutionNodesOnly, + ExecNodeIdentitiesProvider: execNodeIdentitiesProvider, }) require.NoError(suite.T(), err) @@ -674,6 +680,7 @@ func (suite *Suite) TestGetSealedTransaction() { blocksToMarkExecuted, collections, all.Blocks, + blockTransactions, ) require.NoError(suite.T(), err) @@ -686,8 +693,23 @@ func (suite *Suite) TestGetSealedTransaction() { // create the ingest engine processedHeight := bstorage.NewConsumerProgress(db, module.ConsumeProgressIngestionEngineBlockHeight) - ingestEng, err := ingestion.New(suite.log, suite.net, suite.state, suite.me, suite.request, all.Blocks, all.Headers, collections, - transactions, results, receipts, collectionExecutedMetric, processedHeight, lastFullBlockHeight) + ingestEng, err := ingestion.New( + suite.log, + suite.net, + suite.state, + suite.me, + suite.request, + all.Blocks, + all.Headers, + collections, + transactions, + results, + receipts, + collectionExecutedMetric, + processedHeight, + lastFullBlockHeight, + nil, + ) require.NoError(suite.T(), err) // 1. Assume that follower engine updated the block storage and the protocol state. The block is reported as sealed @@ -744,7 +766,6 @@ func (suite *Suite) TestGetTransactionResult() { all := util.StorageLayer(suite.T(), db) results := bstorage.NewExecutionResults(suite.metrics, db) receipts := bstorage.NewExecutionReceipts(suite.metrics, db, results, bstorage.DefaultCacheSize) - originID := unittest.IdentifierFixture() *suite.state = protocol.State{} @@ -779,6 +800,9 @@ func (suite *Suite) TestGetTransactionResult() { allIdentities := append(colIdentities, enIdentities...) finalSnapshot.On("Identities", mock.Anything).Return(allIdentities, nil) + suite.state.On("AtBlockID", blockNegativeId).Return(suite.sealedSnapshot) + suite.sealedSnapshot.On("Identities", mock.Anything).Return(allIdentities, nil) + // assume execution node returns an empty list of events suite.execClient.On("GetTransactionResult", mock.Anything, mock.Anything).Return(&execproto.GetTransactionResultResponse{ Events: nil, @@ -791,7 +815,7 @@ func (suite *Suite) TestGetTransactionResult() { // create a mock connection factory connFactory := connectionmock.NewConnectionFactory(suite.T()) - connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mockCloser{}, nil) + connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mocks.MockCloser{}, nil) // initialize storage metrics := metrics.NewNoopCollector() @@ -805,25 +829,34 @@ func (suite *Suite) TestGetTransactionResult() { require.NoError(suite.T(), err) blocksToMarkExecuted, err := stdmap.NewTimes(100) require.NoError(suite.T(), err) + blockTransactions, err := stdmap.NewIdentifierMap(100) + require.NoError(suite.T(), err) + + execNodeIdentitiesProvider := commonrpc.NewExecutionNodeIdentitiesProvider( + suite.log, + suite.state, + receipts, + enNodeIDs, + nil, + ) bnd, err := backend.New(backend.Params{State: suite.state, - CollectionRPC: suite.collClient, - Blocks: all.Blocks, - Headers: all.Headers, - Collections: collections, - Transactions: transactions, - ExecutionReceipts: receipts, - ExecutionResults: results, - ChainID: suite.chainID, - AccessMetrics: suite.metrics, - ConnFactory: connFactory, - MaxHeightRange: backend.DefaultMaxHeightRange, - PreferredExecutionNodeIDs: enNodeIDs.Strings(), - Log: suite.log, - SnapshotHistoryLimit: backend.DefaultSnapshotHistoryLimit, - Communicator: backend.NewNodeCommunicator(false), - TxErrorMessagesCacheSize: 1000, - TxResultQueryMode: backend.IndexQueryModeExecutionNodesOnly, + CollectionRPC: suite.collClient, + Blocks: all.Blocks, + Headers: all.Headers, + Collections: collections, + Transactions: transactions, + ExecutionReceipts: receipts, + ExecutionResults: results, + ChainID: suite.chainID, + AccessMetrics: suite.metrics, + ConnFactory: connFactory, + MaxHeightRange: backend.DefaultMaxHeightRange, + Log: suite.log, + SnapshotHistoryLimit: backend.DefaultSnapshotHistoryLimit, + Communicator: backend.NewNodeCommunicator(false), + TxResultQueryMode: backend.IndexQueryModeExecutionNodesOnly, + ExecNodeIdentitiesProvider: execNodeIdentitiesProvider, }) require.NoError(suite.T(), err) @@ -837,10 +870,12 @@ func (suite *Suite) TestGetTransactionResult() { blocksToMarkExecuted, collections, all.Blocks, + blockTransactions, ) require.NoError(suite.T(), err) processedHeight := bstorage.NewConsumerProgress(db, module.ConsumeProgressIngestionEngineBlockHeight) + lastFullBlockHeight, err := counters.NewPersistentStrictMonotonicCounter( bstorage.NewConsumerProgress(db, module.ConsumeProgressLastFullBlockHeight), suite.rootBlock.Height, @@ -848,8 +883,23 @@ func (suite *Suite) TestGetTransactionResult() { require.NoError(suite.T(), err) // create the ingest engine - ingestEng, err := ingestion.New(suite.log, suite.net, suite.state, suite.me, suite.request, all.Blocks, all.Headers, collections, - transactions, results, receipts, collectionExecutedMetric, processedHeight, lastFullBlockHeight) + ingestEng, err := ingestion.New( + suite.log, + suite.net, + suite.state, + suite.me, + suite.request, + all.Blocks, + all.Headers, + collections, + transactions, + results, + receipts, + collectionExecutedMetric, + processedHeight, + lastFullBlockHeight, + nil, + ) require.NoError(suite.T(), err) background, cancel := context.WithCancel(context.Background()) @@ -945,6 +995,7 @@ func (suite *Suite) TestGetTransactionResult() { } resp, err := handler.GetTransactionResult(context.Background(), getReq) require.Error(suite.T(), err) + require.Contains(suite.T(), err.Error(), "failed to find: transaction not in block") require.Nil(suite.T(), resp) }) @@ -1011,36 +1062,42 @@ func (suite *Suite) TestExecuteScript() { collections := bstorage.NewCollections(db, transactions) results := bstorage.NewExecutionResults(suite.metrics, db) receipts := bstorage.NewExecutionReceipts(suite.metrics, db, results, bstorage.DefaultCacheSize) - identities := unittest.IdentityListFixture(2, unittest.WithRole(flow.RoleExecution)) suite.sealedSnapshot.On("Identities", mock.Anything).Return(identities, nil) suite.finalSnapshot.On("Identities", mock.Anything).Return(identities, nil) // create a mock connection factory connFactory := connectionmock.NewConnectionFactory(suite.T()) - connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mockCloser{}, nil) + connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mocks.MockCloser{}, nil) + + execNodeIdentitiesProvider := commonrpc.NewExecutionNodeIdentitiesProvider( + suite.log, + suite.state, + receipts, + nil, + identities.NodeIDs(), + ) var err error suite.backend, err = backend.New(backend.Params{ - State: suite.state, - CollectionRPC: suite.collClient, - Blocks: all.Blocks, - Headers: all.Headers, - Collections: collections, - Transactions: transactions, - ExecutionReceipts: receipts, - ExecutionResults: results, - ChainID: suite.chainID, - AccessMetrics: suite.metrics, - ConnFactory: connFactory, - MaxHeightRange: backend.DefaultMaxHeightRange, - FixedExecutionNodeIDs: (identities.NodeIDs()).Strings(), - Log: suite.log, - SnapshotHistoryLimit: backend.DefaultSnapshotHistoryLimit, - Communicator: backend.NewNodeCommunicator(false), - ScriptExecutionMode: backend.IndexQueryModeExecutionNodesOnly, - TxErrorMessagesCacheSize: 1000, - TxResultQueryMode: backend.IndexQueryModeExecutionNodesOnly, + State: suite.state, + CollectionRPC: suite.collClient, + Blocks: all.Blocks, + Headers: all.Headers, + Collections: collections, + Transactions: transactions, + ExecutionReceipts: receipts, + ExecutionResults: results, + ChainID: suite.chainID, + AccessMetrics: suite.metrics, + ConnFactory: connFactory, + MaxHeightRange: backend.DefaultMaxHeightRange, + Log: suite.log, + SnapshotHistoryLimit: backend.DefaultSnapshotHistoryLimit, + Communicator: backend.NewNodeCommunicator(false), + ScriptExecutionMode: backend.IndexQueryModeExecutionNodesOnly, + TxResultQueryMode: backend.IndexQueryModeExecutionNodesOnly, + ExecNodeIdentitiesProvider: execNodeIdentitiesProvider, }) require.NoError(suite.T(), err) @@ -1054,6 +1111,8 @@ func (suite *Suite) TestExecuteScript() { require.NoError(suite.T(), err) blocksToMarkExecuted, err := stdmap.NewTimes(100) require.NoError(suite.T(), err) + blockTransactions, err := stdmap.NewIdentifierMap(100) + require.NoError(suite.T(), err) collectionExecutedMetric, err := indexer.NewCollectionExecutedMetricImpl( suite.log, @@ -1063,6 +1122,7 @@ func (suite *Suite) TestExecuteScript() { blocksToMarkExecuted, collections, all.Blocks, + blockTransactions, ) require.NoError(suite.T(), err) @@ -1071,6 +1131,7 @@ func (suite *Suite) TestExecuteScript() { Once() processedHeight := bstorage.NewConsumerProgress(db, module.ConsumeProgressIngestionEngineBlockHeight) + lastFullBlockHeight, err := counters.NewPersistentStrictMonotonicCounter( bstorage.NewConsumerProgress(db, module.ConsumeProgressLastFullBlockHeight), suite.rootBlock.Height, @@ -1078,8 +1139,23 @@ func (suite *Suite) TestExecuteScript() { require.NoError(suite.T(), err) // create the ingest engine - ingestEng, err := ingestion.New(suite.log, suite.net, suite.state, suite.me, suite.request, all.Blocks, all.Headers, collections, - transactions, results, receipts, collectionExecutedMetric, processedHeight, lastFullBlockHeight) + ingestEng, err := ingestion.New( + suite.log, + suite.net, + suite.state, + suite.me, + suite.request, + all.Blocks, + all.Headers, + collections, + transactions, + results, + receipts, + collectionExecutedMetric, + processedHeight, + lastFullBlockHeight, + nil, + ) require.NoError(suite.T(), err) // create another block as a predecessor of the block created earlier diff --git a/engine/access/handle_irrecoverable_state_test.go b/engine/access/handle_irrecoverable_state_test.go index 911ba5c2a53..303a542339a 100644 --- a/engine/access/handle_irrecoverable_state_test.go +++ b/engine/access/handle_irrecoverable_state_test.go @@ -22,7 +22,7 @@ import ( accessmock "github.com/onflow/flow-go/engine/access/mock" "github.com/onflow/flow-go/engine/access/rest" - "github.com/onflow/flow-go/engine/access/rest/routes" + "github.com/onflow/flow-go/engine/access/rest/router" "github.com/onflow/flow-go/engine/access/rpc" "github.com/onflow/flow-go/engine/access/rpc/backend" statestreambackend "github.com/onflow/flow-go/engine/access/state_stream/backend" @@ -249,7 +249,7 @@ func (suite *IrrecoverableStateTestSuite) TestRestInconsistentNodeState() { // optionsForBlocksIdGetOpts returns options for the BlocksApi.BlocksIdGet function. func optionsForBlocksIdGetOpts() *restclient.BlocksApiBlocksIdGetOpts { return &restclient.BlocksApiBlocksIdGetOpts{ - Expand: optional.NewInterface([]string{routes.ExpandableFieldPayload}), + Expand: optional.NewInterface([]string{router.ExpandableFieldPayload}), Select_: optional.NewInterface([]string{"header.id"}), } } diff --git a/engine/access/ingestion/engine.go b/engine/access/ingestion/engine.go index b36e5598c59..0e4ac42367e 100644 --- a/engine/access/ingestion/engine.go +++ b/engine/access/ingestion/engine.go @@ -10,6 +10,7 @@ import ( "github.com/onflow/flow-go/consensus/hotstuff/model" "github.com/onflow/flow-go/engine" + "github.com/onflow/flow-go/engine/access/ingestion/tx_error_messages" "github.com/onflow/flow-go/engine/common/fifoqueue" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/model/flow/filter" @@ -50,8 +51,9 @@ const ( // default queue capacity defaultQueueCapacity = 10_000 - // how many workers will concurrently process the tasks in the jobqueue - workersCount = 1 + // processFinalizedBlocksWorkersCount defines the number of workers that + // concurrently process finalized blocks in the job queue. + processFinalizedBlocksWorkersCount = 1 // ensure blocks are processed sequentially by jobqueue searchAhead = 1 @@ -77,10 +79,12 @@ type Engine struct { executionReceiptsQueue engine.MessageStore // Job queue finalizedBlockConsumer *jobqueue.ComponentConsumer - // Notifier for queue consumer finalizedBlockNotifier engine.Notifier + // txResultErrorMessagesChan is used to fetch and store transaction result error messages for blocks + txResultErrorMessagesChan chan flow.Identifier + log zerolog.Logger // used to log relevant actions with context state protocol.State // used to access the protocol state me module.Local // used to access local node information @@ -99,6 +103,8 @@ type Engine struct { lastFullBlockHeight *counters.PersistentStrictMonotonicCounter // metrics collectionExecutedMetric module.CollectionExecutedMetric + + txErrorMessagesCore *tx_error_messages.TxErrorMessagesCore } var _ network.MessageProcessor = (*Engine)(nil) @@ -119,8 +125,9 @@ func New( executionResults storage.ExecutionResults, executionReceipts storage.ExecutionReceipts, collectionExecutedMetric module.CollectionExecutedMetric, - processedHeight storage.ConsumerProgress, + finalizedProcessedHeight storage.ConsumerProgress, lastFullBlockHeight *counters.PersistentStrictMonotonicCounter, + txErrorMessagesCore *tx_error_messages.TxErrorMessagesCore, ) (*Engine, error) { executionReceiptsRawQueue, err := fifoqueue.NewFifoQueue(defaultQueueCapacity) if err != nil { @@ -162,8 +169,10 @@ func New( // queue / notifier for execution receipts executionReceiptsNotifier: engine.NewNotifier(), + txResultErrorMessagesChan: make(chan flow.Identifier, 1), executionReceiptsQueue: executionReceiptsQueue, messageHandler: messageHandler, + txErrorMessagesCore: txErrorMessagesCore, } // jobqueue Jobs object that tracks finalized blocks by height. This is used by the finalizedBlockConsumer @@ -172,7 +181,7 @@ func New( defaultIndex, err := e.defaultProcessedIndex() if err != nil { - return nil, fmt.Errorf("could not read default processed index: %w", err) + return nil, fmt.Errorf("could not read default finalized processed index: %w", err) } // create a jobqueue that will process new available finalized block. The `finalizedBlockNotifier` is used to @@ -180,11 +189,11 @@ func New( e.finalizedBlockConsumer, err = jobqueue.NewComponentConsumer( e.log.With().Str("module", "ingestion_block_consumer").Logger(), e.finalizedBlockNotifier.Channel(), - processedHeight, + finalizedProcessedHeight, finalizedBlockReader, defaultIndex, e.processFinalizedBlockJob, - workersCount, + processFinalizedBlocksWorkersCount, searchAhead, ) if err != nil { @@ -192,11 +201,20 @@ func New( } // Add workers - e.ComponentManager = component.NewComponentManagerBuilder(). + builder := component.NewComponentManagerBuilder(). AddWorker(e.processBackground). AddWorker(e.processExecutionReceipts). - AddWorker(e.runFinalizedBlockConsumer). - Build() + AddWorker(e.runFinalizedBlockConsumer) + + // If txErrorMessagesCore is provided, add a worker responsible for processing + // transaction result error messages by receipts. This worker listens for blocks + // containing execution receipts and processes any associated transaction result + // error messages. The worker is added only when error message processing is enabled. + if txErrorMessagesCore != nil { + builder.AddWorker(e.processTransactionResultErrorMessagesByReceipts) + } + + e.ComponentManager = builder.Build() // register engine with the execution receipt provider _, err = net.Register(channels.ReceiveReceipts, e) @@ -209,7 +227,7 @@ func New( // defaultProcessedIndex returns the last finalized block height from the protocol state. // -// The BlockConsumer utilizes this return height to fetch and consume block jobs from +// The finalizedBlockConsumer utilizes this return height to fetch and consume block jobs from // jobs queue the first time it initializes. // // No errors are expected during normal operation. @@ -337,6 +355,42 @@ func (e *Engine) processAvailableExecutionReceipts(ctx context.Context) error { if err := e.handleExecutionReceipt(msg.OriginID, receipt); err != nil { return err } + + // Notify to fetch and store transaction result error messages for the block. + // If txErrorMessagesCore is enabled, the receipt's BlockID is sent to trigger + // transaction error message processing. This step is skipped if error message + // storage is not enabled. + if e.txErrorMessagesCore != nil { + e.txResultErrorMessagesChan <- receipt.BlockID + } + } +} + +// processTransactionResultErrorMessagesByReceipts handles error messages related to transaction +// results by reading from the error messages channel and processing them accordingly. +// +// This function listens for messages on the txResultErrorMessagesChan channel and +// processes each transaction result error message as it arrives. +// +// No errors are expected during normal operation. +func (e *Engine) processTransactionResultErrorMessagesByReceipts(ctx irrecoverable.SignalerContext, ready component.ReadyFunc) { + ready() + + for { + select { + case <-ctx.Done(): + return + case blockID := <-e.txResultErrorMessagesChan: + err := e.txErrorMessagesCore.HandleTransactionResultErrorMessages(ctx, blockID) + if err != nil { + // TODO: we should revisit error handling here. + // Errors that come from querying the EN and possibly ExecutionNodesForBlockID should be logged and + // retried later, while others should cause an exception. + e.log.Error(). + Err(err). + Msg("error encountered while processing transaction result error messages by receipts") + } + } } } diff --git a/engine/access/ingestion/engine_test.go b/engine/access/ingestion/engine_test.go index 5e00e720334..2f5b0169b34 100644 --- a/engine/access/ingestion/engine_test.go +++ b/engine/access/ingestion/engine_test.go @@ -47,18 +47,19 @@ type Suite struct { params *protocol.Params } - me *modulemock.Local - net *mocknetwork.Network - request *modulemock.Requester - obsIdentity *flow.Identity - provider *mocknetwork.Engine - blocks *storage.Blocks - headers *storage.Headers - collections *storage.Collections - transactions *storage.Transactions - receipts *storage.ExecutionReceipts - results *storage.ExecutionResults - seals *storage.Seals + me *modulemock.Local + net *mocknetwork.Network + request *modulemock.Requester + obsIdentity *flow.Identity + provider *mocknetwork.Engine + blocks *storage.Blocks + headers *storage.Headers + collections *storage.Collections + transactions *storage.Transactions + receipts *storage.ExecutionReceipts + results *storage.ExecutionResults + seals *storage.Seals + conduit *mocknetwork.Conduit downloader *downloadermock.Downloader sealedBlock *flow.Header @@ -102,7 +103,6 @@ func (s *Suite) SetupTest() { s.proto.params = new(protocol.Params) s.finalizedBlock = unittest.BlockHeaderFixture(unittest.WithHeaderHeight(0)) s.proto.state.On("Identity").Return(s.obsIdentity, nil) - s.proto.state.On("Final").Return(s.proto.snapshot, nil) s.proto.state.On("Params").Return(s.proto.params) s.proto.snapshot.On("Head").Return( func() *flow.Header { @@ -119,7 +119,6 @@ func (s *Suite) SetupTest() { Return(conduit, nil). Once() s.request = modulemock.NewRequester(s.T()) - s.provider = mocknetwork.NewEngine(s.T()) s.blocks = storage.NewBlocks(s.T()) s.headers = storage.NewHeaders(s.T()) @@ -133,6 +132,8 @@ func (s *Suite) SetupTest() { require.NoError(s.T(), err) blocksToMarkExecuted, err := stdmap.NewTimes(100) require.NoError(s.T(), err) + blockTransactions, err := stdmap.NewIdentifierMap(100) + require.NoError(s.T(), err) s.proto.state.On("Identity").Return(s.obsIdentity, nil) s.proto.state.On("Params").Return(s.proto.params) @@ -166,6 +167,10 @@ func (s *Suite) SetupTest() { ).Maybe() s.proto.state.On("Final").Return(s.proto.snapshot, nil) + // Mock the finalized root block header with height 0. + header := unittest.BlockHeaderFixture(unittest.WithHeaderHeight(0)) + s.proto.params.On("FinalizedRoot").Return(header, nil) + s.collectionExecutedMetric, err = indexer.NewCollectionExecutedMetricImpl( s.log, metrics.NewNoopCollector(), @@ -174,6 +179,7 @@ func (s *Suite) SetupTest() { blocksToMarkExecuted, s.collections, s.blocks, + blockTransactions, ) require.NoError(s.T(), err) } @@ -189,8 +195,24 @@ func (s *Suite) initIngestionEngine(ctx irrecoverable.SignalerContext) *Engine { ) require.NoError(s.T(), err) - eng, err := New(s.log, s.net, s.proto.state, s.me, s.request, s.blocks, s.headers, s.collections, - s.transactions, s.results, s.receipts, s.collectionExecutedMetric, processedHeight, s.lastFullBlockHeight) + eng, err := New( + s.log, + s.net, + s.proto.state, + s.me, + s.request, + s.blocks, + s.headers, + s.collections, + s.transactions, + s.results, + s.receipts, + s.collectionExecutedMetric, + processedHeight, + s.lastFullBlockHeight, + nil, + ) + require.NoError(s.T(), err) eng.ComponentManager.Start(ctx) diff --git a/engine/access/ingestion/tx_error_messages/tx_error_messages_core.go b/engine/access/ingestion/tx_error_messages/tx_error_messages_core.go new file mode 100644 index 00000000000..12dcb906cf5 --- /dev/null +++ b/engine/access/ingestion/tx_error_messages/tx_error_messages_core.go @@ -0,0 +1,133 @@ +package tx_error_messages + +import ( + "context" + "fmt" + + "github.com/rs/zerolog" + + "github.com/onflow/flow-go/engine/access/rpc/backend" + commonrpc "github.com/onflow/flow-go/engine/common/rpc" + "github.com/onflow/flow-go/engine/common/rpc/convert" + "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/storage" + + execproto "github.com/onflow/flow/protobuf/go/flow/execution" +) + +// TxErrorMessagesCore is responsible for managing transaction result error messages +// It handles both storage and retrieval of error messages +// from execution nodes. +type TxErrorMessagesCore struct { + log zerolog.Logger // used to log relevant actions with context + + backend *backend.Backend + transactionResultErrorMessages storage.TransactionResultErrorMessages + execNodeIdentitiesProvider *commonrpc.ExecutionNodeIdentitiesProvider +} + +// NewTxErrorMessagesCore creates a new instance of TxErrorMessagesCore. +func NewTxErrorMessagesCore( + log zerolog.Logger, + backend *backend.Backend, + transactionResultErrorMessages storage.TransactionResultErrorMessages, + execNodeIdentitiesProvider *commonrpc.ExecutionNodeIdentitiesProvider, +) *TxErrorMessagesCore { + return &TxErrorMessagesCore{ + log: log.With().Str("module", "tx_error_messages_core").Logger(), + backend: backend, + transactionResultErrorMessages: transactionResultErrorMessages, + execNodeIdentitiesProvider: execNodeIdentitiesProvider, + } +} + +// HandleTransactionResultErrorMessages processes transaction result error messages for a given block ID. +// It retrieves error messages from the backend if they do not already exist in storage. +// +// The function first checks if error messages for the given block ID are already present in storage. +// If they are not, it fetches the messages from execution nodes and stores them. +// +// Parameters: +// - ctx: The context for managing cancellation and deadlines during the operation. +// - blockID: The identifier of the block for which transaction result error messages need to be processed. +// +// No errors are expected during normal operation. +func (c *TxErrorMessagesCore) HandleTransactionResultErrorMessages(ctx context.Context, blockID flow.Identifier) error { + execNodes, err := c.execNodeIdentitiesProvider.ExecutionNodesForBlockID(ctx, blockID) + if err != nil { + c.log.Error().Err(err).Msg(fmt.Sprintf("failed to find execution nodes for block id: %s", blockID)) + return fmt.Errorf("could not find execution nodes for block: %w", err) + } + + return c.HandleTransactionResultErrorMessagesByENs(ctx, blockID, execNodes) +} + +func (c *TxErrorMessagesCore) HandleTransactionResultErrorMessagesByENs( + ctx context.Context, + blockID flow.Identifier, + execNodes flow.IdentitySkeletonList, +) error { + exists, err := c.transactionResultErrorMessages.Exists(blockID) + if err != nil { + return fmt.Errorf("could not check existance of transaction result error messages: %w", err) + } + + if exists { + return nil + } + + // retrieves error messages from the backend if they do not already exist in storage + req := &execproto.GetTransactionErrorMessagesByBlockIDRequest{ + BlockId: convert.IdentifierToMessage(blockID), + } + + c.log.Debug(). + Msgf("transaction error messages for block %s are being downloaded", blockID) + + resp, execNode, err := c.backend.GetTransactionErrorMessagesFromAnyEN(ctx, execNodes, req) + if err != nil { + c.log.Error().Err(err).Msg("failed to get transaction error messages from execution nodes") + return err + } + + if len(resp) > 0 { + err = c.storeTransactionResultErrorMessages(blockID, resp, execNode) + if err != nil { + return fmt.Errorf("could not store error messages (block: %s): %w", blockID, err) + } + } + + return nil +} + +// storeTransactionResultErrorMessages stores the transaction result error messages for a given block ID. +// +// Parameters: +// - blockID: The identifier of the block for which the error messages are to be stored. +// - errorMessagesResponses: A slice of responses containing the error messages to be stored. +// - execNode: The execution node associated with the error messages. +// +// No errors are expected during normal operation. +func (c *TxErrorMessagesCore) storeTransactionResultErrorMessages( + blockID flow.Identifier, + errorMessagesResponses []*execproto.GetTransactionErrorMessagesResponse_Result, + execNode *flow.IdentitySkeleton, +) error { + errorMessages := make([]flow.TransactionResultErrorMessage, 0, len(errorMessagesResponses)) + for _, value := range errorMessagesResponses { + errorMessage := flow.TransactionResultErrorMessage{ + ErrorMessage: value.ErrorMessage, + TransactionID: convert.MessageToIdentifier(value.TransactionId), + Index: value.Index, + ExecutorID: execNode.NodeID, + } + errorMessages = append(errorMessages, errorMessage) + } + + err := c.transactionResultErrorMessages.Store(blockID, errorMessages) + if err != nil { + return fmt.Errorf("failed to store transaction error messages: %w", err) + } + + return nil +} diff --git a/engine/access/ingestion/tx_error_messages/tx_error_messages_core_test.go b/engine/access/ingestion/tx_error_messages/tx_error_messages_core_test.go new file mode 100644 index 00000000000..f5bf2715bb4 --- /dev/null +++ b/engine/access/ingestion/tx_error_messages/tx_error_messages_core_test.go @@ -0,0 +1,341 @@ +package tx_error_messages + +import ( + "context" + "fmt" + "os" + "testing" + + execproto "github.com/onflow/flow/protobuf/go/flow/execution" + "github.com/rs/zerolog" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + + accessmock "github.com/onflow/flow-go/engine/access/mock" + "github.com/onflow/flow-go/engine/access/rpc/backend" + connectionmock "github.com/onflow/flow-go/engine/access/rpc/connection/mock" + commonrpc "github.com/onflow/flow-go/engine/common/rpc" + "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/module/irrecoverable" + protocol "github.com/onflow/flow-go/state/protocol/mock" + storage "github.com/onflow/flow-go/storage/mock" + "github.com/onflow/flow-go/utils/unittest" +) + +const expectedErrorMsg = "expected test error" + +type TxErrorMessagesCoreSuite struct { + suite.Suite + + log zerolog.Logger + proto struct { + state *protocol.FollowerState + snapshot *protocol.Snapshot + params *protocol.Params + } + + receipts *storage.ExecutionReceipts + txErrorMessages *storage.TransactionResultErrorMessages + + enNodeIDs flow.IdentityList + execClient *accessmock.ExecutionAPIClient + connFactory *connectionmock.ConnectionFactory + + blockMap map[uint64]*flow.Block + rootBlock flow.Block + finalizedBlock *flow.Header + + ctx context.Context + cancel context.CancelFunc +} + +func TestTxErrorMessagesCore(t *testing.T) { + suite.Run(t, new(TxErrorMessagesCoreSuite)) +} + +// TearDownTest stops the engine and cleans up the db +func (s *TxErrorMessagesCoreSuite) TearDownTest() { + s.cancel() +} + +type mockCloser struct{} + +func (mc *mockCloser) Close() error { return nil } + +func (s *TxErrorMessagesCoreSuite) SetupTest() { + s.log = zerolog.New(os.Stderr) + s.ctx, s.cancel = context.WithCancel(context.Background()) + // mock out protocol state + s.proto.state = protocol.NewFollowerState(s.T()) + s.proto.snapshot = protocol.NewSnapshot(s.T()) + s.proto.params = protocol.NewParams(s.T()) + s.execClient = accessmock.NewExecutionAPIClient(s.T()) + s.connFactory = connectionmock.NewConnectionFactory(s.T()) + s.receipts = storage.NewExecutionReceipts(s.T()) + s.txErrorMessages = storage.NewTransactionResultErrorMessages(s.T()) + + s.rootBlock = unittest.BlockFixture() + s.rootBlock.Header.Height = 0 + s.finalizedBlock = unittest.BlockWithParentFixture(s.rootBlock.Header).Header + + s.proto.state.On("Params").Return(s.proto.params) + + // Mock the finalized root block header with height 0. + s.proto.params.On("FinalizedRoot").Return(s.rootBlock.Header, nil) + + s.proto.snapshot.On("Head").Return( + func() *flow.Header { + return s.finalizedBlock + }, + nil, + ).Maybe() + s.proto.state.On("Final").Return(s.proto.snapshot, nil) + + // Create identities for 1 execution nodes. + s.enNodeIDs = unittest.IdentityListFixture(1, unittest.WithRole(flow.RoleExecution)) +} + +// TestHandleTransactionResultErrorMessages checks that transaction result error messages +// are properly fetched from the execution nodes, processed, and stored in the protocol database. +func (s *TxErrorMessagesCoreSuite) TestHandleTransactionResultErrorMessages() { + irrecoverableCtx := irrecoverable.NewMockSignalerContext(s.T(), s.ctx) + + block := unittest.BlockWithParentFixture(s.finalizedBlock) + blockId := block.ID() + + s.connFactory.On("GetExecutionAPIClient", mock.Anything).Return(s.execClient, &mockCloser{}, nil) + + // Mock the protocol snapshot to return fixed execution node IDs. + setupReceiptsForBlock(s.receipts, block, s.enNodeIDs.NodeIDs()[0]) + s.proto.snapshot.On("Identities", mock.Anything).Return(s.enNodeIDs, nil) + s.proto.state.On("AtBlockID", blockId).Return(s.proto.snapshot).Once() + + // Create mock transaction results with a mix of failed and non-failed transactions. + resultsByBlockID := mockTransactionResultsByBlock(5) + + // Prepare a request to fetch transaction error messages by block ID from execution nodes. + exeEventReq := &execproto.GetTransactionErrorMessagesByBlockIDRequest{ + BlockId: blockId[:], + } + + s.execClient.On("GetTransactionErrorMessagesByBlockID", mock.Anything, exeEventReq). + Return(createTransactionErrorMessagesResponse(resultsByBlockID), nil). + Once() + + // 1. Mock the txErrorMessages storage to confirm that error messages do not exist yet. + s.txErrorMessages.On("Exists", blockId). + Return(false, nil).Once() + + // Prepare the expected transaction error messages that should be stored. + expectedStoreTxErrorMessages := createExpectedTxErrorMessages(resultsByBlockID, s.enNodeIDs.NodeIDs()[0]) + + // Mock the storage of the fetched error messages into the protocol database. + s.txErrorMessages.On("Store", blockId, expectedStoreTxErrorMessages). + Return(nil).Once() + + core := s.initCore() + err := core.HandleTransactionResultErrorMessages(irrecoverableCtx, blockId) + require.NoError(s.T(), err) + + // Verify that the mock expectations for storing the error messages were met. + s.txErrorMessages.AssertExpectations(s.T()) + s.proto.state.AssertExpectations(s.T()) + + // 2. Now simulate the second try when the error messages already exist in storage. + // Mock the txErrorMessages storage to confirm that error messages exist. + s.txErrorMessages.On("Exists", blockId). + Return(true, nil).Once() + s.proto.state.On("AtBlockID", blockId).Return(s.proto.snapshot).Once() + err = core.HandleTransactionResultErrorMessages(irrecoverableCtx, blockId) + require.NoError(s.T(), err) + + // Verify that the mock expectations for storing the error messages were not met. + s.txErrorMessages.AssertExpectations(s.T()) + s.execClient.AssertExpectations(s.T()) + s.proto.state.AssertExpectations(s.T()) +} + +// TestHandleTransactionResultErrorMessages_ErrorCases tests the error handling of +// the HandleTransactionResultErrorMessages function in the following cases: +// +// 1. Execution node fetch error: When fetching transaction error messages from the execution node fails, +// the function should return an appropriate error and no further actions should be taken. +// 2. Storage store error after fetching results: When fetching transaction error messages succeeds, +// but storing them in the storage fails, the function should return an error and no further actions should be taken. +func (s *TxErrorMessagesCoreSuite) TestHandleTransactionResultErrorMessages_ErrorCases() { + irrecoverableCtx := irrecoverable.NewMockSignalerContext(s.T(), s.ctx) + + block := unittest.BlockWithParentFixture(s.finalizedBlock) + blockId := block.ID() + + s.connFactory.On("GetExecutionAPIClient", mock.Anything).Return(s.execClient, &mockCloser{}, nil) + + // Mock the protocol snapshot to return fixed execution node IDs. + setupReceiptsForBlock(s.receipts, block, s.enNodeIDs.NodeIDs()[0]) + s.proto.snapshot.On("Identities", mock.Anything).Return(s.enNodeIDs, nil) + s.proto.state.On("AtBlockID", blockId).Return(s.proto.snapshot) + + s.Run("Execution node fetch error", func() { + // Mock the txErrorMessages storage to confirm that error messages do not exist yet. + s.txErrorMessages.On("Exists", blockId).Return(false, nil).Once() + + // Simulate an error when fetching transaction error messages from the execution node. + exeEventReq := &execproto.GetTransactionErrorMessagesByBlockIDRequest{ + BlockId: blockId[:], + } + s.execClient.On("GetTransactionErrorMessagesByBlockID", mock.Anything, exeEventReq). + Return(nil, fmt.Errorf("execution node fetch error")).Once() + + core := s.initCore() + err := core.HandleTransactionResultErrorMessages(irrecoverableCtx, blockId) + + // Assert that the function returns an error due to the client fetch error. + require.Error(s.T(), err) + require.Contains(s.T(), err.Error(), "execution node fetch error") + + // Ensure that no further steps are taken after the client fetch error. + s.txErrorMessages.AssertNotCalled(s.T(), "Store", mock.Anything, mock.Anything) + }) + + s.Run("Storage error after fetching results", func() { + // Simulate successful fetching of transaction error messages but error in storing them. + + // Mock the txErrorMessages storage to confirm that error messages do not exist yet. + s.txErrorMessages.On("Exists", blockId).Return(false, nil).Once() + + // Create mock transaction results with a mix of failed and non-failed transactions. + resultsByBlockID := mockTransactionResultsByBlock(5) + + // Prepare a request to fetch transaction error messages by block ID from execution nodes. + exeEventReq := &execproto.GetTransactionErrorMessagesByBlockIDRequest{ + BlockId: blockId[:], + } + s.execClient.On("GetTransactionErrorMessagesByBlockID", mock.Anything, exeEventReq). + Return(createTransactionErrorMessagesResponse(resultsByBlockID), nil).Once() + + // Simulate an error when attempting to store the fetched transaction error messages in storage. + expectedStoreTxErrorMessages := createExpectedTxErrorMessages(resultsByBlockID, s.enNodeIDs.NodeIDs()[0]) + s.txErrorMessages.On("Store", blockId, expectedStoreTxErrorMessages). + Return(fmt.Errorf("storage error")).Once() + + core := s.initCore() + err := core.HandleTransactionResultErrorMessages(irrecoverableCtx, blockId) + + // Assert that the function returns an error due to the store error. + require.Error(s.T(), err) + require.Contains(s.T(), err.Error(), "storage error") + + // Ensure that storage existence check and transaction fetch were called before the store error. + s.txErrorMessages.AssertCalled(s.T(), "Exists", blockId) + s.execClient.AssertCalled(s.T(), "GetTransactionErrorMessagesByBlockID", mock.Anything, exeEventReq) + }) +} + +// initCore create new instance of transaction error messages core. +func (s *TxErrorMessagesCoreSuite) initCore() *TxErrorMessagesCore { + execNodeIdentitiesProvider := commonrpc.NewExecutionNodeIdentitiesProvider( + s.log, + s.proto.state, + s.receipts, + flow.IdentifierList{}, + s.enNodeIDs.NodeIDs(), + ) + + // Initialize the backend + backend, err := backend.New(backend.Params{ + State: s.proto.state, + ExecutionReceipts: s.receipts, + ConnFactory: s.connFactory, + MaxHeightRange: backend.DefaultMaxHeightRange, + Log: s.log, + SnapshotHistoryLimit: backend.DefaultSnapshotHistoryLimit, + Communicator: backend.NewNodeCommunicator(false), + ScriptExecutionMode: backend.IndexQueryModeExecutionNodesOnly, + TxResultQueryMode: backend.IndexQueryModeExecutionNodesOnly, + ChainID: flow.Testnet, + ExecNodeIdentitiesProvider: execNodeIdentitiesProvider, + }) + require.NoError(s.T(), err) + + core := NewTxErrorMessagesCore( + s.log, + backend, + s.txErrorMessages, + execNodeIdentitiesProvider, + ) + return core +} + +// createExpectedTxErrorMessages creates a list of expected transaction error messages based on transaction results +func createExpectedTxErrorMessages(resultsByBlockID []flow.LightTransactionResult, executionNode flow.Identifier) []flow.TransactionResultErrorMessage { + // Prepare the expected transaction error messages that should be stored. + var expectedStoreTxErrorMessages []flow.TransactionResultErrorMessage + + for i, result := range resultsByBlockID { + if result.Failed { + errMsg := fmt.Sprintf("%s.%s", expectedErrorMsg, result.TransactionID) + + expectedStoreTxErrorMessages = append(expectedStoreTxErrorMessages, + flow.TransactionResultErrorMessage{ + TransactionID: result.TransactionID, + ErrorMessage: errMsg, + Index: uint32(i), + ExecutorID: executionNode, + }) + } + } + + return expectedStoreTxErrorMessages +} + +// mockTransactionResultsByBlock create mock transaction results with a mix of failed and non-failed transactions. +func mockTransactionResultsByBlock(count int) []flow.LightTransactionResult { + // Create mock transaction results with a mix of failed and non-failed transactions. + resultsByBlockID := make([]flow.LightTransactionResult, 0) + for i := 0; i < count; i++ { + resultsByBlockID = append(resultsByBlockID, flow.LightTransactionResult{ + TransactionID: unittest.IdentifierFixture(), + Failed: i%2 == 0, // create a mix of failed and non-failed transactions + ComputationUsed: 0, + }) + } + + return resultsByBlockID +} + +// setupReceiptsForBlock sets up mock execution receipts for a block and returns the receipts along +// with the identities of the execution nodes that processed them. +func setupReceiptsForBlock(receipts *storage.ExecutionReceipts, block *flow.Block, eNodeID flow.Identifier) { + receipt1 := unittest.ReceiptForBlockFixture(block) + receipt1.ExecutorID = eNodeID + receipt2 := unittest.ReceiptForBlockFixture(block) + receipt2.ExecutorID = eNodeID + receipt1.ExecutionResult = receipt2.ExecutionResult + + receiptsList := flow.ExecutionReceiptList{receipt1, receipt2} + + receipts. + On("ByBlockID", block.ID()). + Return(func(flow.Identifier) flow.ExecutionReceiptList { + return receiptsList + }, nil) +} + +// createTransactionErrorMessagesResponse create TransactionErrorMessagesResponse from execution node based on results. +func createTransactionErrorMessagesResponse(resultsByBlockID []flow.LightTransactionResult) *execproto.GetTransactionErrorMessagesResponse { + exeErrMessagesResp := &execproto.GetTransactionErrorMessagesResponse{} + + for i, result := range resultsByBlockID { + if result.Failed { + errMsg := fmt.Sprintf("%s.%s", expectedErrorMsg, result.TransactionID) + exeErrMessagesResp.Results = append(exeErrMessagesResp.Results, &execproto.GetTransactionErrorMessagesResponse_Result{ + TransactionId: result.TransactionID[:], + ErrorMessage: errMsg, + Index: uint32(i), + }) + } + } + + return exeErrMessagesResp +} diff --git a/engine/access/ingestion/tx_error_messages/tx_error_messages_engine.go b/engine/access/ingestion/tx_error_messages/tx_error_messages_engine.go new file mode 100644 index 00000000000..cdd65bdc0b3 --- /dev/null +++ b/engine/access/ingestion/tx_error_messages/tx_error_messages_engine.go @@ -0,0 +1,181 @@ +package tx_error_messages + +import ( + "context" + "fmt" + "time" + + "github.com/rs/zerolog" + "github.com/sethvargo/go-retry" + + "github.com/onflow/flow-go/consensus/hotstuff/model" + "github.com/onflow/flow-go/engine" + "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/module" + "github.com/onflow/flow-go/module/component" + "github.com/onflow/flow-go/module/irrecoverable" + "github.com/onflow/flow-go/module/jobqueue" + "github.com/onflow/flow-go/module/util" + "github.com/onflow/flow-go/state/protocol" + "github.com/onflow/flow-go/storage" +) + +const ( + // processTxErrorMessagesWorkersCount defines the number of workers that + // concurrently process transaction error messages in the job queue. + processTxErrorMessagesWorkersCount = 3 + + // defaultRetryDelay specifies the initial delay for the exponential backoff + // when the process of fetching transaction error messages fails. + // + // This delay increases with each retry attempt, up to the maximum defined by + // defaultMaxRetryDelay. + defaultRetryDelay = 1 * time.Second + + // defaultMaxRetryDelay specifies the maximum delay for the exponential backoff + // when the process of fetching transaction error messages fails. + // + // Once this delay is reached, the backoff will no longer increase with each retry. + defaultMaxRetryDelay = 5 * time.Minute +) + +// Engine represents the component responsible for managing and processing +// transaction result error messages. It retrieves, stores, +// and retries fetching of error messages from execution nodes, ensuring +// that they are processed and stored for sealed blocks. +// +// No errors are expected during normal operation. +type Engine struct { + *component.ComponentManager + + log zerolog.Logger + state protocol.State + headers storage.Headers + + // Job queue + txErrorMessagesConsumer *jobqueue.ComponentConsumer + // Notifiers for queue consumer + txErrorMessagesNotifier engine.Notifier + + txErrorMessagesCore *TxErrorMessagesCore // core logic for handling tx error messages +} + +// New creates a new Engine instance, initializing all necessary components +// for processing transaction result error messages. This includes setting +// up the job queue and the notifier for handling finalized blocks. +// +// No errors are expected during normal operation. +func New( + log zerolog.Logger, + state protocol.State, + headers storage.Headers, + txErrorMessagesProcessedHeight storage.ConsumerProgress, + txErrorMessagesCore *TxErrorMessagesCore, +) (*Engine, error) { + e := &Engine{ + log: log.With().Str("engine", "tx_error_messages_engine").Logger(), + state: state, + headers: headers, + txErrorMessagesCore: txErrorMessagesCore, + txErrorMessagesNotifier: engine.NewNotifier(), + } + + // jobqueue Jobs object that tracks sealed blocks by height. This is used by the txErrorMessagesConsumer + // to get a sequential list of sealed blocks. + sealedBlockReader := jobqueue.NewSealedBlockHeaderReader(state, headers) + + var err error + // Create a job queue that will process error messages for new sealed blocks. + // It listens to block finalization events from `txErrorMessagesNotifier`, then checks if there + // are new sealed blocks with `sealedBlockReader`. If there are, it starts workers to process + // them with `processTxResultErrorMessagesJob`, which fetches transaction error messages. At most + // `processTxErrorMessagesWorkersCount` workers will be created for concurrent processing. + // When a sealed block's error messages has been processed, it updates and persists the highest consecutive + // processed height with `txErrorMessagesProcessedHeight`. That way, if the node crashes, + // it reads the `txErrorMessagesProcessedHeight` and resume from `txErrorMessagesProcessedHeight + 1`. + // If the database is empty, rootHeight will be used to init the last processed height. + e.txErrorMessagesConsumer, err = jobqueue.NewComponentConsumer( + e.log.With().Str("engine", "tx_error_messages").Logger(), + e.txErrorMessagesNotifier.Channel(), + txErrorMessagesProcessedHeight, + sealedBlockReader, + e.state.Params().SealedRoot().Height, + e.processTxResultErrorMessagesJob, + processTxErrorMessagesWorkersCount, + 0, + ) + if err != nil { + return nil, fmt.Errorf("error creating transaction result error messages jobqueue: %w", err) + } + + // Add workers + e.ComponentManager = component.NewComponentManagerBuilder(). + AddWorker(e.runTxResultErrorMessagesConsumer). + Build() + + return e, nil +} + +// processTxResultErrorMessagesJob processes a job for transaction error messages by +// converting the job to a block and processing error messages. If processing +// fails for all attempts, it logs the error. +func (e *Engine) processTxResultErrorMessagesJob(ctx irrecoverable.SignalerContext, job module.Job, done func()) { + header, err := jobqueue.JobToBlockHeader(job) + if err != nil { + ctx.Throw(fmt.Errorf("failed to convert job to block: %w", err)) + } + + err = e.processErrorMessagesForBlock(ctx, header.ID()) + if err == nil { + done() + return + } + + e.log.Error(). + Err(err). + Str("job_id", string(job.ID())). + Msg("error encountered while processing transaction result error messages job") +} + +// runTxResultErrorMessagesConsumer runs the txErrorMessagesConsumer component +func (e *Engine) runTxResultErrorMessagesConsumer(ctx irrecoverable.SignalerContext, ready component.ReadyFunc) { + e.txErrorMessagesConsumer.Start(ctx) + + err := util.WaitClosed(ctx, e.txErrorMessagesConsumer.Ready()) + if err == nil { + ready() + } + + <-e.txErrorMessagesConsumer.Done() +} + +// OnFinalizedBlock is called by the follower engine after a block has been finalized and the state has been updated. +// Receives block finalized events from the finalization distributor and forwards them to the txErrorMessagesConsumer. +func (e *Engine) OnFinalizedBlock(*model.Block) { + e.txErrorMessagesNotifier.Notify() +} + +// processErrorMessagesForBlock processes transaction result error messages for block. +// If the process fails, it will retry, using exponential backoff. +// +// No errors are expected during normal operation. +func (e *Engine) processErrorMessagesForBlock(ctx context.Context, blockID flow.Identifier) error { + backoff := retry.NewExponential(defaultRetryDelay) + backoff = retry.WithCappedDuration(defaultMaxRetryDelay, backoff) + backoff = retry.WithJitterPercent(15, backoff) + + attempt := 0 + return retry.Do(ctx, backoff, func(context.Context) error { + if attempt > 0 { + e.log.Debug(). + Str("block_id", blockID.String()). + Uint64("attempt", uint64(attempt)). + Msgf("retrying process transaction result error messages") + + } + attempt++ + err := e.txErrorMessagesCore.HandleTransactionResultErrorMessages(ctx, blockID) + + return retry.RetryableError(err) + }) +} diff --git a/engine/access/ingestion/tx_error_messages/tx_error_messages_engine_test.go b/engine/access/ingestion/tx_error_messages/tx_error_messages_engine_test.go new file mode 100644 index 00000000000..b1edcffb9a5 --- /dev/null +++ b/engine/access/ingestion/tx_error_messages/tx_error_messages_engine_test.go @@ -0,0 +1,255 @@ +package tx_error_messages + +import ( + "context" + "os" + "sync" + "testing" + "time" + + "github.com/dgraph-io/badger/v2" + execproto "github.com/onflow/flow/protobuf/go/flow/execution" + "github.com/rs/zerolog" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + + hotmodel "github.com/onflow/flow-go/consensus/hotstuff/model" + accessmock "github.com/onflow/flow-go/engine/access/mock" + "github.com/onflow/flow-go/engine/access/rpc/backend" + connectionmock "github.com/onflow/flow-go/engine/access/rpc/connection/mock" + commonrpc "github.com/onflow/flow-go/engine/common/rpc" + "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/module" + "github.com/onflow/flow-go/module/irrecoverable" + protocol "github.com/onflow/flow-go/state/protocol/mock" + bstorage "github.com/onflow/flow-go/storage/badger" + storage "github.com/onflow/flow-go/storage/mock" + "github.com/onflow/flow-go/utils/unittest" + "github.com/onflow/flow-go/utils/unittest/mocks" +) + +// TxErrorMessagesEngineSuite is a test suite for the transaction error messages engine. +// It sets up the necessary mocks and dependencies to test the functionality of +// handling transaction error messages. +type TxErrorMessagesEngineSuite struct { + suite.Suite + + log zerolog.Logger + proto struct { + state *protocol.FollowerState + snapshot *protocol.Snapshot + params *protocol.Params + } + headers *storage.Headers + receipts *storage.ExecutionReceipts + txErrorMessages *storage.TransactionResultErrorMessages + + enNodeIDs flow.IdentityList + execClient *accessmock.ExecutionAPIClient + connFactory *connectionmock.ConnectionFactory + + blockMap map[uint64]*flow.Block + rootBlock flow.Block + sealedBlock *flow.Header + + db *badger.DB + dbDir string + + ctx context.Context + cancel context.CancelFunc +} + +// TestTxErrorMessagesEngine runs the test suite for the transaction error messages engine. +func TestTxErrorMessagesEngine(t *testing.T) { + suite.Run(t, new(TxErrorMessagesEngineSuite)) +} + +// TearDownTest stops the engine and cleans up the db +func (s *TxErrorMessagesEngineSuite) TearDownTest() { + s.cancel() + err := os.RemoveAll(s.dbDir) + s.Require().NoError(err) +} + +func (s *TxErrorMessagesEngineSuite) SetupTest() { + s.log = zerolog.New(os.Stderr) + s.ctx, s.cancel = context.WithCancel(context.Background()) + s.db, s.dbDir = unittest.TempBadgerDB(s.T()) + // mock out protocol state + s.proto.state = protocol.NewFollowerState(s.T()) + s.proto.snapshot = protocol.NewSnapshot(s.T()) + s.proto.params = protocol.NewParams(s.T()) + s.execClient = accessmock.NewExecutionAPIClient(s.T()) + s.connFactory = connectionmock.NewConnectionFactory(s.T()) + s.headers = storage.NewHeaders(s.T()) + s.receipts = storage.NewExecutionReceipts(s.T()) + s.txErrorMessages = storage.NewTransactionResultErrorMessages(s.T()) + + blockCount := 5 + s.blockMap = make(map[uint64]*flow.Block, blockCount) + s.rootBlock = unittest.BlockFixture() + s.rootBlock.Header.Height = 0 + parent := s.rootBlock.Header + + for i := 0; i < blockCount; i++ { + block := unittest.BlockWithParentFixture(parent) + // update for next iteration + parent = block.Header + s.blockMap[block.Header.Height] = block + } + + s.sealedBlock = parent + + s.headers.On("ByHeight", mock.AnythingOfType("uint64")).Return( + mocks.ConvertStorageOutput( + mocks.StorageMapGetter(s.blockMap), + func(block *flow.Block) *flow.Header { return block.Header }, + ), + ).Maybe() + + s.proto.state.On("Params").Return(s.proto.params) + + // Mock the finalized and sealed root block header with height 0. + s.proto.params.On("FinalizedRoot").Return(s.rootBlock.Header, nil) + s.proto.params.On("SealedRoot").Return(s.rootBlock.Header, nil) + + s.proto.snapshot.On("Head").Return( + func() *flow.Header { + return s.sealedBlock + }, + nil, + ).Maybe() + + s.proto.state.On("Sealed").Return(s.proto.snapshot, nil) + s.proto.state.On("Final").Return(s.proto.snapshot, nil) + + // Create identities for 1 execution nodes. + s.enNodeIDs = unittest.IdentityListFixture(1, unittest.WithRole(flow.RoleExecution)) +} + +// initEngine creates a new instance of the transaction error messages engine +// and waits for it to start. It initializes the engine with mocked components and state. +func (s *TxErrorMessagesEngineSuite) initEngine(ctx irrecoverable.SignalerContext) *Engine { + processedTxErrorMessagesBlockHeight := bstorage.NewConsumerProgress( + s.db, + module.ConsumeProgressEngineTxErrorMessagesBlockHeight, + ) + + execNodeIdentitiesProvider := commonrpc.NewExecutionNodeIdentitiesProvider( + s.log, + s.proto.state, + s.receipts, + s.enNodeIDs.NodeIDs(), + flow.IdentifierList{}, + ) + + // Initialize the backend with the mocked state, blocks, headers, transactions, etc. + backend, err := backend.New(backend.Params{ + State: s.proto.state, + Headers: s.headers, + ExecutionReceipts: s.receipts, + ConnFactory: s.connFactory, + MaxHeightRange: backend.DefaultMaxHeightRange, + Log: s.log, + SnapshotHistoryLimit: backend.DefaultSnapshotHistoryLimit, + Communicator: backend.NewNodeCommunicator(false), + ScriptExecutionMode: backend.IndexQueryModeExecutionNodesOnly, + TxResultQueryMode: backend.IndexQueryModeExecutionNodesOnly, + ChainID: flow.Testnet, + ExecNodeIdentitiesProvider: execNodeIdentitiesProvider, + }) + require.NoError(s.T(), err) + + txResultErrorMessagesCore := NewTxErrorMessagesCore( + s.log, + backend, + s.txErrorMessages, + execNodeIdentitiesProvider, + ) + + eng, err := New( + s.log, + s.proto.state, + s.headers, + processedTxErrorMessagesBlockHeight, + txResultErrorMessagesCore, + ) + require.NoError(s.T(), err) + + eng.ComponentManager.Start(ctx) + <-eng.Ready() + + return eng +} + +// TestOnFinalizedBlockHandleTxErrorMessages tests the handling of transaction error messages +// when a new finalized block is processed. It verifies that the engine fetches transaction +// error messages from execution nodes and stores them in the database. +func (s *TxErrorMessagesEngineSuite) TestOnFinalizedBlockHandleTxErrorMessages() { + irrecoverableCtx := irrecoverable.NewMockSignalerContext(s.T(), s.ctx) + + block := unittest.BlockWithParentFixture(s.sealedBlock) + + s.blockMap[block.Header.Height] = block + s.sealedBlock = block.Header + + hotstuffBlock := hotmodel.Block{ + BlockID: block.ID(), + } + + // mock the connection factory + s.connFactory.On("GetExecutionAPIClient", mock.Anything).Return(s.execClient, &mockCloser{}, nil) + + s.proto.snapshot.On("Identities", mock.Anything).Return(s.enNodeIDs, nil) + s.proto.state.On("AtBlockID", mock.Anything).Return(s.proto.snapshot) + + count := 6 + wg := sync.WaitGroup{} + wg.Add(count) + + for _, b := range s.blockMap { + blockID := b.ID() + + // Mock the protocol snapshot to return fixed execution node IDs. + setupReceiptsForBlock(s.receipts, b, s.enNodeIDs.NodeIDs()[0]) + + // Mock the txErrorMessages storage to confirm that error messages do not exist yet. + s.txErrorMessages.On("Exists", blockID). + Return(false, nil).Once() + + // Create mock transaction results with a mix of failed and non-failed transactions. + resultsByBlockID := mockTransactionResultsByBlock(5) + + // Prepare a request to fetch transaction error messages by block ID from execution nodes. + exeEventReq := &execproto.GetTransactionErrorMessagesByBlockIDRequest{ + BlockId: blockID[:], + } + + s.execClient.On("GetTransactionErrorMessagesByBlockID", mock.Anything, exeEventReq). + Return(createTransactionErrorMessagesResponse(resultsByBlockID), nil).Once() + + // Prepare the expected transaction error messages that should be stored. + expectedStoreTxErrorMessages := createExpectedTxErrorMessages(resultsByBlockID, s.enNodeIDs.NodeIDs()[0]) + + // Mock the storage of the fetched error messages into the protocol database. + s.txErrorMessages.On("Store", blockID, expectedStoreTxErrorMessages).Return(nil). + Run(func(args mock.Arguments) { + // Ensure the test does not complete its work faster than necessary + wg.Done() + }).Once() + } + + eng := s.initEngine(irrecoverableCtx) + // process the block through the finalized callback + eng.OnFinalizedBlock(&hotstuffBlock) + + // Verify that all transaction error messages were processed within the timeout. + unittest.RequireReturnsBefore(s.T(), wg.Wait, 2*time.Second, "expect to process new block before timeout") + + // Ensure all expectations were met. + s.txErrorMessages.AssertExpectations(s.T()) + s.headers.AssertExpectations(s.T()) + s.proto.state.AssertExpectations(s.T()) + s.execClient.AssertExpectations(s.T()) +} diff --git a/engine/access/mock/execution_api_client.go b/engine/access/mock/execution_api_client.go index fd5fc20c718..7aa04d143c7 100644 --- a/engine/access/mock/execution_api_client.go +++ b/engine/access/mock/execution_api_client.go @@ -349,6 +349,43 @@ func (_m *ExecutionAPIClient) GetTransactionErrorMessagesByBlockID(ctx context.C return r0, r1 } +// GetTransactionExecutionMetricsAfter provides a mock function with given fields: ctx, in, opts +func (_m *ExecutionAPIClient) GetTransactionExecutionMetricsAfter(ctx context.Context, in *execution.GetTransactionExecutionMetricsAfterRequest, opts ...grpc.CallOption) (*execution.GetTransactionExecutionMetricsAfterResponse, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, in) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + if len(ret) == 0 { + panic("no return value specified for GetTransactionExecutionMetricsAfter") + } + + var r0 *execution.GetTransactionExecutionMetricsAfterResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *execution.GetTransactionExecutionMetricsAfterRequest, ...grpc.CallOption) (*execution.GetTransactionExecutionMetricsAfterResponse, error)); ok { + return rf(ctx, in, opts...) + } + if rf, ok := ret.Get(0).(func(context.Context, *execution.GetTransactionExecutionMetricsAfterRequest, ...grpc.CallOption) *execution.GetTransactionExecutionMetricsAfterResponse); ok { + r0 = rf(ctx, in, opts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*execution.GetTransactionExecutionMetricsAfterResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *execution.GetTransactionExecutionMetricsAfterRequest, ...grpc.CallOption) error); ok { + r1 = rf(ctx, in, opts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // GetTransactionResult provides a mock function with given fields: ctx, in, opts func (_m *ExecutionAPIClient) GetTransactionResult(ctx context.Context, in *execution.GetTransactionResultRequest, opts ...grpc.CallOption) (*execution.GetTransactionResultResponse, error) { _va := make([]interface{}, len(opts)) diff --git a/engine/access/mock/execution_api_server.go b/engine/access/mock/execution_api_server.go index e61517cb617..2c3f1f3b5a7 100644 --- a/engine/access/mock/execution_api_server.go +++ b/engine/access/mock/execution_api_server.go @@ -284,6 +284,36 @@ func (_m *ExecutionAPIServer) GetTransactionErrorMessagesByBlockID(_a0 context.C return r0, r1 } +// GetTransactionExecutionMetricsAfter provides a mock function with given fields: _a0, _a1 +func (_m *ExecutionAPIServer) GetTransactionExecutionMetricsAfter(_a0 context.Context, _a1 *execution.GetTransactionExecutionMetricsAfterRequest) (*execution.GetTransactionExecutionMetricsAfterResponse, error) { + ret := _m.Called(_a0, _a1) + + if len(ret) == 0 { + panic("no return value specified for GetTransactionExecutionMetricsAfter") + } + + var r0 *execution.GetTransactionExecutionMetricsAfterResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *execution.GetTransactionExecutionMetricsAfterRequest) (*execution.GetTransactionExecutionMetricsAfterResponse, error)); ok { + return rf(_a0, _a1) + } + if rf, ok := ret.Get(0).(func(context.Context, *execution.GetTransactionExecutionMetricsAfterRequest) *execution.GetTransactionExecutionMetricsAfterResponse); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*execution.GetTransactionExecutionMetricsAfterResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *execution.GetTransactionExecutionMetricsAfterRequest) error); ok { + r1 = rf(_a0, _a1) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // GetTransactionResult provides a mock function with given fields: _a0, _a1 func (_m *ExecutionAPIServer) GetTransactionResult(_a0 context.Context, _a1 *execution.GetTransactionResultRequest) (*execution.GetTransactionResultResponse, error) { ret := _m.Called(_a0, _a1) diff --git a/engine/access/rest/README.md b/engine/access/rest/README.md index d94af68c238..746f9ec2415 100644 --- a/engine/access/rest/README.md +++ b/engine/access/rest/README.md @@ -5,12 +5,15 @@ the [Flow OpenAPI definition](https://github.com/onflow/flow/blob/master/openapi available on our [docs site](https://docs.onflow.org/http-api/). ## Packages - - `rest`: The HTTP handlers for the server generator and the select filter, implementation of handling local requests. -- `middleware`: The common [middlewares](https://github.com/gorilla/mux#middleware) that all request pass through. -- `models`: The generated models using openapi generators and implementation of model builders. -- `request`: Implementation of API requests that provide validation for input data and build request models. -- `routes`: The common HTTP handlers for all the requests, tests for each request. +- `common`: Includes shared components for REST requests. + - `middleware`: The common [middlewares](https://github.com/gorilla/mux#middleware) that all request pass through. + - `models`: The common generated models using openapi generators. +- `http`: Implements core HTTP handling functionality for access node. + - `models`: The generated models using openapi generators and implementation of model builders. + - `request`: Implementation of API requests that provide validation for input data and build request models. + - `routes`: The HTTP handlers for all http requests, tests for each request. +- `router`: Implementation of building HTTP routers with common middleware and routes. - `apiproxy`: Implementation of proxy backend handler which includes the local backend and forwards the methods which can't be handled locally to an upstream using gRPC API. This is used by observers that don't have all data in their local db. @@ -19,7 +22,7 @@ local db. 1. Every incoming request passes through a common set of middlewares - logging middleware, query expandable and query select middleware defined in the middleware package. -2. Each request is then wrapped by our handler (`rest/handler.go`) and request input data is used to build the request +2. Each request is then wrapped by our handler (`rest/http/handler.go`) and request input data is used to build the request models defined in request package. 3. The request is then sent to the corresponding API handler based on the configuration in the router. 4. Each handler implements actions to perform the request (database lookups etc) and after the response is built using diff --git a/engine/access/rest/models/error.go b/engine/access/rest/common/error.go similarity index 98% rename from engine/access/rest/models/error.go rename to engine/access/rest/common/error.go index 2247b38743b..d39ad03e193 100644 --- a/engine/access/rest/models/error.go +++ b/engine/access/rest/common/error.go @@ -1,4 +1,4 @@ -package models +package common import "net/http" diff --git a/engine/access/rest/routes/http_handler.go b/engine/access/rest/common/http_request_handler.go similarity index 85% rename from engine/access/rest/routes/http_handler.go rename to engine/access/rest/common/http_request_handler.go index f6a190ba0ad..bb78ff5b3a3 100644 --- a/engine/access/rest/routes/http_handler.go +++ b/engine/access/rest/common/http_request_handler.go @@ -1,4 +1,4 @@ -package routes +package common import ( "encoding/json" @@ -11,12 +11,12 @@ import ( "google.golang.org/grpc/codes" "google.golang.org/grpc/status" - "github.com/onflow/flow-go/engine/access/rest/models" + "github.com/onflow/flow-go/engine/access/rest/common/models" fvmErrors "github.com/onflow/flow-go/fvm/errors" "github.com/onflow/flow-go/model/flow" ) -const MaxRequestSize = 2 << 20 // 2MB +const DefaultMaxRequestSize = 2 << 20 // 2MB // HttpHandler is custom http handler implementing custom handler function. // HttpHandler function allows easier handling of errors and responses as it @@ -24,15 +24,19 @@ const MaxRequestSize = 2 << 20 // 2MB type HttpHandler struct { Logger zerolog.Logger Chain flow.Chain + + MaxRequestSize int64 } func NewHttpHandler( logger zerolog.Logger, chain flow.Chain, + maxRequestSize int64, ) *HttpHandler { return &HttpHandler{ - Logger: logger, - Chain: chain, + Logger: logger, + Chain: chain, + MaxRequestSize: maxRequestSize, } } @@ -43,18 +47,18 @@ func (h *HttpHandler) VerifyRequest(w http.ResponseWriter, r *http.Request) erro errLog := h.Logger.With().Str("request_url", r.URL.String()).Logger() // limit requested body size - r.Body = http.MaxBytesReader(w, r.Body, MaxRequestSize) + r.Body = http.MaxBytesReader(w, r.Body, h.MaxRequestSize) err := r.ParseForm() if err != nil { - h.errorHandler(w, err, errLog) + h.ErrorHandler(w, err, errLog) return err } return nil } -func (h *HttpHandler) errorHandler(w http.ResponseWriter, err error, errorLogger zerolog.Logger) { +func (h *HttpHandler) ErrorHandler(w http.ResponseWriter, err error, errorLogger zerolog.Logger) { // rest status type error should be returned with status and user message provided - var statusErr models.StatusError + var statusErr StatusError if errors.As(err, &statusErr) { h.errorResponse(w, statusErr.Status(), statusErr.UserMessage(), errorLogger) return @@ -98,8 +102,8 @@ func (h *HttpHandler) errorHandler(w http.ResponseWriter, err error, errorLogger h.errorResponse(w, http.StatusInternalServerError, msg, errorLogger) } -// jsonResponse builds a JSON response and send it to the client -func (h *HttpHandler) jsonResponse(w http.ResponseWriter, code int, response interface{}, errLogger zerolog.Logger) { +// JsonResponse builds a JSON response and send it to the client +func (h *HttpHandler) JsonResponse(w http.ResponseWriter, code int, response interface{}, errLogger zerolog.Logger) { w.Header().Set("Content-Type", "application/json; charset=UTF-8") // serialize response to JSON and handler errors @@ -131,5 +135,5 @@ func (h *HttpHandler) errorResponse( Code: int32(returnCode), Message: responseMessage, } - h.jsonResponse(w, returnCode, modelError, logger) + h.JsonResponse(w, returnCode, modelError, logger) } diff --git a/engine/access/rest/middleware/common_query_params.go b/engine/access/rest/common/middleware/common_query_params.go similarity index 100% rename from engine/access/rest/middleware/common_query_params.go rename to engine/access/rest/common/middleware/common_query_params.go diff --git a/engine/access/rest/middleware/common_query_params_test.go b/engine/access/rest/common/middleware/common_query_params_test.go similarity index 100% rename from engine/access/rest/middleware/common_query_params_test.go rename to engine/access/rest/common/middleware/common_query_params_test.go diff --git a/engine/access/rest/middleware/logging.go b/engine/access/rest/common/middleware/logging.go similarity index 100% rename from engine/access/rest/middleware/logging.go rename to engine/access/rest/common/middleware/logging.go diff --git a/engine/access/rest/middleware/metrics.go b/engine/access/rest/common/middleware/metrics.go similarity index 100% rename from engine/access/rest/middleware/metrics.go rename to engine/access/rest/common/middleware/metrics.go diff --git a/engine/access/rest/middleware/request_attribute.go b/engine/access/rest/common/middleware/request_attribute.go similarity index 100% rename from engine/access/rest/middleware/request_attribute.go rename to engine/access/rest/common/middleware/request_attribute.go diff --git a/engine/access/rest/models/model_error.go b/engine/access/rest/common/models/model_error.go similarity index 100% rename from engine/access/rest/models/model_error.go rename to engine/access/rest/common/models/model_error.go diff --git a/engine/access/rest/request/request.go b/engine/access/rest/common/request.go similarity index 50% rename from engine/access/rest/request/request.go rename to engine/access/rest/common/request.go index e8eeda9d1e6..b0b4a67f501 100644 --- a/engine/access/rest/request/request.go +++ b/engine/access/rest/common/request.go @@ -1,4 +1,4 @@ -package request +package common import ( "net/http" @@ -6,7 +6,7 @@ import ( "github.com/gorilla/mux" - "github.com/onflow/flow-go/engine/access/rest/middleware" + "github.com/onflow/flow-go/engine/access/rest/common/middleware" "github.com/onflow/flow-go/model/flow" ) @@ -18,102 +18,6 @@ type Request struct { Chain flow.Chain } -func (rd *Request) GetScriptRequest() (GetScript, error) { - var req GetScript - err := req.Build(rd) - return req, err -} - -func (rd *Request) GetBlockRequest() (GetBlock, error) { - var req GetBlock - err := req.Build(rd) - return req, err -} - -func (rd *Request) GetBlockByIDsRequest() (GetBlockByIDs, error) { - var req GetBlockByIDs - err := req.Build(rd) - return req, err -} - -func (rd *Request) GetBlockPayloadRequest() (GetBlockPayload, error) { - var req GetBlockPayload - err := req.Build(rd) - return req, err -} - -func (rd *Request) GetCollectionRequest() (GetCollection, error) { - var req GetCollection - err := req.Build(rd) - return req, err -} - -func (rd *Request) GetAccountRequest() (GetAccount, error) { - var req GetAccount - err := req.Build(rd) - return req, err -} - -func (rd *Request) GetAccountBalanceRequest() (GetAccountBalance, error) { - var req GetAccountBalance - err := req.Build(rd) - return req, err -} - -func (rd *Request) GetAccountKeysRequest() (GetAccountKeys, error) { - var req GetAccountKeys - err := req.Build(rd) - return req, err -} - -func (rd *Request) GetAccountKeyRequest() (GetAccountKey, error) { - var req GetAccountKey - err := req.Build(rd) - return req, err -} - -func (rd *Request) GetExecutionResultByBlockIDsRequest() (GetExecutionResultByBlockIDs, error) { - var req GetExecutionResultByBlockIDs - err := req.Build(rd) - return req, err -} - -func (rd *Request) GetExecutionResultRequest() (GetExecutionResult, error) { - var req GetExecutionResult - err := req.Build(rd) - return req, err -} - -func (rd *Request) GetTransactionRequest() (GetTransaction, error) { - var req GetTransaction - err := req.Build(rd) - return req, err -} - -func (rd *Request) GetTransactionResultRequest() (GetTransactionResult, error) { - var req GetTransactionResult - err := req.Build(rd) - return req, err -} - -func (rd *Request) GetEventsRequest() (GetEvents, error) { - var req GetEvents - err := req.Build(rd) - return req, err -} - -func (rd *Request) CreateTransactionRequest() (CreateTransaction, error) { - var req CreateTransaction - err := req.Build(rd) - return req, err -} - -func (rd *Request) SubscribeEventsRequest() (SubscribeEvents, error) { - var req SubscribeEvents - err := req.Build(rd) - return req, err -} - func (rd *Request) Expands(field string) bool { return rd.ExpandFields[field] } diff --git a/engine/access/rest/routes/handler.go b/engine/access/rest/http/handler.go similarity index 80% rename from engine/access/rest/routes/handler.go rename to engine/access/rest/http/handler.go index 2779fe32699..cac602a6888 100644 --- a/engine/access/rest/routes/handler.go +++ b/engine/access/rest/http/handler.go @@ -1,4 +1,4 @@ -package routes +package http import ( "net/http" @@ -6,8 +6,8 @@ import ( "github.com/rs/zerolog" "github.com/onflow/flow-go/access" - "github.com/onflow/flow-go/engine/access/rest/models" - "github.com/onflow/flow-go/engine/access/rest/request" + "github.com/onflow/flow-go/engine/access/rest/common" + "github.com/onflow/flow-go/engine/access/rest/http/models" "github.com/onflow/flow-go/engine/access/rest/util" "github.com/onflow/flow-go/model/flow" ) @@ -15,7 +15,7 @@ import ( // ApiHandlerFunc is a function that contains endpoint handling logic, // it fetches necessary resources and returns an error or response model. type ApiHandlerFunc func( - r *request.Request, + r *common.Request, backend access.API, generator models.LinkGenerator, ) (interface{}, error) @@ -24,7 +24,7 @@ type ApiHandlerFunc func( // Handler function allows easier handling of errors and responses as it // wraps functionality for handling error and responses outside of endpoint handling. type Handler struct { - *HttpHandler + *common.HttpHandler backend access.API linkGenerator models.LinkGenerator apiHandlerFunc ApiHandlerFunc @@ -36,12 +36,13 @@ func NewHandler( handlerFunc ApiHandlerFunc, generator models.LinkGenerator, chain flow.Chain, + maxRequestSize int64, ) *Handler { handler := &Handler{ backend: backend, apiHandlerFunc: handlerFunc, linkGenerator: generator, - HttpHandler: NewHttpHandler(logger, chain), + HttpHandler: common.NewHttpHandler(logger, chain, maxRequestSize), } return handler @@ -57,22 +58,22 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { if err != nil { return } - decoratedRequest := request.Decorate(r, h.Chain) + decoratedRequest := common.Decorate(r, h.Chain) // execute handler function and check for error response, err := h.apiHandlerFunc(decoratedRequest, h.backend, h.linkGenerator) if err != nil { - h.errorHandler(w, err, errLog) + h.ErrorHandler(w, err, errLog) return } // apply the select filter if any select fields have been specified response, err = util.SelectFilter(response, decoratedRequest.Selects()) if err != nil { - h.errorHandler(w, err, errLog) + h.ErrorHandler(w, err, errLog) return } // write response to response stream - h.jsonResponse(w, http.StatusOK, response, errLog) + h.JsonResponse(w, http.StatusOK, response, errLog) } diff --git a/engine/access/rest/models/account.go b/engine/access/rest/http/models/account.go similarity index 100% rename from engine/access/rest/models/account.go rename to engine/access/rest/http/models/account.go diff --git a/engine/access/rest/models/block.go b/engine/access/rest/http/models/block.go similarity index 100% rename from engine/access/rest/models/block.go rename to engine/access/rest/http/models/block.go diff --git a/engine/access/rest/models/collection.go b/engine/access/rest/http/models/collection.go similarity index 100% rename from engine/access/rest/models/collection.go rename to engine/access/rest/http/models/collection.go diff --git a/engine/access/rest/models/enums.go b/engine/access/rest/http/models/enums.go similarity index 100% rename from engine/access/rest/models/enums.go rename to engine/access/rest/http/models/enums.go diff --git a/engine/access/rest/models/event.go b/engine/access/rest/http/models/event.go similarity index 100% rename from engine/access/rest/models/event.go rename to engine/access/rest/http/models/event.go diff --git a/engine/access/rest/models/execution_result.go b/engine/access/rest/http/models/execution_result.go similarity index 100% rename from engine/access/rest/models/execution_result.go rename to engine/access/rest/http/models/execution_result.go diff --git a/engine/access/rest/models/link.go b/engine/access/rest/http/models/link.go similarity index 100% rename from engine/access/rest/models/link.go rename to engine/access/rest/http/models/link.go diff --git a/engine/access/rest/models/model_account.go b/engine/access/rest/http/models/model_account.go similarity index 100% rename from engine/access/rest/models/model_account.go rename to engine/access/rest/http/models/model_account.go diff --git a/engine/access/rest/models/model_account__expandable.go b/engine/access/rest/http/models/model_account__expandable.go similarity index 100% rename from engine/access/rest/models/model_account__expandable.go rename to engine/access/rest/http/models/model_account__expandable.go diff --git a/engine/access/rest/models/model_account_balance.go b/engine/access/rest/http/models/model_account_balance.go similarity index 100% rename from engine/access/rest/models/model_account_balance.go rename to engine/access/rest/http/models/model_account_balance.go diff --git a/engine/access/rest/models/model_account_public_key.go b/engine/access/rest/http/models/model_account_public_key.go similarity index 100% rename from engine/access/rest/models/model_account_public_key.go rename to engine/access/rest/http/models/model_account_public_key.go diff --git a/engine/access/rest/models/model_account_public_keys.go b/engine/access/rest/http/models/model_account_public_keys.go similarity index 100% rename from engine/access/rest/models/model_account_public_keys.go rename to engine/access/rest/http/models/model_account_public_keys.go diff --git a/engine/access/rest/models/model_aggregated_signature.go b/engine/access/rest/http/models/model_aggregated_signature.go similarity index 100% rename from engine/access/rest/models/model_aggregated_signature.go rename to engine/access/rest/http/models/model_aggregated_signature.go diff --git a/engine/access/rest/models/model_block.go b/engine/access/rest/http/models/model_block.go similarity index 100% rename from engine/access/rest/models/model_block.go rename to engine/access/rest/http/models/model_block.go diff --git a/engine/access/rest/models/model_block__expandable.go b/engine/access/rest/http/models/model_block__expandable.go similarity index 100% rename from engine/access/rest/models/model_block__expandable.go rename to engine/access/rest/http/models/model_block__expandable.go diff --git a/engine/access/rest/models/model_block_events.go b/engine/access/rest/http/models/model_block_events.go similarity index 100% rename from engine/access/rest/models/model_block_events.go rename to engine/access/rest/http/models/model_block_events.go diff --git a/engine/access/rest/models/model_block_header.go b/engine/access/rest/http/models/model_block_header.go similarity index 100% rename from engine/access/rest/models/model_block_header.go rename to engine/access/rest/http/models/model_block_header.go diff --git a/engine/access/rest/models/model_block_height.go b/engine/access/rest/http/models/model_block_height.go similarity index 100% rename from engine/access/rest/models/model_block_height.go rename to engine/access/rest/http/models/model_block_height.go diff --git a/engine/access/rest/models/model_block_payload.go b/engine/access/rest/http/models/model_block_payload.go similarity index 100% rename from engine/access/rest/models/model_block_payload.go rename to engine/access/rest/http/models/model_block_payload.go diff --git a/engine/access/rest/models/model_block_seal.go b/engine/access/rest/http/models/model_block_seal.go similarity index 100% rename from engine/access/rest/models/model_block_seal.go rename to engine/access/rest/http/models/model_block_seal.go diff --git a/engine/access/rest/models/model_chunk.go b/engine/access/rest/http/models/model_chunk.go similarity index 100% rename from engine/access/rest/models/model_chunk.go rename to engine/access/rest/http/models/model_chunk.go diff --git a/engine/access/rest/models/model_collection.go b/engine/access/rest/http/models/model_collection.go similarity index 100% rename from engine/access/rest/models/model_collection.go rename to engine/access/rest/http/models/model_collection.go diff --git a/engine/access/rest/models/model_collection__expandable.go b/engine/access/rest/http/models/model_collection__expandable.go similarity index 100% rename from engine/access/rest/models/model_collection__expandable.go rename to engine/access/rest/http/models/model_collection__expandable.go diff --git a/engine/access/rest/models/model_collection_guarantee.go b/engine/access/rest/http/models/model_collection_guarantee.go similarity index 100% rename from engine/access/rest/models/model_collection_guarantee.go rename to engine/access/rest/http/models/model_collection_guarantee.go diff --git a/engine/access/rest/models/model_compatible_range.go b/engine/access/rest/http/models/model_compatible_range.go similarity index 100% rename from engine/access/rest/models/model_compatible_range.go rename to engine/access/rest/http/models/model_compatible_range.go diff --git a/engine/access/rest/models/model_event.go b/engine/access/rest/http/models/model_event.go similarity index 100% rename from engine/access/rest/models/model_event.go rename to engine/access/rest/http/models/model_event.go diff --git a/engine/access/rest/models/model_execution_result.go b/engine/access/rest/http/models/model_execution_result.go similarity index 100% rename from engine/access/rest/models/model_execution_result.go rename to engine/access/rest/http/models/model_execution_result.go diff --git a/engine/access/rest/models/model_hashing_algorithm.go b/engine/access/rest/http/models/model_hashing_algorithm.go similarity index 100% rename from engine/access/rest/models/model_hashing_algorithm.go rename to engine/access/rest/http/models/model_hashing_algorithm.go diff --git a/engine/access/rest/models/model_inline_response_200.go b/engine/access/rest/http/models/model_inline_response_200.go similarity index 100% rename from engine/access/rest/models/model_inline_response_200.go rename to engine/access/rest/http/models/model_inline_response_200.go diff --git a/engine/access/rest/models/model_links.go b/engine/access/rest/http/models/model_links.go similarity index 100% rename from engine/access/rest/models/model_links.go rename to engine/access/rest/http/models/model_links.go diff --git a/engine/access/rest/models/model_network_parameters.go b/engine/access/rest/http/models/model_network_parameters.go similarity index 100% rename from engine/access/rest/models/model_network_parameters.go rename to engine/access/rest/http/models/model_network_parameters.go diff --git a/engine/access/rest/models/model_node_version_info.go b/engine/access/rest/http/models/model_node_version_info.go similarity index 100% rename from engine/access/rest/models/model_node_version_info.go rename to engine/access/rest/http/models/model_node_version_info.go diff --git a/engine/access/rest/models/model_one_of_block_height.go b/engine/access/rest/http/models/model_one_of_block_height.go similarity index 100% rename from engine/access/rest/models/model_one_of_block_height.go rename to engine/access/rest/http/models/model_one_of_block_height.go diff --git a/engine/access/rest/models/model_proposal_key.go b/engine/access/rest/http/models/model_proposal_key.go similarity index 100% rename from engine/access/rest/models/model_proposal_key.go rename to engine/access/rest/http/models/model_proposal_key.go diff --git a/engine/access/rest/models/model_scripts_body.go b/engine/access/rest/http/models/model_scripts_body.go similarity index 100% rename from engine/access/rest/models/model_scripts_body.go rename to engine/access/rest/http/models/model_scripts_body.go diff --git a/engine/access/rest/models/model_signing_algorithm.go b/engine/access/rest/http/models/model_signing_algorithm.go similarity index 100% rename from engine/access/rest/models/model_signing_algorithm.go rename to engine/access/rest/http/models/model_signing_algorithm.go diff --git a/engine/access/rest/models/model_transaction.go b/engine/access/rest/http/models/model_transaction.go similarity index 100% rename from engine/access/rest/models/model_transaction.go rename to engine/access/rest/http/models/model_transaction.go diff --git a/engine/access/rest/models/model_transaction__expandable.go b/engine/access/rest/http/models/model_transaction__expandable.go similarity index 100% rename from engine/access/rest/models/model_transaction__expandable.go rename to engine/access/rest/http/models/model_transaction__expandable.go diff --git a/engine/access/rest/models/model_transaction_execution.go b/engine/access/rest/http/models/model_transaction_execution.go similarity index 100% rename from engine/access/rest/models/model_transaction_execution.go rename to engine/access/rest/http/models/model_transaction_execution.go diff --git a/engine/access/rest/models/model_transaction_result.go b/engine/access/rest/http/models/model_transaction_result.go similarity index 100% rename from engine/access/rest/models/model_transaction_result.go rename to engine/access/rest/http/models/model_transaction_result.go diff --git a/engine/access/rest/models/model_transaction_result__expandable.go b/engine/access/rest/http/models/model_transaction_result__expandable.go similarity index 100% rename from engine/access/rest/models/model_transaction_result__expandable.go rename to engine/access/rest/http/models/model_transaction_result__expandable.go diff --git a/engine/access/rest/models/model_transaction_signature.go b/engine/access/rest/http/models/model_transaction_signature.go similarity index 100% rename from engine/access/rest/models/model_transaction_signature.go rename to engine/access/rest/http/models/model_transaction_signature.go diff --git a/engine/access/rest/models/model_transaction_status.go b/engine/access/rest/http/models/model_transaction_status.go similarity index 100% rename from engine/access/rest/models/model_transaction_status.go rename to engine/access/rest/http/models/model_transaction_status.go diff --git a/engine/access/rest/models/model_transactions_body.go b/engine/access/rest/http/models/model_transactions_body.go similarity index 100% rename from engine/access/rest/models/model_transactions_body.go rename to engine/access/rest/http/models/model_transactions_body.go diff --git a/engine/access/rest/models/network.go b/engine/access/rest/http/models/network.go similarity index 100% rename from engine/access/rest/models/network.go rename to engine/access/rest/http/models/network.go diff --git a/engine/access/rest/models/node_version_info.go b/engine/access/rest/http/models/node_version_info.go similarity index 100% rename from engine/access/rest/models/node_version_info.go rename to engine/access/rest/http/models/node_version_info.go diff --git a/engine/access/rest/models/transaction.go b/engine/access/rest/http/models/transaction.go similarity index 100% rename from engine/access/rest/models/transaction.go rename to engine/access/rest/http/models/transaction.go diff --git a/engine/access/rest/request/address.go b/engine/access/rest/http/request/address.go similarity index 100% rename from engine/access/rest/request/address.go rename to engine/access/rest/http/request/address.go diff --git a/engine/access/rest/request/address_test.go b/engine/access/rest/http/request/address_test.go similarity index 100% rename from engine/access/rest/request/address_test.go rename to engine/access/rest/http/request/address_test.go diff --git a/engine/access/rest/request/arguments.go b/engine/access/rest/http/request/arguments.go similarity index 100% rename from engine/access/rest/request/arguments.go rename to engine/access/rest/http/request/arguments.go diff --git a/engine/access/rest/request/arguments_test.go b/engine/access/rest/http/request/arguments_test.go similarity index 100% rename from engine/access/rest/request/arguments_test.go rename to engine/access/rest/http/request/arguments_test.go diff --git a/engine/access/rest/request/create_transaction.go b/engine/access/rest/http/request/create_transaction.go similarity index 60% rename from engine/access/rest/request/create_transaction.go rename to engine/access/rest/http/request/create_transaction.go index 3fa8bdafb94..951cede2c58 100644 --- a/engine/access/rest/request/create_transaction.go +++ b/engine/access/rest/http/request/create_transaction.go @@ -3,6 +3,7 @@ package request import ( "io" + "github.com/onflow/flow-go/engine/access/rest/common" "github.com/onflow/flow-go/model/flow" ) @@ -10,7 +11,13 @@ type CreateTransaction struct { Transaction flow.TransactionBody } -func (c *CreateTransaction) Build(r *Request) error { +func CreateTransactionRequest(r *common.Request) (CreateTransaction, error) { + var req CreateTransaction + err := req.Build(r) + return req, err +} + +func (c *CreateTransaction) Build(r *common.Request) error { return c.Parse(r.Body, r.Chain) } diff --git a/engine/access/rest/request/event_type.go b/engine/access/rest/http/request/event_type.go similarity index 100% rename from engine/access/rest/request/event_type.go rename to engine/access/rest/http/request/event_type.go diff --git a/engine/access/rest/request/get_account.go b/engine/access/rest/http/request/get_account.go similarity index 61% rename from engine/access/rest/request/get_account.go rename to engine/access/rest/http/request/get_account.go index 4d0a1b2bf54..8beba0cce58 100644 --- a/engine/access/rest/request/get_account.go +++ b/engine/access/rest/http/request/get_account.go @@ -1,6 +1,7 @@ package request import ( + "github.com/onflow/flow-go/engine/access/rest/common" "github.com/onflow/flow-go/model/flow" ) @@ -12,7 +13,17 @@ type GetAccount struct { Height uint64 } -func (g *GetAccount) Build(r *Request) error { +// GetAccountRequest extracts necessary variables and query parameters from the provided request, +// builds a GetAccount instance, and validates it. +// +// No errors are expected during normal operation. +func GetAccountRequest(r *common.Request) (GetAccount, error) { + var req GetAccount + err := req.Build(r) + return req, err +} + +func (g *GetAccount) Build(r *common.Request) error { return g.Parse( r.GetVar(addressVar), r.GetQueryParam(blockHeightQuery), diff --git a/engine/access/rest/request/get_account_balance.go b/engine/access/rest/http/request/get_account_balance.go similarity index 57% rename from engine/access/rest/request/get_account_balance.go rename to engine/access/rest/http/request/get_account_balance.go index 42347c3b102..8a5887e2e82 100644 --- a/engine/access/rest/request/get_account_balance.go +++ b/engine/access/rest/http/request/get_account_balance.go @@ -1,6 +1,7 @@ package request import ( + "github.com/onflow/flow-go/engine/access/rest/common" "github.com/onflow/flow-go/model/flow" ) @@ -9,7 +10,17 @@ type GetAccountBalance struct { Height uint64 } -func (g *GetAccountBalance) Build(r *Request) error { +// GetAccountBalanceRequest extracts necessary variables and query parameters from the provided request, +// builds a GetAccountBalance instance, and validates it. +// +// No errors are expected during normal operation. +func GetAccountBalanceRequest(r *common.Request) (GetAccountBalance, error) { + var req GetAccountBalance + err := req.Build(r) + return req, err +} + +func (g *GetAccountBalance) Build(r *common.Request) error { return g.Parse( r.GetVar(addressVar), r.GetQueryParam(blockHeightQuery), diff --git a/engine/access/rest/request/get_account_balance_test.go b/engine/access/rest/http/request/get_account_balance_test.go similarity index 100% rename from engine/access/rest/request/get_account_balance_test.go rename to engine/access/rest/http/request/get_account_balance_test.go diff --git a/engine/access/rest/request/get_account_key.go b/engine/access/rest/http/request/get_account_key.go similarity index 66% rename from engine/access/rest/request/get_account_key.go rename to engine/access/rest/http/request/get_account_key.go index 2d6466e19bc..91826671f51 100644 --- a/engine/access/rest/request/get_account_key.go +++ b/engine/access/rest/http/request/get_account_key.go @@ -3,6 +3,7 @@ package request import ( "fmt" + "github.com/onflow/flow-go/engine/access/rest/common" "github.com/onflow/flow-go/engine/access/rest/util" "github.com/onflow/flow-go/model/flow" ) @@ -15,7 +16,17 @@ type GetAccountKey struct { Height uint64 } -func (g *GetAccountKey) Build(r *Request) error { +// GetAccountKeyRequest extracts necessary variables and query parameters from the provided request, +// builds a GetAccountKey instance, and validates it. +// +// No errors are expected during normal operation. +func GetAccountKeyRequest(r *common.Request) (GetAccountKey, error) { + var req GetAccountKey + err := req.Build(r) + return req, err +} + +func (g *GetAccountKey) Build(r *common.Request) error { return g.Parse( r.GetVar(addressVar), r.GetVar(indexVar), diff --git a/engine/access/rest/request/get_account_key_test.go b/engine/access/rest/http/request/get_account_key_test.go similarity index 100% rename from engine/access/rest/request/get_account_key_test.go rename to engine/access/rest/http/request/get_account_key_test.go diff --git a/engine/access/rest/request/get_account_keys.go b/engine/access/rest/http/request/get_account_keys.go similarity index 58% rename from engine/access/rest/request/get_account_keys.go rename to engine/access/rest/http/request/get_account_keys.go index 6580135648f..689d9f3d765 100644 --- a/engine/access/rest/request/get_account_keys.go +++ b/engine/access/rest/http/request/get_account_keys.go @@ -1,6 +1,7 @@ package request import ( + "github.com/onflow/flow-go/engine/access/rest/common" "github.com/onflow/flow-go/model/flow" ) @@ -9,7 +10,17 @@ type GetAccountKeys struct { Height uint64 } -func (g *GetAccountKeys) Build(r *Request) error { +// GetAccountKeysRequest extracts necessary variables and query parameters from the provided request, +// builds a GetAccountKeys instance, and validates it. +// +// No errors are expected during normal operation. +func GetAccountKeysRequest(r *common.Request) (GetAccountKeys, error) { + var req GetAccountKeys + err := req.Build(r) + return req, err +} + +func (g *GetAccountKeys) Build(r *common.Request) error { return g.Parse( r.GetVar(addressVar), r.GetQueryParam(blockHeightQuery), diff --git a/engine/access/rest/request/get_account_keys_test.go b/engine/access/rest/http/request/get_account_keys_test.go similarity index 100% rename from engine/access/rest/request/get_account_keys_test.go rename to engine/access/rest/http/request/get_account_keys_test.go diff --git a/engine/access/rest/request/get_account_test.go b/engine/access/rest/http/request/get_account_test.go similarity index 100% rename from engine/access/rest/request/get_account_test.go rename to engine/access/rest/http/request/get_account_test.go diff --git a/engine/access/rest/request/get_block.go b/engine/access/rest/http/request/get_block.go similarity index 72% rename from engine/access/rest/request/get_block.go rename to engine/access/rest/http/request/get_block.go index 4a4a1cd319d..fd74b0e4be0 100644 --- a/engine/access/rest/request/get_block.go +++ b/engine/access/rest/http/request/get_block.go @@ -3,6 +3,7 @@ package request import ( "fmt" + "github.com/onflow/flow-go/engine/access/rest/common" "github.com/onflow/flow-go/model/flow" ) @@ -20,7 +21,17 @@ type GetBlock struct { SealedHeight bool } -func (g *GetBlock) Build(r *Request) error { +// GetBlockRequest extracts necessary query parameters from the provided request, +// builds a GetBlock instance, and validates it. +// +// No errors are expected during normal operation. +func GetBlockRequest(r *common.Request) (GetBlock, error) { + var req GetBlock + err := req.Build(r) + return req, err +} + +func (g *GetBlock) Build(r *common.Request) error { return g.Parse( r.GetQueryParams(heightQuery), r.GetQueryParam(startHeightQuery), @@ -94,7 +105,17 @@ type GetBlockByIDs struct { IDs []flow.Identifier } -func (g *GetBlockByIDs) Build(r *Request) error { +// GetBlockByIDsRequest extracts necessary variables from the provided request, +// builds a GetBlockByIDs instance, and validates it. +// +// No errors are expected during normal operation. +func GetBlockByIDsRequest(r *common.Request) (GetBlockByIDs, error) { + var req GetBlockByIDs + err := req.Build(r) + return req, err +} + +func (g *GetBlockByIDs) Build(r *common.Request) error { return g.Parse( r.GetVars(idParam), ) @@ -114,3 +135,13 @@ func (g *GetBlockByIDs) Parse(rawIds []string) error { type GetBlockPayload struct { GetByIDRequest } + +// GetBlockPayloadRequest extracts necessary variables from the provided request, +// builds a GetBlockPayload instance, and validates it. +// +// No errors are expected during normal operation. +func GetBlockPayloadRequest(r *common.Request) (GetBlockPayload, error) { + var req GetBlockPayload + err := req.Build(r) + return req, err +} diff --git a/engine/access/rest/request/get_block_test.go b/engine/access/rest/http/request/get_block_test.go similarity index 100% rename from engine/access/rest/request/get_block_test.go rename to engine/access/rest/http/request/get_block_test.go diff --git a/engine/access/rest/http/request/get_collection.go b/engine/access/rest/http/request/get_collection.go new file mode 100644 index 00000000000..f7af68a6dcf --- /dev/null +++ b/engine/access/rest/http/request/get_collection.go @@ -0,0 +1,29 @@ +package request + +import ( + "github.com/onflow/flow-go/engine/access/rest/common" +) + +const ExpandsTransactions = "transactions" + +type GetCollection struct { + GetByIDRequest + ExpandsTransactions bool +} + +// GetCollectionRequest extracts necessary variables from the provided request, +// builds a GetCollection instance, and validates it. +// +// No errors are expected during normal operation. +func GetCollectionRequest(r *common.Request) (GetCollection, error) { + var req GetCollection + err := req.Build(r) + return req, err +} + +func (g *GetCollection) Build(r *common.Request) error { + err := g.GetByIDRequest.Build(r) + g.ExpandsTransactions = r.Expands(ExpandsTransactions) + + return err +} diff --git a/engine/access/rest/request/get_events.go b/engine/access/rest/http/request/get_events.go similarity index 84% rename from engine/access/rest/request/get_events.go rename to engine/access/rest/http/request/get_events.go index b9adcbe0f19..39f2ba9faef 100644 --- a/engine/access/rest/request/get_events.go +++ b/engine/access/rest/http/request/get_events.go @@ -3,6 +3,7 @@ package request import ( "fmt" + "github.com/onflow/flow-go/engine/access/rest/common" "github.com/onflow/flow-go/model/flow" ) @@ -17,7 +18,17 @@ type GetEvents struct { BlockIDs []flow.Identifier } -func (g *GetEvents) Build(r *Request) error { +// GetEventsRequest extracts necessary variables from the provided request, +// builds a GetEvents instance, and validates it. +// +// No errors are expected during normal operation. +func GetEventsRequest(r *common.Request) (GetEvents, error) { + var req GetEvents + err := req.Build(r) + return req, err +} + +func (g *GetEvents) Build(r *common.Request) error { return g.Parse( r.GetQueryParam(eventTypeQuery), r.GetQueryParam(startHeightQuery), diff --git a/engine/access/rest/request/get_events_test.go b/engine/access/rest/http/request/get_events_test.go similarity index 100% rename from engine/access/rest/request/get_events_test.go rename to engine/access/rest/http/request/get_events_test.go diff --git a/engine/access/rest/http/request/get_execution_result.go b/engine/access/rest/http/request/get_execution_result.go new file mode 100644 index 00000000000..cdf216766c1 --- /dev/null +++ b/engine/access/rest/http/request/get_execution_result.go @@ -0,0 +1,59 @@ +package request + +import ( + "fmt" + + "github.com/onflow/flow-go/engine/access/rest/common" + "github.com/onflow/flow-go/model/flow" +) + +const idQuery = "id" + +type GetExecutionResultByBlockIDs struct { + BlockIDs []flow.Identifier +} + +// GetExecutionResultByBlockIDsRequest extracts necessary variables from the provided request, +// builds a GetExecutionResultByBlockIDs instance, and validates it. +// +// No errors are expected during normal operation. +func GetExecutionResultByBlockIDsRequest(r *common.Request) (GetExecutionResultByBlockIDs, error) { + var req GetExecutionResultByBlockIDs + err := req.Build(r) + return req, err +} + +func (g *GetExecutionResultByBlockIDs) Build(r *common.Request) error { + return g.Parse( + r.GetQueryParams(blockIDQuery), + ) +} + +func (g *GetExecutionResultByBlockIDs) Parse(rawIDs []string) error { + var ids IDs + err := ids.Parse(rawIDs) + if err != nil { + return err + } + g.BlockIDs = ids.Flow() + + if len(g.BlockIDs) == 0 { + return fmt.Errorf("no block IDs provided") + } + + return nil +} + +type GetExecutionResult struct { + GetByIDRequest +} + +// GetExecutionResultRequest extracts necessary variables from the provided request, +// builds a GetExecutionResult instance, and validates it. +// +// No errors are expected during normal operation. +func GetExecutionResultRequest(r *common.Request) (GetExecutionResult, error) { + var req GetExecutionResult + err := req.Build(r) + return req, err +} diff --git a/engine/access/rest/request/get_script.go b/engine/access/rest/http/request/get_script.go similarity index 70% rename from engine/access/rest/request/get_script.go rename to engine/access/rest/http/request/get_script.go index 3e4abc29be7..de8da72cac1 100644 --- a/engine/access/rest/request/get_script.go +++ b/engine/access/rest/http/request/get_script.go @@ -4,6 +4,7 @@ import ( "fmt" "io" + "github.com/onflow/flow-go/engine/access/rest/common" "github.com/onflow/flow-go/model/flow" ) @@ -15,7 +16,17 @@ type GetScript struct { Script Script } -func (g *GetScript) Build(r *Request) error { +// GetScriptRequest extracts necessary variables from the provided request, +// builds a GetScript instance, and validates it. +// +// No errors are expected during normal operation. +func GetScriptRequest(r *common.Request) (GetScript, error) { + var req GetScript + err := req.Build(r) + return req, err +} + +func (g *GetScript) Build(r *common.Request) error { return g.Parse( r.GetQueryParam(blockHeightQuery), r.GetQueryParam(blockIDQuery), diff --git a/engine/access/rest/request/get_script_test.go b/engine/access/rest/http/request/get_script_test.go similarity index 100% rename from engine/access/rest/request/get_script_test.go rename to engine/access/rest/http/request/get_script_test.go diff --git a/engine/access/rest/request/get_transaction.go b/engine/access/rest/http/request/get_transaction.go similarity index 50% rename from engine/access/rest/request/get_transaction.go rename to engine/access/rest/http/request/get_transaction.go index e2748f2ef14..359570cd71d 100644 --- a/engine/access/rest/request/get_transaction.go +++ b/engine/access/rest/http/request/get_transaction.go @@ -1,6 +1,9 @@ package request -import "github.com/onflow/flow-go/model/flow" +import ( + "github.com/onflow/flow-go/engine/access/rest/common" + "github.com/onflow/flow-go/model/flow" +) const resultExpandable = "result" const blockIDQueryParam = "block_id" @@ -11,7 +14,7 @@ type TransactionOptionals struct { CollectionID flow.Identifier } -func (t *TransactionOptionals) Parse(r *Request) error { +func (t *TransactionOptionals) Parse(r *common.Request) error { var blockId ID err := blockId.Parse(r.GetQueryParam(blockIDQueryParam)) if err != nil { @@ -35,7 +38,17 @@ type GetTransaction struct { ExpandsResult bool } -func (g *GetTransaction) Build(r *Request) error { +// GetTransactionRequest extracts necessary variables from the provided request, +// builds a GetTransaction instance, and validates it. +// +// No errors are expected during normal operation. +func GetTransactionRequest(r *common.Request) (GetTransaction, error) { + var req GetTransaction + err := req.Build(r) + return req, err +} + +func (g *GetTransaction) Build(r *common.Request) error { err := g.TransactionOptionals.Parse(r) if err != nil { return err @@ -52,7 +65,17 @@ type GetTransactionResult struct { TransactionOptionals } -func (g *GetTransactionResult) Build(r *Request) error { +// GetTransactionResultRequest extracts necessary variables from the provided request, +// builds a GetTransactionResult instance, and validates it. +// +// No errors are expected during normal operation. +func GetTransactionResultRequest(r *common.Request) (GetTransactionResult, error) { + var req GetTransactionResult + err := req.Build(r) + return req, err +} + +func (g *GetTransactionResult) Build(r *common.Request) error { err := g.TransactionOptionals.Parse(r) if err != nil { return err diff --git a/engine/access/rest/request/height.go b/engine/access/rest/http/request/height.go similarity index 100% rename from engine/access/rest/request/height.go rename to engine/access/rest/http/request/height.go diff --git a/engine/access/rest/request/height_test.go b/engine/access/rest/http/request/height_test.go similarity index 100% rename from engine/access/rest/request/height_test.go rename to engine/access/rest/http/request/height_test.go diff --git a/engine/access/rest/request/helpers.go b/engine/access/rest/http/request/helpers.go similarity index 93% rename from engine/access/rest/request/helpers.go rename to engine/access/rest/http/request/helpers.go index 33d5246c674..5591cc6df9b 100644 --- a/engine/access/rest/request/helpers.go +++ b/engine/access/rest/http/request/helpers.go @@ -7,6 +7,7 @@ import ( "io" "strings" + "github.com/onflow/flow-go/engine/access/rest/common" "github.com/onflow/flow-go/model/flow" ) @@ -52,7 +53,7 @@ type GetByIDRequest struct { ID flow.Identifier } -func (g *GetByIDRequest) Build(r *Request) error { +func (g *GetByIDRequest) Build(r *common.Request) error { return g.Parse( r.GetVar(idQuery), ) diff --git a/engine/access/rest/request/helpers_test.go b/engine/access/rest/http/request/helpers_test.go similarity index 100% rename from engine/access/rest/request/helpers_test.go rename to engine/access/rest/http/request/helpers_test.go diff --git a/engine/access/rest/request/id.go b/engine/access/rest/http/request/id.go similarity index 100% rename from engine/access/rest/request/id.go rename to engine/access/rest/http/request/id.go diff --git a/engine/access/rest/request/id_test.go b/engine/access/rest/http/request/id_test.go similarity index 100% rename from engine/access/rest/request/id_test.go rename to engine/access/rest/http/request/id_test.go diff --git a/engine/access/rest/request/proposal_key.go b/engine/access/rest/http/request/proposal_key.go similarity index 92% rename from engine/access/rest/request/proposal_key.go rename to engine/access/rest/http/request/proposal_key.go index 5d38fa27e99..1bc792022ac 100644 --- a/engine/access/rest/request/proposal_key.go +++ b/engine/access/rest/http/request/proposal_key.go @@ -3,7 +3,7 @@ package request import ( "fmt" - "github.com/onflow/flow-go/engine/access/rest/models" + "github.com/onflow/flow-go/engine/access/rest/http/models" "github.com/onflow/flow-go/engine/access/rest/util" "github.com/onflow/flow-go/model/flow" ) diff --git a/engine/access/rest/request/script.go b/engine/access/rest/http/request/script.go similarity index 100% rename from engine/access/rest/request/script.go rename to engine/access/rest/http/request/script.go diff --git a/engine/access/rest/request/script_test.go b/engine/access/rest/http/request/script_test.go similarity index 100% rename from engine/access/rest/request/script_test.go rename to engine/access/rest/http/request/script_test.go diff --git a/engine/access/rest/request/signature_test.go b/engine/access/rest/http/request/signature_test.go similarity index 97% rename from engine/access/rest/request/signature_test.go rename to engine/access/rest/http/request/signature_test.go index 1f1bc4e9c9c..a8219f34bbc 100644 --- a/engine/access/rest/request/signature_test.go +++ b/engine/access/rest/http/request/signature_test.go @@ -7,7 +7,7 @@ import ( "github.com/stretchr/testify/assert" - "github.com/onflow/flow-go/engine/access/rest/models" + "github.com/onflow/flow-go/engine/access/rest/http/models" "github.com/onflow/flow-go/engine/access/rest/util" "github.com/onflow/flow-go/model/flow" ) diff --git a/engine/access/rest/request/signatures.go b/engine/access/rest/http/request/signatures.go similarity index 96% rename from engine/access/rest/request/signatures.go rename to engine/access/rest/http/request/signatures.go index 7d4121a6a47..e333be3781b 100644 --- a/engine/access/rest/request/signatures.go +++ b/engine/access/rest/http/request/signatures.go @@ -3,7 +3,7 @@ package request import ( "fmt" - "github.com/onflow/flow-go/engine/access/rest/models" + "github.com/onflow/flow-go/engine/access/rest/http/models" "github.com/onflow/flow-go/engine/access/rest/util" "github.com/onflow/flow-go/model/flow" ) diff --git a/engine/access/rest/request/transaction.go b/engine/access/rest/http/request/transaction.go similarity index 98% rename from engine/access/rest/request/transaction.go rename to engine/access/rest/http/request/transaction.go index 8c15ab98389..614d78f1e07 100644 --- a/engine/access/rest/request/transaction.go +++ b/engine/access/rest/http/request/transaction.go @@ -4,7 +4,7 @@ import ( "fmt" "io" - "github.com/onflow/flow-go/engine/access/rest/models" + "github.com/onflow/flow-go/engine/access/rest/http/models" "github.com/onflow/flow-go/engine/access/rest/util" "github.com/onflow/flow-go/engine/common/rpc/convert" "github.com/onflow/flow-go/model/flow" diff --git a/engine/access/rest/request/transaction_test.go b/engine/access/rest/http/request/transaction_test.go similarity index 100% rename from engine/access/rest/request/transaction_test.go rename to engine/access/rest/http/request/transaction_test.go diff --git a/engine/access/rest/routes/account_balance.go b/engine/access/rest/http/routes/account_balance.go similarity index 64% rename from engine/access/rest/routes/account_balance.go rename to engine/access/rest/http/routes/account_balance.go index 82936beddda..3d0ebf50667 100644 --- a/engine/access/rest/routes/account_balance.go +++ b/engine/access/rest/http/routes/account_balance.go @@ -4,15 +4,16 @@ import ( "fmt" "github.com/onflow/flow-go/access" - "github.com/onflow/flow-go/engine/access/rest/models" - "github.com/onflow/flow-go/engine/access/rest/request" + "github.com/onflow/flow-go/engine/access/rest/common" + "github.com/onflow/flow-go/engine/access/rest/http/models" + "github.com/onflow/flow-go/engine/access/rest/http/request" ) // GetAccountBalance handler retrieves an account balance by address and block height and returns the response -func GetAccountBalance(r *request.Request, backend access.API, _ models.LinkGenerator) (interface{}, error) { - req, err := r.GetAccountBalanceRequest() +func GetAccountBalance(r *common.Request, backend access.API, _ models.LinkGenerator) (interface{}, error) { + req, err := request.GetAccountBalanceRequest(r) if err != nil { - return nil, models.NewBadRequestError(err) + return nil, common.NewBadRequestError(err) } // In case we receive special height values 'final' and 'sealed', @@ -23,7 +24,7 @@ func GetAccountBalance(r *request.Request, backend access.API, _ models.LinkGene header, _, err := backend.GetLatestBlockHeader(r.Context(), isSealed) if err != nil { err := fmt.Errorf("block with height: %d does not exist", req.Height) - return nil, models.NewNotFoundError(err.Error(), err) + return nil, common.NewNotFoundError(err.Error(), err) } req.Height = header.Height } @@ -31,7 +32,7 @@ func GetAccountBalance(r *request.Request, backend access.API, _ models.LinkGene balance, err := backend.GetAccountBalanceAtBlockHeight(r.Context(), req.Address, req.Height) if err != nil { err = fmt.Errorf("failed to get account balance, reason: %w", err) - return nil, models.NewNotFoundError(err.Error(), err) + return nil, common.NewNotFoundError(err.Error(), err) } var response models.AccountBalance diff --git a/engine/access/rest/routes/account_balance_test.go b/engine/access/rest/http/routes/account_balance_test.go similarity index 88% rename from engine/access/rest/routes/account_balance_test.go rename to engine/access/rest/http/routes/account_balance_test.go index 7b47fc76391..df541547aae 100644 --- a/engine/access/rest/routes/account_balance_test.go +++ b/engine/access/rest/http/routes/account_balance_test.go @@ -1,4 +1,4 @@ -package routes +package routes_test import ( "fmt" @@ -11,6 +11,7 @@ import ( "github.com/stretchr/testify/require" "github.com/onflow/flow-go/access/mock" + "github.com/onflow/flow-go/engine/access/rest/router" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/utils/unittest" ) @@ -30,7 +31,7 @@ func TestGetAccountBalance(t *testing.T) { var height uint64 = 100 block := unittest.BlockHeaderFixture(unittest.WithHeaderHeight(height)) - req := getAccountBalanceRequest(t, account, sealedHeightQueryParam) + req := getAccountBalanceRequest(t, account, router.SealedHeightQueryParam) backend.Mock. On("GetLatestBlockHeader", mocktestify.Anything, true). @@ -42,7 +43,7 @@ func TestGetAccountBalance(t *testing.T) { expected := expectedAccountBalanceResponse(account) - assertOKResponse(t, req, expected, backend) + router.AssertOKResponse(t, req, expected, backend) mocktestify.AssertExpectationsForObjects(t, backend) }) @@ -51,7 +52,7 @@ func TestGetAccountBalance(t *testing.T) { var height uint64 = 100 block := unittest.BlockHeaderFixture(unittest.WithHeaderHeight(height)) - req := getAccountBalanceRequest(t, account, finalHeightQueryParam) + req := getAccountBalanceRequest(t, account, router.FinalHeightQueryParam) backend.Mock. On("GetLatestBlockHeader", mocktestify.Anything, false). @@ -63,7 +64,7 @@ func TestGetAccountBalance(t *testing.T) { expected := expectedAccountBalanceResponse(account) - assertOKResponse(t, req, expected, backend) + router.AssertOKResponse(t, req, expected, backend) mocktestify.AssertExpectationsForObjects(t, backend) }) @@ -78,7 +79,7 @@ func TestGetAccountBalance(t *testing.T) { expected := expectedAccountBalanceResponse(account) - assertOKResponse(t, req, expected, backend) + router.AssertOKResponse(t, req, expected, backend) mocktestify.AssertExpectationsForObjects(t, backend) }) @@ -93,7 +94,7 @@ func TestGetAccountBalance(t *testing.T) { for i, test := range tests { req, _ := http.NewRequest("GET", test.url, nil) - rr := executeRequest(req, backend) + rr := router.ExecuteRequest(req, backend) assert.Equal(t, http.StatusBadRequest, rr.Code) assert.JSONEq(t, test.out, rr.Body.String(), fmt.Sprintf("test #%d failed: %v", i, test)) @@ -127,9 +128,9 @@ func getAccountBalanceRequest(t *testing.T, account *flow.Account, height string func expectedAccountBalanceResponse(account *flow.Account) string { return fmt.Sprintf(` - { - "balance":"%d" - }`, + { + "balance":"%d" + }`, account.Balance, ) } diff --git a/engine/access/rest/routes/account_keys.go b/engine/access/rest/http/routes/account_keys.go similarity index 68% rename from engine/access/rest/routes/account_keys.go rename to engine/access/rest/http/routes/account_keys.go index c77655aba68..fa4e429c20d 100644 --- a/engine/access/rest/routes/account_keys.go +++ b/engine/access/rest/http/routes/account_keys.go @@ -4,15 +4,17 @@ import ( "fmt" "github.com/onflow/flow-go/access" - "github.com/onflow/flow-go/engine/access/rest/models" - "github.com/onflow/flow-go/engine/access/rest/request" + "github.com/onflow/flow-go/engine/access/rest/common" + "github.com/onflow/flow-go/engine/access/rest/http/request" + + "github.com/onflow/flow-go/engine/access/rest/http/models" ) // GetAccountKeyByIndex handler retrieves an account key by address and index and returns the response -func GetAccountKeyByIndex(r *request.Request, backend access.API, _ models.LinkGenerator) (interface{}, error) { - req, err := r.GetAccountKeyRequest() +func GetAccountKeyByIndex(r *common.Request, backend access.API, _ models.LinkGenerator) (interface{}, error) { + req, err := request.GetAccountKeyRequest(r) if err != nil { - return nil, models.NewBadRequestError(err) + return nil, common.NewBadRequestError(err) } // In case we receive special height values 'final' and 'sealed', @@ -22,7 +24,7 @@ func GetAccountKeyByIndex(r *request.Request, backend access.API, _ models.LinkG header, _, err := backend.GetLatestBlockHeader(r.Context(), isSealed) if err != nil { err := fmt.Errorf("block with height: %d does not exist", req.Height) - return nil, models.NewNotFoundError(err.Error(), err) + return nil, common.NewNotFoundError(err.Error(), err) } req.Height = header.Height } @@ -30,7 +32,7 @@ func GetAccountKeyByIndex(r *request.Request, backend access.API, _ models.LinkG accountKey, err := backend.GetAccountKeyAtBlockHeight(r.Context(), req.Address, req.Index, req.Height) if err != nil { err = fmt.Errorf("failed to get account key with index: %d, reason: %w", req.Index, err) - return nil, models.NewNotFoundError(err.Error(), err) + return nil, common.NewNotFoundError(err.Error(), err) } var response models.AccountPublicKey @@ -39,10 +41,10 @@ func GetAccountKeyByIndex(r *request.Request, backend access.API, _ models.LinkG } // GetAccountKeys handler retrieves an account keys by address and returns the response -func GetAccountKeys(r *request.Request, backend access.API, _ models.LinkGenerator) (interface{}, error) { - req, err := r.GetAccountKeysRequest() +func GetAccountKeys(r *common.Request, backend access.API, _ models.LinkGenerator) (interface{}, error) { + req, err := request.GetAccountKeysRequest(r) if err != nil { - return nil, models.NewBadRequestError(err) + return nil, common.NewBadRequestError(err) } // In case we receive special height values 'final' and 'sealed', @@ -53,7 +55,7 @@ func GetAccountKeys(r *request.Request, backend access.API, _ models.LinkGenerat header, _, err := backend.GetLatestBlockHeader(r.Context(), isSealed) if err != nil { err := fmt.Errorf("block with height: %d does not exist", req.Height) - return nil, models.NewNotFoundError(err.Error(), err) + return nil, common.NewNotFoundError(err.Error(), err) } req.Height = header.Height } @@ -61,7 +63,7 @@ func GetAccountKeys(r *request.Request, backend access.API, _ models.LinkGenerat accountKeys, err := backend.GetAccountKeysAtBlockHeight(r.Context(), req.Address, req.Height) if err != nil { err = fmt.Errorf("failed to get account keys, reason: %w", err) - return nil, models.NewNotFoundError(err.Error(), err) + return nil, common.NewNotFoundError(err.Error(), err) } var response models.AccountPublicKeys diff --git a/engine/access/rest/routes/account_keys_test.go b/engine/access/rest/http/routes/account_keys_test.go similarity index 85% rename from engine/access/rest/routes/account_keys_test.go rename to engine/access/rest/http/routes/account_keys_test.go index ecffbf4f3ca..6ad11bc89fc 100644 --- a/engine/access/rest/routes/account_keys_test.go +++ b/engine/access/rest/http/routes/account_keys_test.go @@ -1,4 +1,4 @@ -package routes +package routes_test import ( "fmt" @@ -15,6 +15,7 @@ import ( "github.com/onflow/crypto/hash" "github.com/onflow/flow-go/access/mock" + "github.com/onflow/flow-go/engine/access/rest/router" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/utils/unittest" ) @@ -38,7 +39,7 @@ func TestGetAccountKeyByIndex(t *testing.T) { var keyIndex uint32 = 0 keyByIndex := findAccountKeyByIndex(account.Keys, keyIndex) - req := getAccountKeyByIndexRequest(t, account, "0", sealedHeightQueryParam) + req := getAccountKeyByIndexRequest(t, account, "0", router.SealedHeightQueryParam) backend.Mock. On("GetLatestBlockHeader", mocktestify.Anything, true). @@ -50,7 +51,7 @@ func TestGetAccountKeyByIndex(t *testing.T) { expected := expectedAccountKeyResponse(account) - assertOKResponse(t, req, expected, backend) + router.AssertOKResponse(t, req, expected, backend) mocktestify.AssertExpectationsForObjects(t, backend) }) @@ -61,7 +62,7 @@ func TestGetAccountKeyByIndex(t *testing.T) { var keyIndex uint32 = 0 keyByIndex := findAccountKeyByIndex(account.Keys, keyIndex) - req := getAccountKeyByIndexRequest(t, account, "0", finalHeightQueryParam) + req := getAccountKeyByIndexRequest(t, account, "0", router.FinalHeightQueryParam) backend.Mock. On("GetLatestBlockHeader", mocktestify.Anything, false). @@ -73,7 +74,7 @@ func TestGetAccountKeyByIndex(t *testing.T) { expected := expectedAccountKeyResponse(account) - assertOKResponse(t, req, expected, backend) + router.AssertOKResponse(t, req, expected, backend) mocktestify.AssertExpectationsForObjects(t, backend) }) @@ -83,7 +84,7 @@ func TestGetAccountKeyByIndex(t *testing.T) { index := "2" block := unittest.BlockHeaderFixture(unittest.WithHeaderHeight(height)) - req := getAccountKeyByIndexRequest(t, account, index, sealedHeightQueryParam) + req := getAccountKeyByIndexRequest(t, account, index, router.SealedHeightQueryParam) backend.Mock. On("GetLatestBlockHeader", mocktestify.Anything, true). @@ -97,13 +98,13 @@ func TestGetAccountKeyByIndex(t *testing.T) { statusCode := 404 expected := fmt.Sprintf(` - { - "code": %d, - "message": "failed to get account key with index: %s, reason: failed to get account key with index: %s" - } + { + "code": %d, + "message": "failed to get account key with index: %s, reason: failed to get account key with index: %s" + } `, statusCode, index, index) - assertResponse(t, req, statusCode, expected, backend) + router.AssertResponse(t, req, statusCode, expected, backend) mocktestify.AssertExpectationsForObjects(t, backend) }) @@ -113,7 +114,7 @@ func TestGetAccountKeyByIndex(t *testing.T) { index := "2" block := unittest.BlockHeaderFixture(unittest.WithHeaderHeight(height)) - req := getAccountKeyByIndexRequest(t, account, index, finalHeightQueryParam) + req := getAccountKeyByIndexRequest(t, account, index, router.FinalHeightQueryParam) backend.Mock. On("GetLatestBlockHeader", mocktestify.Anything, false). @@ -127,13 +128,13 @@ func TestGetAccountKeyByIndex(t *testing.T) { statusCode := 404 expected := fmt.Sprintf(` - { - "code": %d, - "message": "failed to get account key with index: %s, reason: failed to get account key with index: %s" - } + { + "code": %d, + "message": "failed to get account key with index: %s, reason: failed to get account key with index: %s" + } `, statusCode, index, index) - assertResponse(t, req, statusCode, expected, backend) + router.AssertResponse(t, req, statusCode, expected, backend) mocktestify.AssertExpectationsForObjects(t, backend) }) @@ -151,7 +152,7 @@ func TestGetAccountKeyByIndex(t *testing.T) { expected := expectedAccountKeyResponse(account) - assertOKResponse(t, req, expected, backend) + router.AssertOKResponse(t, req, expected, backend) mocktestify.AssertExpectationsForObjects(t, backend) }) @@ -160,7 +161,7 @@ func TestGetAccountKeyByIndex(t *testing.T) { account := accountFixture(t) const finalHeight uint64 = math.MaxUint64 - 2 - req := getAccountKeyByIndexRequest(t, account, "0", finalHeightQueryParam) + req := getAccountKeyByIndexRequest(t, account, "0", router.FinalHeightQueryParam) err := fmt.Errorf("block with height: %d does not exist", finalHeight) backend.Mock. @@ -175,7 +176,7 @@ func TestGetAccountKeyByIndex(t *testing.T) { } `, statusCode, finalHeight) - assertResponse(t, req, statusCode, expected, backend) + router.AssertResponse(t, req, statusCode, expected, backend) mocktestify.AssertExpectationsForObjects(t, backend) }) @@ -214,7 +215,7 @@ func TestGetAccountKeyByIndex(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { req, _ := http.NewRequest("GET", test.url, nil) - rr := executeRequest(req, backend) + rr := router.ExecuteRequest(req, backend) assert.Equal(t, http.StatusBadRequest, rr.Code) assert.JSONEq(t, test.out, rr.Body.String()) }) @@ -236,7 +237,7 @@ func TestGetAccountKeys(t *testing.T) { var height uint64 = 100 block := unittest.BlockHeaderFixture(unittest.WithHeaderHeight(height)) - req := getAccountKeysRequest(t, account, sealedHeightQueryParam) + req := getAccountKeysRequest(t, account, router.SealedHeightQueryParam) backend.Mock. On("GetLatestBlockHeader", mocktestify.Anything, true). @@ -248,7 +249,7 @@ func TestGetAccountKeys(t *testing.T) { expected := expectedAccountKeysResponse(account) - assertOKResponse(t, req, expected, backend) + router.AssertOKResponse(t, req, expected, backend) mocktestify.AssertExpectationsForObjects(t, backend) }) @@ -257,7 +258,7 @@ func TestGetAccountKeys(t *testing.T) { var height uint64 = 100 block := unittest.BlockHeaderFixture(unittest.WithHeaderHeight(height)) - req := getAccountKeysRequest(t, account, finalHeightQueryParam) + req := getAccountKeysRequest(t, account, router.FinalHeightQueryParam) backend.Mock. On("GetLatestBlockHeader", mocktestify.Anything, false). @@ -269,7 +270,7 @@ func TestGetAccountKeys(t *testing.T) { expected := expectedAccountKeysResponse(account) - assertOKResponse(t, req, expected, backend) + router.AssertOKResponse(t, req, expected, backend) mocktestify.AssertExpectationsForObjects(t, backend) }) @@ -284,7 +285,7 @@ func TestGetAccountKeys(t *testing.T) { expected := expectedAccountKeysResponse(account) - assertOKResponse(t, req, expected, backend) + router.AssertOKResponse(t, req, expected, backend) mocktestify.AssertExpectationsForObjects(t, backend) }) @@ -293,7 +294,7 @@ func TestGetAccountKeys(t *testing.T) { account := accountWithKeysFixture(t) const finalHeight uint64 = math.MaxUint64 - 2 - req := getAccountKeysRequest(t, account, finalHeightQueryParam) + req := getAccountKeysRequest(t, account, router.FinalHeightQueryParam) err := fmt.Errorf("block with height: %d does not exist", finalHeight) backend.Mock. @@ -308,7 +309,7 @@ func TestGetAccountKeys(t *testing.T) { } `, statusCode, finalHeight) - assertResponse(t, req, statusCode, expected, backend) + router.AssertResponse(t, req, statusCode, expected, backend) mocktestify.AssertExpectationsForObjects(t, backend) }) @@ -336,7 +337,7 @@ func TestGetAccountKeys(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { req, _ := http.NewRequest("GET", test.url, nil) - rr := executeRequest(req, backend) + rr := router.ExecuteRequest(req, backend) assert.Equal(t, http.StatusBadRequest, rr.Code) assert.JSONEq(t, test.out, rr.Body.String()) }) @@ -406,23 +407,23 @@ func getAccountKeysRequest( func expectedAccountKeyResponse(account *flow.Account) string { return fmt.Sprintf(` - { - "index":"0", - "public_key":"%s", - "signing_algorithm":"ECDSA_P256", - "hashing_algorithm":"SHA3_256", - "sequence_number":"0", - "weight":"1000", - "revoked":false - }`, + { + "index":"0", + "public_key":"%s", + "signing_algorithm":"ECDSA_P256", + "hashing_algorithm":"SHA3_256", + "sequence_number":"0", + "weight":"1000", + "revoked":false + }`, account.Keys[0].PublicKey.String(), ) } func expectedAccountKeysResponse(account *flow.Account) string { return fmt.Sprintf(` - { - "keys":[ + { + "keys":[ { "index":"0", "public_key":"%s", @@ -442,7 +443,7 @@ func expectedAccountKeysResponse(account *flow.Account) string { "revoked":false } ] - }`, + }`, account.Keys[0].PublicKey.String(), account.Keys[1].PublicKey.String(), ) diff --git a/engine/access/rest/routes/accounts.go b/engine/access/rest/http/routes/accounts.go similarity index 67% rename from engine/access/rest/routes/accounts.go rename to engine/access/rest/http/routes/accounts.go index 972c2ba68ac..a27eaa248c5 100644 --- a/engine/access/rest/routes/accounts.go +++ b/engine/access/rest/http/routes/accounts.go @@ -2,15 +2,16 @@ package routes import ( "github.com/onflow/flow-go/access" - "github.com/onflow/flow-go/engine/access/rest/models" - "github.com/onflow/flow-go/engine/access/rest/request" + "github.com/onflow/flow-go/engine/access/rest/common" + "github.com/onflow/flow-go/engine/access/rest/http/models" + "github.com/onflow/flow-go/engine/access/rest/http/request" ) // GetAccount handler retrieves account by address and returns the response -func GetAccount(r *request.Request, backend access.API, link models.LinkGenerator) (interface{}, error) { - req, err := r.GetAccountRequest() +func GetAccount(r *common.Request, backend access.API, link models.LinkGenerator) (interface{}, error) { + req, err := request.GetAccountRequest(r) if err != nil { - return nil, models.NewBadRequestError(err) + return nil, common.NewBadRequestError(err) } // in case we receive special height values 'final' and 'sealed', fetch that height and overwrite request with it diff --git a/engine/access/rest/routes/accounts_test.go b/engine/access/rest/http/routes/accounts_test.go similarity index 83% rename from engine/access/rest/routes/accounts_test.go rename to engine/access/rest/http/routes/accounts_test.go index feb9e77eeae..983f13c3448 100644 --- a/engine/access/rest/routes/accounts_test.go +++ b/engine/access/rest/http/routes/accounts_test.go @@ -1,4 +1,4 @@ -package routes +package routes_test import ( "fmt" @@ -12,7 +12,8 @@ import ( "github.com/stretchr/testify/require" "github.com/onflow/flow-go/access/mock" - "github.com/onflow/flow-go/engine/access/rest/middleware" + "github.com/onflow/flow-go/engine/access/rest/common/middleware" + "github.com/onflow/flow-go/engine/access/rest/router" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/utils/unittest" ) @@ -49,7 +50,7 @@ func TestAccessGetAccount(t *testing.T) { var height uint64 = 100 block := unittest.BlockHeaderFixture(unittest.WithHeaderHeight(height)) - req := getAccountRequest(t, account, sealedHeightQueryParam, expandableFieldKeys, expandableFieldContracts) + req := getAccountRequest(t, account, router.SealedHeightQueryParam, expandableFieldKeys, expandableFieldContracts) backend.Mock. On("GetLatestBlockHeader", mocktestify.Anything, true). @@ -61,7 +62,7 @@ func TestAccessGetAccount(t *testing.T) { expected := expectedExpandedResponse(account) - assertOKResponse(t, req, expected, backend) + router.AssertOKResponse(t, req, expected, backend) mocktestify.AssertExpectationsForObjects(t, backend) }) @@ -71,7 +72,7 @@ func TestAccessGetAccount(t *testing.T) { block := unittest.BlockHeaderFixture(unittest.WithHeaderHeight(height)) account := accountFixture(t) - req := getAccountRequest(t, account, finalHeightQueryParam, expandableFieldKeys, expandableFieldContracts) + req := getAccountRequest(t, account, router.FinalHeightQueryParam, expandableFieldKeys, expandableFieldContracts) backend.Mock. On("GetLatestBlockHeader", mocktestify.Anything, false). Return(block, flow.BlockStatusFinalized, nil) @@ -81,7 +82,7 @@ func TestAccessGetAccount(t *testing.T) { expected := expectedExpandedResponse(account) - assertOKResponse(t, req, expected, backend) + router.AssertOKResponse(t, req, expected, backend) mocktestify.AssertExpectationsForObjects(t, backend) }) @@ -96,7 +97,7 @@ func TestAccessGetAccount(t *testing.T) { expected := expectedExpandedResponse(account) - assertOKResponse(t, req, expected, backend) + router.AssertOKResponse(t, req, expected, backend) mocktestify.AssertExpectationsForObjects(t, backend) }) @@ -111,7 +112,7 @@ func TestAccessGetAccount(t *testing.T) { expected := expectedCondensedResponse(account) - assertOKResponse(t, req, expected, backend) + router.AssertOKResponse(t, req, expected, backend) mocktestify.AssertExpectationsForObjects(t, backend) }) @@ -126,7 +127,7 @@ func TestAccessGetAccount(t *testing.T) { for i, test := range tests { req, _ := http.NewRequest("GET", test.url, nil) - rr := executeRequest(req, backend) + rr := router.ExecuteRequest(req, backend) assert.Equal(t, http.StatusBadRequest, rr.Code) assert.JSONEq(t, test.out, rr.Body.String(), fmt.Sprintf("test #%d failed: %v", i, test)) @@ -149,9 +150,9 @@ func expectedExpandedResponse(account *flow.Account) string { "revoked":false } ], - "_links":{"_self":"/v1/accounts/%s" }, - "_expandable": {}, - "contracts": {"contract1":"Y29udHJhY3Qx", "contract2":"Y29udHJhY3Qy"} + "_links":{"_self":"/v1/accounts/%s" }, + "_expandable": {}, + "contracts": {"contract1":"Y29udHJhY3Qx", "contract2":"Y29udHJhY3Qy"} }`, account.Address, account.Keys[0].PublicKey.String(), account.Address) } @@ -159,8 +160,8 @@ func expectedCondensedResponse(account *flow.Account) string { return fmt.Sprintf(`{ "address":"%s", "balance":"100", - "_links":{"_self":"/v1/accounts/%s" }, - "_expandable":{"contracts":"contracts", "keys":"keys"} + "_links":{"_self":"/v1/accounts/%s" }, + "_expandable":{"contracts":"contracts", "keys":"keys"} }`, account.Address, account.Address) } diff --git a/engine/access/rest/routes/blocks.go b/engine/access/rest/http/routes/blocks.go similarity index 81% rename from engine/access/rest/routes/blocks.go rename to engine/access/rest/http/routes/blocks.go index c26f14dd8bf..f3995d2a5a5 100644 --- a/engine/access/rest/routes/blocks.go +++ b/engine/access/rest/http/routes/blocks.go @@ -9,16 +9,17 @@ import ( "google.golang.org/grpc/status" "github.com/onflow/flow-go/access" - "github.com/onflow/flow-go/engine/access/rest/models" - "github.com/onflow/flow-go/engine/access/rest/request" + "github.com/onflow/flow-go/engine/access/rest/common" + "github.com/onflow/flow-go/engine/access/rest/http/models" + "github.com/onflow/flow-go/engine/access/rest/http/request" "github.com/onflow/flow-go/model/flow" ) // GetBlocksByIDs gets blocks by provided ID or list of IDs. -func GetBlocksByIDs(r *request.Request, backend access.API, link models.LinkGenerator) (interface{}, error) { - req, err := r.GetBlockByIDsRequest() +func GetBlocksByIDs(r *common.Request, backend access.API, link models.LinkGenerator) (interface{}, error) { + req, err := request.GetBlockByIDsRequest(r) if err != nil { - return nil, models.NewBadRequestError(err) + return nil, common.NewBadRequestError(err) } blocks := make([]*models.Block, len(req.IDs)) @@ -35,10 +36,10 @@ func GetBlocksByIDs(r *request.Request, backend access.API, link models.LinkGene } // GetBlocksByHeight gets blocks by height. -func GetBlocksByHeight(r *request.Request, backend access.API, link models.LinkGenerator) (interface{}, error) { - req, err := r.GetBlockRequest() +func GetBlocksByHeight(r *common.Request, backend access.API, link models.LinkGenerator) (interface{}, error) { + req, err := request.GetBlockRequest(r) if err != nil { - return nil, models.NewBadRequestError(err) + return nil, common.NewBadRequestError(err) } if req.FinalHeight || req.SealedHeight { @@ -74,7 +75,7 @@ func GetBlocksByHeight(r *request.Request, backend access.API, link models.LinkG req.EndHeight = latest.Header.Height // overwrite special value height with fetched if req.StartHeight > req.EndHeight { - return nil, models.NewBadRequestError(fmt.Errorf("start height must be less than or equal to end height")) + return nil, common.NewBadRequestError(fmt.Errorf("start height must be less than or equal to end height")) } } @@ -92,10 +93,10 @@ func GetBlocksByHeight(r *request.Request, backend access.API, link models.LinkG } // GetBlockPayloadByID gets block payload by ID -func GetBlockPayloadByID(r *request.Request, backend access.API, _ models.LinkGenerator) (interface{}, error) { - req, err := r.GetBlockPayloadRequest() +func GetBlockPayloadByID(r *common.Request, backend access.API, _ models.LinkGenerator) (interface{}, error) { + req, err := request.GetBlockPayloadRequest(r) if err != nil { - return nil, models.NewBadRequestError(err) + return nil, common.NewBadRequestError(err) } blkProvider := NewBlockProvider(backend, forID(&req.ID)) @@ -113,7 +114,7 @@ func GetBlockPayloadByID(r *request.Request, backend access.API, _ models.LinkGe return payload, nil } -func getBlock(option blockProviderOption, req *request.Request, backend access.API, link models.LinkGenerator) (*models.Block, error) { +func getBlock(option blockProviderOption, req *common.Request, backend access.API, link models.LinkGenerator) (*models.Block, error) { // lookup block blkProvider := NewBlockProvider(backend, option) blk, blockStatus, err := blkProvider.getBlock(req.Context()) @@ -196,7 +197,7 @@ func (blkProvider *blockProvider) getBlock(ctx context.Context) (*flow.Block, fl if blkProvider.id != nil { blk, _, err := blkProvider.backend.GetBlockByID(ctx, *blkProvider.id) if err != nil { // unfortunately backend returns internal error status if not found - return nil, flow.BlockStatusUnknown, models.NewNotFoundError( + return nil, flow.BlockStatusUnknown, common.NewNotFoundError( fmt.Sprintf("error looking up block with ID %s", blkProvider.id.String()), err, ) } @@ -207,14 +208,14 @@ func (blkProvider *blockProvider) getBlock(ctx context.Context) (*flow.Block, fl blk, status, err := blkProvider.backend.GetLatestBlock(ctx, blkProvider.sealed) if err != nil { // cannot be a 'not found' error since final and sealed block should always be found - return nil, flow.BlockStatusUnknown, models.NewRestError(http.StatusInternalServerError, "block lookup failed", err) + return nil, flow.BlockStatusUnknown, common.NewRestError(http.StatusInternalServerError, "block lookup failed", err) } return blk, status, nil } blk, status, err := blkProvider.backend.GetBlockByHeight(ctx, blkProvider.height) if err != nil { // unfortunately backend returns internal error status if not found - return nil, flow.BlockStatusUnknown, models.NewNotFoundError( + return nil, flow.BlockStatusUnknown, common.NewNotFoundError( fmt.Sprintf("error looking up block at height %d", blkProvider.height), err, ) } diff --git a/engine/access/rest/routes/blocks_test.go b/engine/access/rest/http/routes/blocks_test.go similarity index 96% rename from engine/access/rest/routes/blocks_test.go rename to engine/access/rest/http/routes/blocks_test.go index 94962e87bb6..44ff77584e6 100644 --- a/engine/access/rest/routes/blocks_test.go +++ b/engine/access/rest/http/routes/blocks_test.go @@ -1,4 +1,4 @@ -package routes +package routes_test import ( "fmt" @@ -8,16 +8,16 @@ import ( "testing" "time" - "github.com/onflow/flow-go/engine/access/rest/request" - "github.com/onflow/flow-go/engine/access/rest/util" - mocks "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "github.com/onflow/flow-go/access/mock" - "github.com/onflow/flow-go/engine/access/rest/middleware" + "github.com/onflow/flow-go/engine/access/rest/common/middleware" + "github.com/onflow/flow-go/engine/access/rest/http/request" + "github.com/onflow/flow-go/engine/access/rest/router" + "github.com/onflow/flow-go/engine/access/rest/util" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/utils/unittest" ) @@ -169,7 +169,7 @@ func TestAccessGetBlocks(t *testing.T) { testVectors := prepareTestVectors(t, blockIDs, heights, blocks, executionResults, blkCnt) for _, tv := range testVectors { - rr := executeRequest(tv.request, backend) + rr := router.ExecuteRequest(tv.request, backend) require.Equal(t, tv.expectedStatus, rr.Code, "failed test %s: incorrect response code", tv.description) actualResp := rr.Body.String() require.JSONEq(t, tv.expectedResponse, actualResp, "Failed: %s: incorrect response body", tv.description) @@ -185,13 +185,13 @@ func requestURL(t *testing.T, ids []string, start string, end string, expandResp } if start != "" { - q.Add(startHeightQueryParam, start) - q.Add(endHeightQueryParam, end) + q.Add(router.StartHeightQueryParam, start) + q.Add(router.EndHeightQueryParam, end) } if len(heights) > 0 { heightsStr := strings.Join(heights, ",") - q.Add(heightQueryParam, heightsStr) + q.Add(router.HeightQueryParam, heightsStr) } if len(selectedFields) > 0 { @@ -201,8 +201,8 @@ func requestURL(t *testing.T, ids []string, start string, end string, expandResp if expandResponse { var expands []string - expands = append(expands, ExpandableFieldPayload) - expands = append(expands, ExpandableExecutionResult) + expands = append(expands, router.ExpandableFieldPayload) + expands = append(expands, router.ExpandableExecutionResult) expandsStr := strings.Join(expands, ",") q.Add(middleware.ExpandQueryParam, expandsStr) } diff --git a/engine/access/rest/routes/collections.go b/engine/access/rest/http/routes/collections.go similarity index 68% rename from engine/access/rest/routes/collections.go rename to engine/access/rest/http/routes/collections.go index 47b6150f480..84157cd68ae 100644 --- a/engine/access/rest/routes/collections.go +++ b/engine/access/rest/http/routes/collections.go @@ -2,16 +2,17 @@ package routes import ( "github.com/onflow/flow-go/access" - "github.com/onflow/flow-go/engine/access/rest/models" - "github.com/onflow/flow-go/engine/access/rest/request" + "github.com/onflow/flow-go/engine/access/rest/common" + "github.com/onflow/flow-go/engine/access/rest/http/models" + "github.com/onflow/flow-go/engine/access/rest/http/request" "github.com/onflow/flow-go/model/flow" ) // GetCollectionByID retrieves a collection by ID and builds a response -func GetCollectionByID(r *request.Request, backend access.API, link models.LinkGenerator) (interface{}, error) { - req, err := r.GetCollectionRequest() +func GetCollectionByID(r *common.Request, backend access.API, link models.LinkGenerator) (interface{}, error) { + req, err := request.GetCollectionRequest(r) if err != nil { - return nil, models.NewBadRequestError(err) + return nil, common.NewBadRequestError(err) } collection, err := backend.GetCollectionByID(r.Context(), req.ID) diff --git a/engine/access/rest/routes/collections_test.go b/engine/access/rest/http/routes/collections_test.go similarity index 91% rename from engine/access/rest/routes/collections_test.go rename to engine/access/rest/http/routes/collections_test.go index d0c5684f4ed..ad083eba1d6 100644 --- a/engine/access/rest/routes/collections_test.go +++ b/engine/access/rest/http/routes/collections_test.go @@ -1,4 +1,4 @@ -package routes +package routes_test import ( "encoding/json" @@ -7,15 +7,14 @@ import ( "strings" "testing" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - - "github.com/onflow/flow-go/model/flow" - "github.com/stretchr/testify/assert" mocks "github.com/stretchr/testify/mock" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" "github.com/onflow/flow-go/access/mock" + "github.com/onflow/flow-go/engine/access/rest/router" + "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/utils/unittest" ) @@ -52,17 +51,17 @@ func TestGetCollections(t *testing.T) { transactionsStr := fmt.Sprintf("[%s]", strings.Join(txs, ",")) expected := fmt.Sprintf(`{ - "id":"%s", + "id":"%s", "_links": { "_self": "/v1/collections/%s" }, - "_expandable": { - "transactions": %s - } + "_expandable": { + "transactions": %s + } }`, col.ID(), col.ID(), transactionsStr) req := getCollectionReq(col.ID().String(), false) - assertOKResponse(t, req, expected, backend) + router.AssertOKResponse(t, req, expected, backend) mocks.AssertExpectationsForObjects(t, backend) } }) @@ -87,7 +86,7 @@ func TestGetCollections(t *testing.T) { Once() req := getCollectionReq(col.ID().String(), true) - rr := executeRequest(req, backend) + rr := router.ExecuteRequest(req, backend) assert.Equal(t, http.StatusOK, rr.Code) // really hacky but we can't build whole response since it's really complex @@ -145,7 +144,7 @@ func TestGetCollections(t *testing.T) { Return(test.mockValue, test.mockErr) } req := getCollectionReq(test.id, false) - assertResponse(t, req, test.status, test.response, backend) + router.AssertResponse(t, req, test.status, test.response, backend) } }) } diff --git a/engine/access/rest/routes/events.go b/engine/access/rest/http/routes/events.go similarity index 78% rename from engine/access/rest/routes/events.go rename to engine/access/rest/http/routes/events.go index 59e04439bf8..038a4a98aeb 100644 --- a/engine/access/rest/routes/events.go +++ b/engine/access/rest/http/routes/events.go @@ -6,18 +6,20 @@ import ( entitiesproto "github.com/onflow/flow/protobuf/go/flow/entities" "github.com/onflow/flow-go/access" - "github.com/onflow/flow-go/engine/access/rest/models" - "github.com/onflow/flow-go/engine/access/rest/request" + "github.com/onflow/flow-go/engine/access/rest/common" + + "github.com/onflow/flow-go/engine/access/rest/http/models" + "github.com/onflow/flow-go/engine/access/rest/http/request" ) const BlockQueryParam = "block_ids" const EventTypeQuery = "type" // GetEvents for the provided block range or list of block IDs filtered by type. -func GetEvents(r *request.Request, backend access.API, _ models.LinkGenerator) (interface{}, error) { - req, err := r.GetEventsRequest() +func GetEvents(r *common.Request, backend access.API, _ models.LinkGenerator) (interface{}, error) { + req, err := request.GetEventsRequest(r) if err != nil { - return nil, models.NewBadRequestError(err) + return nil, common.NewBadRequestError(err) } // if the request has block IDs provided then return events for block IDs @@ -47,7 +49,7 @@ func GetEvents(r *request.Request, backend access.API, _ models.LinkGenerator) ( req.EndHeight = latest.Height // special check after we resolve special height value if req.StartHeight > req.EndHeight { - return nil, models.NewBadRequestError(fmt.Errorf("current retrieved end height value is lower than start height")) + return nil, common.NewBadRequestError(fmt.Errorf("current retrieved end height value is lower than start height")) } } diff --git a/engine/access/rest/routes/events_test.go b/engine/access/rest/http/routes/events_test.go similarity index 95% rename from engine/access/rest/routes/events_test.go rename to engine/access/rest/http/routes/events_test.go index 93b58afda6d..dcaa1e01268 100644 --- a/engine/access/rest/routes/events_test.go +++ b/engine/access/rest/http/routes/events_test.go @@ -1,4 +1,4 @@ -package routes +package routes_test import ( "encoding/json" @@ -11,16 +11,17 @@ import ( mocks "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "google.golang.org/grpc/codes" "google.golang.org/grpc/status" - "github.com/onflow/flow/protobuf/go/flow/entities" - "github.com/onflow/flow-go/access/mock" + "github.com/onflow/flow-go/engine/access/rest/http/routes" + "github.com/onflow/flow-go/engine/access/rest/router" "github.com/onflow/flow-go/engine/access/rest/util" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/utils/unittest" + + "github.com/onflow/flow/protobuf/go/flow/entities" ) func TestGetEvents(t *testing.T) { @@ -127,7 +128,7 @@ func TestGetEvents(t *testing.T) { for _, test := range testVectors { t.Run(test.description, func(t *testing.T) { - assertResponse(t, test.request, test.expectedStatus, test.expectedResponse, backend) + router.AssertResponse(t, test.request, test.expectedStatus, test.expectedResponse, backend) }) } @@ -138,15 +139,15 @@ func getEventReq(t *testing.T, eventType string, start string, end string, block q := u.Query() if len(blockIDs) > 0 { - q.Add(BlockQueryParam, strings.Join(blockIDs, ",")) + q.Add(routes.BlockQueryParam, strings.Join(blockIDs, ",")) } if start != "" && end != "" { - q.Add(startHeightQueryParam, start) - q.Add(endHeightQueryParam, end) + q.Add(router.StartHeightQueryParam, start) + q.Add(router.EndHeightQueryParam, end) } - q.Add(EventTypeQuery, eventType) + q.Add(routes.EventTypeQuery, eventType) u.RawQuery = q.Encode() diff --git a/engine/access/rest/routes/execution_result.go b/engine/access/rest/http/routes/execution_result.go similarity index 59% rename from engine/access/rest/routes/execution_result.go rename to engine/access/rest/http/routes/execution_result.go index b999665b26b..b1c32ac4912 100644 --- a/engine/access/rest/routes/execution_result.go +++ b/engine/access/rest/http/routes/execution_result.go @@ -4,15 +4,17 @@ import ( "fmt" "github.com/onflow/flow-go/access" - "github.com/onflow/flow-go/engine/access/rest/models" - "github.com/onflow/flow-go/engine/access/rest/request" + "github.com/onflow/flow-go/engine/access/rest/common" + "github.com/onflow/flow-go/engine/access/rest/http/request" + + "github.com/onflow/flow-go/engine/access/rest/http/models" ) // GetExecutionResultsByBlockIDs gets Execution Result payload by block IDs. -func GetExecutionResultsByBlockIDs(r *request.Request, backend access.API, link models.LinkGenerator) (interface{}, error) { - req, err := r.GetExecutionResultByBlockIDsRequest() +func GetExecutionResultsByBlockIDs(r *common.Request, backend access.API, link models.LinkGenerator) (interface{}, error) { + req, err := request.GetExecutionResultByBlockIDsRequest(r) if err != nil { - return nil, models.NewBadRequestError(err) + return nil, common.NewBadRequestError(err) } // for each block ID we retrieve execution result @@ -35,10 +37,10 @@ func GetExecutionResultsByBlockIDs(r *request.Request, backend access.API, link } // GetExecutionResultByID gets execution result by the ID. -func GetExecutionResultByID(r *request.Request, backend access.API, link models.LinkGenerator) (interface{}, error) { - req, err := r.GetExecutionResultRequest() +func GetExecutionResultByID(r *common.Request, backend access.API, link models.LinkGenerator) (interface{}, error) { + req, err := request.GetExecutionResultRequest(r) if err != nil { - return nil, models.NewBadRequestError(err) + return nil, common.NewBadRequestError(err) } res, err := backend.GetExecutionResultByID(r.Context(), req.ID) @@ -48,7 +50,7 @@ func GetExecutionResultByID(r *request.Request, backend access.API, link models. if res == nil { err := fmt.Errorf("execution result with ID: %s not found", req.ID.String()) - return nil, models.NewNotFoundError(err.Error(), err) + return nil, common.NewNotFoundError(err.Error(), err) } var response models.ExecutionResult diff --git a/engine/access/rest/routes/execution_result_test.go b/engine/access/rest/http/routes/execution_result_test.go similarity index 87% rename from engine/access/rest/routes/execution_result_test.go rename to engine/access/rest/http/routes/execution_result_test.go index ba74974af1a..f38d5a6c78f 100644 --- a/engine/access/rest/routes/execution_result_test.go +++ b/engine/access/rest/http/routes/execution_result_test.go @@ -1,4 +1,4 @@ -package routes +package routes_test import ( "fmt" @@ -7,12 +7,12 @@ import ( "strings" "testing" + mocks "github.com/stretchr/testify/mock" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" - mocks "github.com/stretchr/testify/mock" - "github.com/onflow/flow-go/access/mock" + "github.com/onflow/flow-go/engine/access/rest/router" "github.com/onflow/flow-go/engine/access/rest/util" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/utils/unittest" @@ -48,7 +48,7 @@ func TestGetResultByID(t *testing.T) { req := getResultByIDReq(id.String(), nil) expected := executionResultExpectedStr(result) - assertOKResponse(t, req, expected, backend) + router.AssertOKResponse(t, req, expected, backend) mocks.AssertExpectationsForObjects(t, backend) }) @@ -61,7 +61,7 @@ func TestGetResultByID(t *testing.T) { Once() req := getResultByIDReq(id.String(), nil) - assertResponse(t, req, http.StatusNotFound, `{"code":404,"message":"Flow resource not found: block not found"}`, backend) + router.AssertResponse(t, req, http.StatusNotFound, `{"code":404,"message":"Flow resource not found: block not found"}`, backend) mocks.AssertExpectationsForObjects(t, backend) }) } @@ -81,7 +81,7 @@ func TestGetResultBlockID(t *testing.T) { req := getResultByIDReq("", []string{blockID.String()}) expected := fmt.Sprintf(`[%s]`, executionResultExpectedStr(result)) - assertOKResponse(t, req, expected, backend) + router.AssertOKResponse(t, req, expected, backend) mocks.AssertExpectationsForObjects(t, backend) }) @@ -94,7 +94,7 @@ func TestGetResultBlockID(t *testing.T) { Once() req := getResultByIDReq("", []string{blockID.String()}) - assertResponse(t, req, http.StatusNotFound, `{"code":404,"message":"Flow resource not found: block not found"}`, backend) + router.AssertResponse(t, req, http.StatusNotFound, `{"code":404,"message":"Flow resource not found: block not found"}`, backend) mocks.AssertExpectationsForObjects(t, backend) }) } @@ -119,8 +119,8 @@ func executionResultExpectedStr(result *flow.ExecutionResult) string { "id": "%s", "block_id": "%s", "events": [], - "chunks": %s, - "previous_result_id": "%s", + "chunks": %s, + "previous_result_id": "%s", "_links": { "_self": "/v1/execution_results/%s" } diff --git a/engine/access/rest/routes/network.go b/engine/access/rest/http/routes/network.go similarity index 55% rename from engine/access/rest/routes/network.go rename to engine/access/rest/http/routes/network.go index 82abcbb6d49..c363a38a7a6 100644 --- a/engine/access/rest/routes/network.go +++ b/engine/access/rest/http/routes/network.go @@ -2,12 +2,12 @@ package routes import ( "github.com/onflow/flow-go/access" - "github.com/onflow/flow-go/engine/access/rest/models" - "github.com/onflow/flow-go/engine/access/rest/request" + "github.com/onflow/flow-go/engine/access/rest/common" + "github.com/onflow/flow-go/engine/access/rest/http/models" ) // GetNetworkParameters returns network-wide parameters of the blockchain -func GetNetworkParameters(r *request.Request, backend access.API, _ models.LinkGenerator) (interface{}, error) { +func GetNetworkParameters(r *common.Request, backend access.API, _ models.LinkGenerator) (interface{}, error) { params := backend.GetNetworkParameters(r.Context()) var response models.NetworkParameters diff --git a/engine/access/rest/routes/network_test.go b/engine/access/rest/http/routes/network_test.go similarity index 89% rename from engine/access/rest/routes/network_test.go rename to engine/access/rest/http/routes/network_test.go index 00d0ca03944..effc6891356 100644 --- a/engine/access/rest/routes/network_test.go +++ b/engine/access/rest/http/routes/network_test.go @@ -1,4 +1,4 @@ -package routes +package routes_test import ( "fmt" @@ -11,6 +11,7 @@ import ( "github.com/onflow/flow-go/access" "github.com/onflow/flow-go/access/mock" + "github.com/onflow/flow-go/engine/access/rest/router" "github.com/onflow/flow-go/model/flow" ) @@ -38,7 +39,7 @@ func TestGetNetworkParameters(t *testing.T) { expected := networkParametersExpectedStr(flow.Mainnet) - assertOKResponse(t, req, expected, backend) + router.AssertOKResponse(t, req, expected, backend) mocktestify.AssertExpectationsForObjects(t, backend) }) } diff --git a/engine/access/rest/routes/node_version_info.go b/engine/access/rest/http/routes/node_version_info.go similarity index 57% rename from engine/access/rest/routes/node_version_info.go rename to engine/access/rest/http/routes/node_version_info.go index 31e172bba9f..71b4aa08b43 100644 --- a/engine/access/rest/routes/node_version_info.go +++ b/engine/access/rest/http/routes/node_version_info.go @@ -2,12 +2,12 @@ package routes import ( "github.com/onflow/flow-go/access" - "github.com/onflow/flow-go/engine/access/rest/models" - "github.com/onflow/flow-go/engine/access/rest/request" + "github.com/onflow/flow-go/engine/access/rest/common" + "github.com/onflow/flow-go/engine/access/rest/http/models" ) // GetNodeVersionInfo returns node version information -func GetNodeVersionInfo(r *request.Request, backend access.API, _ models.LinkGenerator) (interface{}, error) { +func GetNodeVersionInfo(r *common.Request, backend access.API, _ models.LinkGenerator) (interface{}, error) { params, err := backend.GetNodeVersionInfo(r.Context()) if err != nil { return nil, err diff --git a/engine/access/rest/routes/node_version_info_test.go b/engine/access/rest/http/routes/node_version_info_test.go similarity index 88% rename from engine/access/rest/routes/node_version_info_test.go rename to engine/access/rest/http/routes/node_version_info_test.go index f08ada0289a..efbbc9f0c20 100644 --- a/engine/access/rest/routes/node_version_info_test.go +++ b/engine/access/rest/http/routes/node_version_info_test.go @@ -1,4 +1,4 @@ -package routes +package routes_test import ( "fmt" @@ -12,6 +12,7 @@ import ( "github.com/onflow/flow-go/access" "github.com/onflow/flow-go/access/mock" "github.com/onflow/flow-go/cmd/build" + "github.com/onflow/flow-go/engine/access/rest/router" "github.com/onflow/flow-go/utils/unittest" ) @@ -49,7 +50,7 @@ func TestGetNodeVersionInfo(t *testing.T) { expected := nodeVersionInfoExpectedStr(params) - assertOKResponse(t, req, expected, backend) + router.AssertOKResponse(t, req, expected, backend) mocktestify.AssertExpectationsForObjects(t, backend) }) } @@ -67,10 +68,10 @@ func nodeVersionInfoExpectedStr(nodeVersionInfo *access.NodeVersionInfo) string "semver": "%s", "commit": "%s", "spork_id": "%s", - "protocol_version": "%d", - "spork_root_block_height": "%d", - "node_root_block_height": "%d", - %s + "protocol_version": "%d", + "spork_root_block_height": "%d", + "node_root_block_height": "%d", + %s }`, nodeVersionInfo.Semver, nodeVersionInfo.Commit, diff --git a/engine/access/rest/routes/scripts.go b/engine/access/rest/http/routes/scripts.go similarity index 70% rename from engine/access/rest/routes/scripts.go rename to engine/access/rest/http/routes/scripts.go index 8627470ab88..2c747ff8850 100644 --- a/engine/access/rest/routes/scripts.go +++ b/engine/access/rest/http/routes/scripts.go @@ -2,16 +2,17 @@ package routes import ( "github.com/onflow/flow-go/access" - "github.com/onflow/flow-go/engine/access/rest/models" - "github.com/onflow/flow-go/engine/access/rest/request" + "github.com/onflow/flow-go/engine/access/rest/common" + "github.com/onflow/flow-go/engine/access/rest/http/models" + "github.com/onflow/flow-go/engine/access/rest/http/request" "github.com/onflow/flow-go/model/flow" ) // ExecuteScript handler sends the script from the request to be executed. -func ExecuteScript(r *request.Request, backend access.API, _ models.LinkGenerator) (interface{}, error) { - req, err := r.GetScriptRequest() +func ExecuteScript(r *common.Request, backend access.API, _ models.LinkGenerator) (interface{}, error) { + req, err := request.GetScriptRequest(r) if err != nil { - return nil, models.NewBadRequestError(err) + return nil, common.NewBadRequestError(err) } if req.BlockID != flow.ZeroID { diff --git a/engine/access/rest/routes/scripts_test.go b/engine/access/rest/http/routes/scripts_test.go similarity index 90% rename from engine/access/rest/routes/scripts_test.go rename to engine/access/rest/http/routes/scripts_test.go index 8998e5c4147..8e08c5d21a6 100644 --- a/engine/access/rest/routes/scripts_test.go +++ b/engine/access/rest/http/routes/scripts_test.go @@ -1,4 +1,4 @@ -package routes +package routes_test import ( "bytes" @@ -14,6 +14,7 @@ import ( "google.golang.org/grpc/status" "github.com/onflow/flow-go/access/mock" + "github.com/onflow/flow-go/engine/access/rest/router" "github.com/onflow/flow-go/engine/access/rest/util" "github.com/onflow/flow-go/model/flow" ) @@ -51,8 +52,8 @@ func TestScripts(t *testing.T) { On("ExecuteScriptAtLatestBlock", mocks.Anything, validCode, [][]byte{validArgs}). Return([]byte("hello world"), nil) - req := scriptReq("", sealedHeightQueryParam, validBody) - assertOKResponse(t, req, fmt.Sprintf( + req := scriptReq("", router.SealedHeightQueryParam, validBody) + router.AssertOKResponse(t, req, fmt.Sprintf( "\"%s\"", base64.StdEncoding.EncodeToString([]byte(`hello world`)), ), backend) @@ -67,7 +68,7 @@ func TestScripts(t *testing.T) { Return([]byte("hello world"), nil) req := scriptReq("", fmt.Sprintf("%d", height), validBody) - assertOKResponse(t, req, fmt.Sprintf( + router.AssertOKResponse(t, req, fmt.Sprintf( "\"%s\"", base64.StdEncoding.EncodeToString([]byte(`hello world`)), ), backend) @@ -82,7 +83,7 @@ func TestScripts(t *testing.T) { Return([]byte("hello world"), nil) req := scriptReq(id.String(), "", validBody) - assertOKResponse(t, req, fmt.Sprintf( + router.AssertOKResponse(t, req, fmt.Sprintf( "\"%s\"", base64.StdEncoding.EncodeToString([]byte(`hello world`)), ), backend) @@ -95,7 +96,7 @@ func TestScripts(t *testing.T) { Return(nil, status.Error(codes.Internal, "internal server error")) req := scriptReq("", "1337", validBody) - assertResponse( + router.AssertResponse( t, req, http.StatusBadRequest, @@ -125,7 +126,7 @@ func TestScripts(t *testing.T) { for _, test := range tests { req := scriptReq(test.id, test.height, test.body) - assertResponse(t, req, http.StatusBadRequest, test.out, backend) + router.AssertResponse(t, req, http.StatusBadRequest, test.out, backend) } }) } diff --git a/engine/access/rest/routes/transactions.go b/engine/access/rest/http/routes/transactions.go similarity index 65% rename from engine/access/rest/routes/transactions.go rename to engine/access/rest/http/routes/transactions.go index e85eb116894..02afef949e8 100644 --- a/engine/access/rest/routes/transactions.go +++ b/engine/access/rest/http/routes/transactions.go @@ -4,15 +4,17 @@ import ( entitiesproto "github.com/onflow/flow/protobuf/go/flow/entities" "github.com/onflow/flow-go/access" - "github.com/onflow/flow-go/engine/access/rest/models" - "github.com/onflow/flow-go/engine/access/rest/request" + "github.com/onflow/flow-go/engine/access/rest/common" + "github.com/onflow/flow-go/engine/access/rest/http/request" + + "github.com/onflow/flow-go/engine/access/rest/http/models" ) // GetTransactionByID gets a transaction by requested ID. -func GetTransactionByID(r *request.Request, backend access.API, link models.LinkGenerator) (interface{}, error) { - req, err := r.GetTransactionRequest() +func GetTransactionByID(r *common.Request, backend access.API, link models.LinkGenerator) (interface{}, error) { + req, err := request.GetTransactionRequest(r) if err != nil { - return nil, models.NewBadRequestError(err) + return nil, common.NewBadRequestError(err) } tx, err := backend.GetTransaction(r.Context(), req.ID) @@ -41,10 +43,10 @@ func GetTransactionByID(r *request.Request, backend access.API, link models.Link } // GetTransactionResultByID retrieves transaction result by the transaction ID. -func GetTransactionResultByID(r *request.Request, backend access.API, link models.LinkGenerator) (interface{}, error) { - req, err := r.GetTransactionResultRequest() +func GetTransactionResultByID(r *common.Request, backend access.API, link models.LinkGenerator) (interface{}, error) { + req, err := request.GetTransactionResultRequest(r) if err != nil { - return nil, models.NewBadRequestError(err) + return nil, common.NewBadRequestError(err) } txr, err := backend.GetTransactionResult( @@ -64,10 +66,10 @@ func GetTransactionResultByID(r *request.Request, backend access.API, link model } // CreateTransaction creates a new transaction from provided payload. -func CreateTransaction(r *request.Request, backend access.API, link models.LinkGenerator) (interface{}, error) { - req, err := r.CreateTransactionRequest() +func CreateTransaction(r *common.Request, backend access.API, link models.LinkGenerator) (interface{}, error) { + req, err := request.CreateTransactionRequest(r) if err != nil { - return nil, models.NewBadRequestError(err) + return nil, common.NewBadRequestError(err) } err = backend.SendTransaction(r.Context(), &req.Transaction) diff --git a/engine/access/rest/routes/transactions_test.go b/engine/access/rest/http/routes/transactions_test.go similarity index 91% rename from engine/access/rest/routes/transactions_test.go rename to engine/access/rest/http/routes/transactions_test.go index e0e36e1680b..e2deadd16e1 100644 --- a/engine/access/rest/routes/transactions_test.go +++ b/engine/access/rest/http/routes/transactions_test.go @@ -1,4 +1,4 @@ -package routes +package routes_test import ( "bytes" @@ -19,7 +19,8 @@ import ( "github.com/onflow/flow-go/access" "github.com/onflow/flow-go/access/mock" - "github.com/onflow/flow-go/engine/access/rest/models" + "github.com/onflow/flow-go/engine/access/rest/http/models" + "github.com/onflow/flow-go/engine/access/rest/router" "github.com/onflow/flow-go/engine/access/rest/util" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/utils/unittest" @@ -85,7 +86,7 @@ func TestGetTransactions(t *testing.T) { { "id":"%s", "script":"YWNjZXNzKGFsbCkgZnVuIG1haW4oKSB7fQ==", - "arguments": [], + "arguments": [], "reference_block_id":"%s", "gas_limit":"10", "payer":"8c5303eaa26202d6", @@ -97,7 +98,7 @@ func TestGetTransactions(t *testing.T) { "authorizers":[ "8c5303eaa26202d6" ], - "payload_signatures": [], + "payload_signatures": [], "envelope_signatures":[ { "address":"8c5303eaa26202d6", @@ -114,7 +115,7 @@ func TestGetTransactions(t *testing.T) { }`, tx.ID(), tx.ReferenceBlockID, util.ToBase64(tx.EnvelopeSignatures[0].Signature), tx.ID(), tx.ID()) - assertOKResponse(t, req, expected, backend) + router.AssertOKResponse(t, req, expected, backend) }) t.Run("Get by ID with results", func(t *testing.T) { @@ -137,7 +138,7 @@ func TestGetTransactions(t *testing.T) { { "id":"%s", "script":"YWNjZXNzKGFsbCkgZnVuIG1haW4oKSB7fQ==", - "arguments": [], + "arguments": [], "reference_block_id":"%s", "gas_limit":"10", "payer":"8c5303eaa26202d6", @@ -149,7 +150,7 @@ func TestGetTransactions(t *testing.T) { "authorizers":[ "8c5303eaa26202d6" ], - "payload_signatures": [], + "payload_signatures": [], "envelope_signatures":[ { "address":"8c5303eaa26202d6", @@ -178,13 +179,13 @@ func TestGetTransactions(t *testing.T) { "_self": "/v1/transaction_results/%s" } }, - "_expandable": {}, + "_expandable": {}, "_links":{ "_self":"/v1/transactions/%s" } }`, tx.ID(), tx.ReferenceBlockID, util.ToBase64(tx.EnvelopeSignatures[0].Signature), tx.ReferenceBlockID, txr.CollectionID, tx.ID(), tx.ID(), tx.ID()) - assertOKResponse(t, req, expected, backend) + router.AssertOKResponse(t, req, expected, backend) }) t.Run("get by ID Invalid", func(t *testing.T) { @@ -192,7 +193,7 @@ func TestGetTransactions(t *testing.T) { req := getTransactionReq("invalid", false, "", "") expected := `{"code":400, "message":"invalid ID format"}` - assertResponse(t, req, http.StatusBadRequest, expected, backend) + router.AssertResponse(t, req, http.StatusBadRequest, expected, backend) }) t.Run("get by ID non-existing", func(t *testing.T) { @@ -206,7 +207,7 @@ func TestGetTransactions(t *testing.T) { Return(nil, status.Error(codes.NotFound, "transaction not found")) expected := `{"code":404, "message":"Flow resource not found: transaction not found"}` - assertResponse(t, req, http.StatusNotFound, expected, backend) + router.AssertResponse(t, req, http.StatusNotFound, expected, backend) }) } @@ -255,7 +256,7 @@ func TestGetTransactionResult(t *testing.T) { On("GetTransactionResult", mocks.Anything, id, flow.ZeroID, flow.ZeroID, entities.EventEncodingVersion_JSON_CDC_V0). Return(txr, nil) - assertOKResponse(t, req, expected, backend) + router.AssertOKResponse(t, req, expected, backend) }) t.Run("get by block ID", func(t *testing.T) { @@ -267,7 +268,7 @@ func TestGetTransactionResult(t *testing.T) { On("GetTransactionResult", mocks.Anything, id, bid, flow.ZeroID, entities.EventEncodingVersion_JSON_CDC_V0). Return(txr, nil) - assertOKResponse(t, req, expected, backend) + router.AssertOKResponse(t, req, expected, backend) }) t.Run("get by collection ID", func(t *testing.T) { @@ -278,7 +279,7 @@ func TestGetTransactionResult(t *testing.T) { On("GetTransactionResult", mocks.Anything, id, flow.ZeroID, cid, entities.EventEncodingVersion_JSON_CDC_V0). Return(txr, nil) - assertOKResponse(t, req, expected, backend) + router.AssertOKResponse(t, req, expected, backend) }) t.Run("get execution statuses", func(t *testing.T) { @@ -326,7 +327,7 @@ func TestGetTransactionResult(t *testing.T) { "_self": "/v1/transaction_results/%s" } }`, bid.String(), cid.String(), err, cases.Title(language.English).String(strings.ToLower(txResult.Status.String())), txResult.ErrorMessage, id.String()) - assertOKResponse(t, req, expectedResp, backend) + router.AssertOKResponse(t, req, expectedResp, backend) } }) @@ -336,7 +337,7 @@ func TestGetTransactionResult(t *testing.T) { req := getTransactionResultReq("invalid", "", "") expected := `{"code":400, "message":"invalid ID format"}` - assertResponse(t, req, http.StatusBadRequest, expected, backend) + router.AssertResponse(t, req, http.StatusBadRequest, expected, backend) }) } @@ -369,7 +370,7 @@ func TestCreateTransaction(t *testing.T) { "authorizers":[ "8c5303eaa26202d6" ], - "payload_signatures":[ + "payload_signatures":[ { "address":"8c5303eaa26202d6", "key_index":"1", @@ -391,7 +392,7 @@ func TestCreateTransaction(t *testing.T) { } }`, tx.ID(), tx.ReferenceBlockID, util.ToBase64(tx.PayloadSignatures[0].Signature), util.ToBase64(tx.EnvelopeSignatures[0].Signature), tx.ID(), tx.ID()) - assertOKResponse(t, req, expected, backend) + router.AssertOKResponse(t, req, expected, backend) }) t.Run("post invalid transaction", func(t *testing.T) { @@ -403,6 +404,7 @@ func TestCreateTransaction(t *testing.T) { {"reference_block_id", "-1", `{"code":400, "message":"invalid reference block ID: invalid ID format"}`}, {"reference_block_id", "", `{"code":400, "message":"reference block not provided"}`}, {"gas_limit", "-1", `{"code":400, "message":"invalid gas limit: value must be an unsigned 64 bit integer"}`}, + {"gas_limit", "18446744073709551616", `{"code":400, "message":"invalid gas limit: value overflows uint64 range"}`}, {"payer", "yo", `{"code":400, "message":"invalid payer: invalid address"}`}, {"proposal_key", "yo", `{"code":400, "message":"request body contains an invalid value for the \"proposal_key\" field (at position 461)"}`}, {"authorizers", "", `{"code":400, "message":"request body contains an invalid value for the \"authorizers\" field (at position 32)"}`}, @@ -418,7 +420,7 @@ func TestCreateTransaction(t *testing.T) { testTx[test.inputField] = test.inputValue req := createTransactionReq(testTx) - assertResponse(t, req, http.StatusBadRequest, test.output, backend) + router.AssertResponse(t, req, http.StatusBadRequest, test.output, backend) } }) } diff --git a/engine/access/rest/request/get_collection.go b/engine/access/rest/request/get_collection.go deleted file mode 100644 index 151f7ddc6d5..00000000000 --- a/engine/access/rest/request/get_collection.go +++ /dev/null @@ -1,15 +0,0 @@ -package request - -const ExpandsTransactions = "transactions" - -type GetCollection struct { - GetByIDRequest - ExpandsTransactions bool -} - -func (g *GetCollection) Build(r *Request) error { - err := g.GetByIDRequest.Build(r) - g.ExpandsTransactions = r.Expands(ExpandsTransactions) - - return err -} diff --git a/engine/access/rest/request/get_execution_result.go b/engine/access/rest/request/get_execution_result.go deleted file mode 100644 index 4feda42a0b6..00000000000 --- a/engine/access/rest/request/get_execution_result.go +++ /dev/null @@ -1,38 +0,0 @@ -package request - -import ( - "fmt" - - "github.com/onflow/flow-go/model/flow" -) - -const idQuery = "id" - -type GetExecutionResultByBlockIDs struct { - BlockIDs []flow.Identifier -} - -func (g *GetExecutionResultByBlockIDs) Build(r *Request) error { - return g.Parse( - r.GetQueryParams(blockIDQuery), - ) -} - -func (g *GetExecutionResultByBlockIDs) Parse(rawIDs []string) error { - var ids IDs - err := ids.Parse(rawIDs) - if err != nil { - return err - } - g.BlockIDs = ids.Flow() - - if len(g.BlockIDs) == 0 { - return fmt.Errorf("no block IDs provided") - } - - return nil -} - -type GetExecutionResult struct { - GetByIDRequest -} diff --git a/engine/access/rest/router/http_routes.go b/engine/access/rest/router/http_routes.go new file mode 100644 index 00000000000..5032f591142 --- /dev/null +++ b/engine/access/rest/router/http_routes.go @@ -0,0 +1,102 @@ +package router + +import ( + "net/http" + + resthttp "github.com/onflow/flow-go/engine/access/rest/http" + "github.com/onflow/flow-go/engine/access/rest/http/routes" +) + +type route struct { + Name string + Method string + Pattern string + Handler resthttp.ApiHandlerFunc +} + +var Routes = []route{{ + Method: http.MethodGet, + Pattern: "/transactions/{id}", + Name: "getTransactionByID", + Handler: routes.GetTransactionByID, +}, { + Method: http.MethodPost, + Pattern: "/transactions", + Name: "createTransaction", + Handler: routes.CreateTransaction, +}, { + Method: http.MethodGet, + Pattern: "/transaction_results/{id}", + Name: "getTransactionResultByID", + Handler: routes.GetTransactionResultByID, +}, { + Method: http.MethodGet, + Pattern: "/blocks/{id}", + Name: "getBlocksByIDs", + Handler: routes.GetBlocksByIDs, +}, { + Method: http.MethodGet, + Pattern: "/blocks", + Name: "getBlocksByHeight", + Handler: routes.GetBlocksByHeight, +}, { + Method: http.MethodGet, + Pattern: "/blocks/{id}/payload", + Name: "getBlockPayloadByID", + Handler: routes.GetBlockPayloadByID, +}, { + Method: http.MethodGet, + Pattern: "/execution_results/{id}", + Name: "getExecutionResultByID", + Handler: routes.GetExecutionResultByID, +}, { + Method: http.MethodGet, + Pattern: "/execution_results", + Name: "getExecutionResultByBlockID", + Handler: routes.GetExecutionResultsByBlockIDs, +}, { + Method: http.MethodGet, + Pattern: "/collections/{id}", + Name: "getCollectionByID", + Handler: routes.GetCollectionByID, +}, { + Method: http.MethodPost, + Pattern: "/scripts", + Name: "executeScript", + Handler: routes.ExecuteScript, +}, { + Method: http.MethodGet, + Pattern: "/accounts/{address}", + Name: "getAccount", + Handler: routes.GetAccount, +}, { + Method: http.MethodGet, + Pattern: "/accounts/{address}/balance", + Name: "getAccountBalance", + Handler: routes.GetAccountBalance, +}, { + Method: http.MethodGet, + Pattern: "/accounts/{address}/keys/{index}", + Name: "getAccountKeyByIndex", + Handler: routes.GetAccountKeyByIndex, +}, { + Method: http.MethodGet, + Pattern: "/accounts/{address}/keys", + Name: "getAccountKeys", + Handler: routes.GetAccountKeys, +}, { + Method: http.MethodGet, + Pattern: "/events", + Name: "getEvents", + Handler: routes.GetEvents, +}, { + Method: http.MethodGet, + Pattern: "/network/parameters", + Name: "getNetworkParameters", + Handler: routes.GetNetworkParameters, +}, { + Method: http.MethodGet, + Pattern: "/node_version_info", + Name: "getNodeVersionInfo", + Handler: routes.GetNodeVersionInfo, +}} diff --git a/engine/access/rest/routes/router.go b/engine/access/rest/router/router.go similarity index 52% rename from engine/access/rest/routes/router.go rename to engine/access/rest/router/router.go index 57e505d7497..102f9797639 100644 --- a/engine/access/rest/routes/router.go +++ b/engine/access/rest/router/router.go @@ -1,8 +1,7 @@ -package routes +package router import ( "fmt" - "net/http" "regexp" "strings" @@ -10,8 +9,10 @@ import ( "github.com/rs/zerolog" "github.com/onflow/flow-go/access" - "github.com/onflow/flow-go/engine/access/rest/middleware" - "github.com/onflow/flow-go/engine/access/rest/models" + "github.com/onflow/flow-go/engine/access/rest/common/middleware" + "github.com/onflow/flow-go/engine/access/rest/http" + "github.com/onflow/flow-go/engine/access/rest/http/models" + legacyws "github.com/onflow/flow-go/engine/access/rest/websockets/legacy" "github.com/onflow/flow-go/engine/access/state_stream" "github.com/onflow/flow-go/engine/access/state_stream/backend" "github.com/onflow/flow-go/model/flow" @@ -46,10 +47,14 @@ func NewRouterBuilder( } // AddRestRoutes adds rest routes to the router. -func (b *RouterBuilder) AddRestRoutes(backend access.API, chain flow.Chain) *RouterBuilder { +func (b *RouterBuilder) AddRestRoutes( + backend access.API, + chain flow.Chain, + maxRequestSize int64, +) *RouterBuilder { linkGenerator := models.NewLinkGeneratorImpl(b.v1SubRouter) for _, r := range Routes { - h := NewHandler(b.logger, backend, r.Handler, linkGenerator, chain) + h := http.NewHandler(b.logger, backend, r.Handler, linkGenerator, chain, maxRequestSize) b.v1SubRouter. Methods(r.Method). Path(r.Pattern). @@ -59,15 +64,16 @@ func (b *RouterBuilder) AddRestRoutes(backend access.API, chain flow.Chain) *Rou return b } -// AddWsRoutes adds WebSocket routes to the router. -func (b *RouterBuilder) AddWsRoutes( +// AddWsLegacyRoutes adds WebSocket routes to the router. +func (b *RouterBuilder) AddWsLegacyRoutes( stateStreamApi state_stream.API, chain flow.Chain, stateStreamConfig backend.Config, + maxRequestSize int64, ) *RouterBuilder { - for _, r := range WSRoutes { - h := NewWSHandler(b.logger, stateStreamApi, r.Handler, chain, stateStreamConfig) + for _, r := range WSLegacyRoutes { + h := legacyws.NewWSHandler(b.logger, stateStreamApi, r.Handler, chain, stateStreamConfig, maxRequestSize) b.v1SubRouter. Methods(r.Method). Path(r.Pattern). @@ -82,114 +88,6 @@ func (b *RouterBuilder) Build() *mux.Router { return b.router } -type route struct { - Name string - Method string - Pattern string - Handler ApiHandlerFunc -} - -type wsroute struct { - Name string - Method string - Pattern string - Handler SubscribeHandlerFunc -} - -var Routes = []route{{ - Method: http.MethodGet, - Pattern: "/transactions/{id}", - Name: "getTransactionByID", - Handler: GetTransactionByID, -}, { - Method: http.MethodPost, - Pattern: "/transactions", - Name: "createTransaction", - Handler: CreateTransaction, -}, { - Method: http.MethodGet, - Pattern: "/transaction_results/{id}", - Name: "getTransactionResultByID", - Handler: GetTransactionResultByID, -}, { - Method: http.MethodGet, - Pattern: "/blocks/{id}", - Name: "getBlocksByIDs", - Handler: GetBlocksByIDs, -}, { - Method: http.MethodGet, - Pattern: "/blocks", - Name: "getBlocksByHeight", - Handler: GetBlocksByHeight, -}, { - Method: http.MethodGet, - Pattern: "/blocks/{id}/payload", - Name: "getBlockPayloadByID", - Handler: GetBlockPayloadByID, -}, { - Method: http.MethodGet, - Pattern: "/execution_results/{id}", - Name: "getExecutionResultByID", - Handler: GetExecutionResultByID, -}, { - Method: http.MethodGet, - Pattern: "/execution_results", - Name: "getExecutionResultByBlockID", - Handler: GetExecutionResultsByBlockIDs, -}, { - Method: http.MethodGet, - Pattern: "/collections/{id}", - Name: "getCollectionByID", - Handler: GetCollectionByID, -}, { - Method: http.MethodPost, - Pattern: "/scripts", - Name: "executeScript", - Handler: ExecuteScript, -}, { - Method: http.MethodGet, - Pattern: "/accounts/{address}", - Name: "getAccount", - Handler: GetAccount, -}, { - Method: http.MethodGet, - Pattern: "/accounts/{address}/balance", - Name: "getAccountBalance", - Handler: GetAccountBalance, -}, { - Method: http.MethodGet, - Pattern: "/accounts/{address}/keys/{index}", - Name: "getAccountKeyByIndex", - Handler: GetAccountKeyByIndex, -}, { - Method: http.MethodGet, - Pattern: "/accounts/{address}/keys", - Name: "getAccountKeys", - Handler: GetAccountKeys, -}, { - Method: http.MethodGet, - Pattern: "/events", - Name: "getEvents", - Handler: GetEvents, -}, { - Method: http.MethodGet, - Pattern: "/network/parameters", - Name: "getNetworkParameters", - Handler: GetNetworkParameters, -}, { - Method: http.MethodGet, - Pattern: "/node_version_info", - Name: "getNodeVersionInfo", - Handler: GetNodeVersionInfo, -}} - -var WSRoutes = []wsroute{{ - Method: http.MethodGet, - Pattern: "/subscribe_events", - Name: "subscribeEvents", - Handler: SubscribeEvents, -}} - var routeUrlMap = map[string]string{} var routeRE = regexp.MustCompile(`(?i)/v1/(\w+)(/(\w+))?(/(\w+))?(/(\w+))?`) @@ -197,7 +95,7 @@ func init() { for _, r := range Routes { routeUrlMap[r.Pattern] = r.Name } - for _, r := range WSRoutes { + for _, r := range WSLegacyRoutes { routeUrlMap[r.Pattern] = r.Name } } diff --git a/engine/access/rest/routes/router_test.go b/engine/access/rest/router/router_test.go similarity index 99% rename from engine/access/rest/routes/router_test.go rename to engine/access/rest/router/router_test.go index bcc4c20cc1f..36f0c0d003f 100644 --- a/engine/access/rest/routes/router_test.go +++ b/engine/access/rest/router/router_test.go @@ -1,4 +1,4 @@ -package routes +package router import ( "testing" diff --git a/engine/access/rest/routes/test_helpers.go b/engine/access/rest/router/router_test_helpers.go similarity index 63% rename from engine/access/rest/routes/test_helpers.go rename to engine/access/rest/router/router_test_helpers.go index feae66f5bf9..59a1d27ea4d 100644 --- a/engine/access/rest/routes/test_helpers.go +++ b/engine/access/rest/router/router_test_helpers.go @@ -1,4 +1,4 @@ -package routes +package router import ( "bufio" @@ -16,6 +16,7 @@ import ( "github.com/onflow/flow-go/access" "github.com/onflow/flow-go/access/mock" + "github.com/onflow/flow-go/engine/access/rest/common" "github.com/onflow/flow-go/engine/access/state_stream" "github.com/onflow/flow-go/engine/access/state_stream/backend" "github.com/onflow/flow-go/engine/access/subscription" @@ -27,16 +28,16 @@ import ( const ( ExpandableFieldPayload = "payload" ExpandableExecutionResult = "execution_result" - sealedHeightQueryParam = "sealed" - finalHeightQueryParam = "final" - startHeightQueryParam = "start_height" - endHeightQueryParam = "end_height" - heightQueryParam = "height" - startBlockIdQueryParam = "start_block_id" - eventTypesQueryParams = "event_types" - addressesQueryParams = "addresses" - contractsQueryParams = "contracts" - heartbeatIntervalQueryParam = "heartbeat_interval" + SealedHeightQueryParam = "sealed" + FinalHeightQueryParam = "final" + StartHeightQueryParam = "start_height" + EndHeightQueryParam = "end_height" + HeightQueryParam = "height" + StartBlockIdQueryParam = "start_block_id" + EventTypesQueryParams = "event_types" + AddressesQueryParams = "addresses" + ContractsQueryParams = "contracts" + HeartbeatIntervalQueryParam = "heartbeat_interval" ) // fakeNetConn implements a mocked ws connection that can be injected in testing logic. @@ -47,7 +48,7 @@ type fakeNetConn struct { var _ net.Conn = (*fakeNetConn)(nil) -// Close closes the fakeNetConn and signals its closure by closing the "closed" channel. +// Close closes the fakeNetConn and signals its closure by closing the "Closed" channel. func (c fakeNetConn) Close() error { select { case <-c.closed: @@ -82,50 +83,51 @@ func (a fakeAddr) String() string { return "str" } -// testHijackResponseRecorder is a custom ResponseRecorder that implements the http.Hijacker interface +// TestHijackResponseRecorder is a custom ResponseRecorder that implements the http.Hijacker interface // for testing WebSocket connections and hijacking. -type testHijackResponseRecorder struct { +type TestHijackResponseRecorder struct { *httptest.ResponseRecorder - closed chan struct{} - responseBuff *bytes.Buffer + Closed chan struct{} + ResponseBuff *bytes.Buffer } -var _ http.Hijacker = (*testHijackResponseRecorder)(nil) +var _ http.Hijacker = (*TestHijackResponseRecorder)(nil) // Hijack implements the http.Hijacker interface by returning a fakeNetConn and a bufio.ReadWriter // that simulate a hijacked connection. -func (w *testHijackResponseRecorder) Hijack() (net.Conn, *bufio.ReadWriter, error) { +func (w *TestHijackResponseRecorder) Hijack() (net.Conn, *bufio.ReadWriter, error) { br := bufio.NewReaderSize(strings.NewReader(""), subscription.DefaultSendBufferSize) bw := bufio.NewWriterSize(&bytes.Buffer{}, subscription.DefaultSendBufferSize) - w.responseBuff = bytes.NewBuffer(make([]byte, 0)) - w.closed = make(chan struct{}, 1) + w.ResponseBuff = bytes.NewBuffer(make([]byte, 0)) + w.Closed = make(chan struct{}, 1) - return fakeNetConn{w.responseBuff, w.closed}, bufio.NewReadWriter(br, bw), nil + return fakeNetConn{w.ResponseBuff, w.Closed}, bufio.NewReadWriter(br, bw), nil } -func (w *testHijackResponseRecorder) Close() error { +func (w *TestHijackResponseRecorder) Close() error { select { - case <-w.closed: + case <-w.Closed: default: - close(w.closed) + close(w.Closed) } return nil } -// newTestHijackResponseRecorder creates a new instance of testHijackResponseRecorder. -func newTestHijackResponseRecorder() *testHijackResponseRecorder { - return &testHijackResponseRecorder{ +// NewTestHijackResponseRecorder creates a new instance of TestHijackResponseRecorder. +func NewTestHijackResponseRecorder() *TestHijackResponseRecorder { + return &TestHijackResponseRecorder{ ResponseRecorder: httptest.NewRecorder(), } } -func executeRequest(req *http.Request, backend access.API) *httptest.ResponseRecorder { +func ExecuteRequest(req *http.Request, backend access.API) *httptest.ResponseRecorder { router := NewRouterBuilder( unittest.Logger(), metrics.NewNoopCollector(), ).AddRestRoutes( backend, flow.Testnet.Chain(), + common.DefaultMaxRequestSize, ).Build() rr := httptest.NewRecorder() @@ -133,7 +135,7 @@ func executeRequest(req *http.Request, backend access.API) *httptest.ResponseRec return rr } -func executeWsRequest(req *http.Request, stateStreamApi state_stream.API, responseRecorder *testHijackResponseRecorder, chain flow.Chain) { +func ExecuteWsRequest(req *http.Request, stateStreamApi state_stream.API, responseRecorder *TestHijackResponseRecorder, chain flow.Chain) { restCollector := metrics.NewNoopCollector() config := backend.Config{ @@ -142,18 +144,22 @@ func executeWsRequest(req *http.Request, stateStreamApi state_stream.API, respon HeartbeatInterval: subscription.DefaultHeartbeatInterval, } - router := NewRouterBuilder(unittest.Logger(), restCollector).AddWsRoutes( + router := NewRouterBuilder( + unittest.Logger(), + restCollector, + ).AddWsLegacyRoutes( stateStreamApi, - chain, config).Build() + chain, config, common.DefaultMaxRequestSize, + ).Build() router.ServeHTTP(responseRecorder, req) } -func assertOKResponse(t *testing.T, req *http.Request, expectedRespBody string, backend *mock.API) { - assertResponse(t, req, http.StatusOK, expectedRespBody, backend) +func AssertOKResponse(t *testing.T, req *http.Request, expectedRespBody string, backend *mock.API) { + AssertResponse(t, req, http.StatusOK, expectedRespBody, backend) } -func assertResponse(t *testing.T, req *http.Request, status int, expectedRespBody string, backend *mock.API) { - rr := executeRequest(req, backend) +func AssertResponse(t *testing.T, req *http.Request, status int, expectedRespBody string, backend *mock.API) { + rr := ExecuteRequest(req, backend) actualResponseBody := rr.Body.String() require.JSONEq(t, expectedRespBody, diff --git a/engine/access/rest/router/ws_routes.go b/engine/access/rest/router/ws_routes.go new file mode 100644 index 00000000000..03c28fa27aa --- /dev/null +++ b/engine/access/rest/router/ws_routes.go @@ -0,0 +1,22 @@ +package router + +import ( + "net/http" + + "github.com/onflow/flow-go/engine/access/rest/websockets/legacy" + "github.com/onflow/flow-go/engine/access/rest/websockets/legacy/routes" +) + +type wsLegacyRoute struct { + Name string + Method string + Pattern string + Handler legacy.SubscribeHandlerFunc +} + +var WSLegacyRoutes = []wsLegacyRoute{{ + Method: http.MethodGet, + Pattern: "/subscribe_events", + Name: "subscribeEvents", + Handler: routes.SubscribeEvents, +}} diff --git a/engine/access/rest/server.go b/engine/access/rest/server.go index 0d05fcd67cf..d25044a60a5 100644 --- a/engine/access/rest/server.go +++ b/engine/access/rest/server.go @@ -8,7 +8,7 @@ import ( "github.com/rs/zerolog" "github.com/onflow/flow-go/access" - "github.com/onflow/flow-go/engine/access/rest/routes" + "github.com/onflow/flow-go/engine/access/rest/router" "github.com/onflow/flow-go/engine/access/state_stream" "github.com/onflow/flow-go/engine/access/state_stream/backend" "github.com/onflow/flow-go/model/flow" @@ -27,10 +27,11 @@ const ( ) type Config struct { - ListenAddress string - WriteTimeout time.Duration - ReadTimeout time.Duration - IdleTimeout time.Duration + ListenAddress string + WriteTimeout time.Duration + ReadTimeout time.Duration + IdleTimeout time.Duration + MaxRequestSize int64 } // NewServer returns an HTTP server initialized with the REST API handler @@ -42,9 +43,9 @@ func NewServer(serverAPI access.API, stateStreamApi state_stream.API, stateStreamConfig backend.Config, ) (*http.Server, error) { - builder := routes.NewRouterBuilder(logger, restCollector).AddRestRoutes(serverAPI, chain) + builder := router.NewRouterBuilder(logger, restCollector).AddRestRoutes(serverAPI, chain, config.MaxRequestSize) if stateStreamApi != nil { - builder.AddWsRoutes(stateStreamApi, chain, stateStreamConfig) + builder.AddWsLegacyRoutes(stateStreamApi, chain, stateStreamConfig, config.MaxRequestSize) } c := cors.New(cors.Options{ diff --git a/engine/access/rest/util/converter.go b/engine/access/rest/util/converter.go index aebd58f8c71..77d3576e8b5 100644 --- a/engine/access/rest/util/converter.go +++ b/engine/access/rest/util/converter.go @@ -2,6 +2,7 @@ package util import ( "encoding/base64" + "errors" "fmt" "strconv" ) @@ -15,6 +16,9 @@ func FromUint[U uint | uint64 | uint32](number U) string { func ToUint64(uint64Str string) (uint64, error) { val, err := strconv.ParseUint(uint64Str, 10, 64) if err != nil { + if errors.Is(err, strconv.ErrRange) { + return 0, fmt.Errorf("value overflows uint64 range") + } return 0, fmt.Errorf("value must be an unsigned 64 bit integer") // hide error from user } return val, nil @@ -24,6 +28,9 @@ func ToUint64(uint64Str string) (uint64, error) { func ToUint32(uint32Str string) (uint32, error) { val, err := strconv.ParseUint(uint32Str, 10, 32) if err != nil { + if errors.Is(err, strconv.ErrRange) { + return 0, fmt.Errorf("value overflows uint32 range") + } return 0, fmt.Errorf("value must be an unsigned 32 bit integer") // hide error from user } return uint32(val), nil diff --git a/engine/access/rest/util/select_filter_test.go b/engine/access/rest/util/select_filter_test.go index 4e2e769bce4..54d186e0e5e 100644 --- a/engine/access/rest/util/select_filter_test.go +++ b/engine/access/rest/util/select_filter_test.go @@ -8,7 +8,7 @@ import ( "testing" "time" - "github.com/onflow/flow-go/engine/access/rest/models" + "github.com/onflow/flow-go/engine/access/rest/http/models" "github.com/onflow/flow-go/engine/access/rest/util" "github.com/stretchr/testify/require" diff --git a/engine/access/rest/request/subscribe_events.go b/engine/access/rest/websockets/legacy/request/subscribe_events.go similarity index 71% rename from engine/access/rest/request/subscribe_events.go rename to engine/access/rest/websockets/legacy/request/subscribe_events.go index 964edff641c..5b2574ccc82 100644 --- a/engine/access/rest/request/subscribe_events.go +++ b/engine/access/rest/websockets/legacy/request/subscribe_events.go @@ -4,9 +4,12 @@ import ( "fmt" "strconv" + "github.com/onflow/flow-go/engine/access/rest/common" + "github.com/onflow/flow-go/engine/access/rest/http/request" "github.com/onflow/flow-go/model/flow" ) +const startHeightQuery = "start_height" const startBlockIdQuery = "start_block_id" const eventTypesQuery = "event_types" const addressesQuery = "addresses" @@ -24,7 +27,17 @@ type SubscribeEvents struct { HeartbeatInterval uint64 } -func (g *SubscribeEvents) Build(r *Request) error { +// SubscribeEventsRequest extracts necessary variables from the provided request, +// builds a SubscribeEvents instance, and validates it. +// +// No errors are expected during normal operation. +func SubscribeEventsRequest(r *common.Request) (SubscribeEvents, error) { + var req SubscribeEvents + err := req.Build(r) + return req, err +} + +func (g *SubscribeEvents) Build(r *common.Request) error { return g.Parse( r.GetQueryParam(startBlockIdQuery), r.GetQueryParam(startHeightQuery), @@ -43,14 +56,14 @@ func (g *SubscribeEvents) Parse( rawContracts []string, rawHeartbeatInterval string, ) error { - var startBlockID ID + var startBlockID request.ID err := startBlockID.Parse(rawStartBlockID) if err != nil { return err } g.StartBlockID = startBlockID.Flow() - var height Height + var height request.Height err = height.Parse(rawStartHeight) if err != nil { return fmt.Errorf("invalid start height: %w", err) @@ -58,16 +71,16 @@ func (g *SubscribeEvents) Parse( g.StartHeight = height.Flow() // if both start_block_id and start_height are provided - if g.StartBlockID != flow.ZeroID && g.StartHeight != EmptyHeight { + if g.StartBlockID != flow.ZeroID && g.StartHeight != request.EmptyHeight { return fmt.Errorf("can only provide either block ID or start height") } // default to root block - if g.StartHeight == EmptyHeight { + if g.StartHeight == request.EmptyHeight { g.StartHeight = 0 } - var eventTypes EventTypes + var eventTypes request.EventTypes err = eventTypes.Parse(rawTypes) if err != nil { return err diff --git a/engine/access/rest/routes/subscribe_events.go b/engine/access/rest/websockets/legacy/routes/subscribe_events.go similarity index 53% rename from engine/access/rest/routes/subscribe_events.go rename to engine/access/rest/websockets/legacy/routes/subscribe_events.go index e1aca3bb316..9d159e7bdd3 100644 --- a/engine/access/rest/routes/subscribe_events.go +++ b/engine/access/rest/websockets/legacy/routes/subscribe_events.go @@ -3,8 +3,9 @@ package routes import ( "context" - "github.com/onflow/flow-go/engine/access/rest/models" - "github.com/onflow/flow-go/engine/access/rest/request" + "github.com/onflow/flow-go/engine/access/rest/common" + "github.com/onflow/flow-go/engine/access/rest/websockets/legacy" + "github.com/onflow/flow-go/engine/access/rest/websockets/legacy/request" "github.com/onflow/flow-go/engine/access/state_stream" "github.com/onflow/flow-go/engine/access/subscription" ) @@ -12,29 +13,29 @@ import ( // SubscribeEvents create websocket connection and write to it requested events. func SubscribeEvents( ctx context.Context, - request *request.Request, - wsController *WebsocketController, + r *common.Request, + wsController *legacy.WebsocketController, ) (subscription.Subscription, error) { - req, err := request.SubscribeEventsRequest() + req, err := request.SubscribeEventsRequest(r) if err != nil { - return nil, models.NewBadRequestError(err) + return nil, common.NewBadRequestError(err) } // Retrieve the filter parameters from the request, if provided filter, err := state_stream.NewEventFilter( - wsController.eventFilterConfig, - request.Chain, + wsController.EventFilterConfig, + r.Chain, req.EventTypes, req.Addresses, req.Contracts, ) if err != nil { - return nil, models.NewBadRequestError(err) + return nil, common.NewBadRequestError(err) } // Check if heartbeat interval was passed via request if req.HeartbeatInterval > 0 { - wsController.heartbeatInterval = req.HeartbeatInterval + wsController.HeartbeatInterval = req.HeartbeatInterval } - return wsController.api.SubscribeEvents(ctx, req.StartBlockID, req.StartHeight, filter), nil + return wsController.Api.SubscribeEvents(ctx, req.StartBlockID, req.StartHeight, filter), nil } diff --git a/engine/access/rest/routes/subscribe_events_test.go b/engine/access/rest/websockets/legacy/routes/subscribe_events_test.go similarity index 89% rename from engine/access/rest/routes/subscribe_events_test.go rename to engine/access/rest/websockets/legacy/routes/subscribe_events_test.go index 6eb56032abf..c4353cecae2 100644 --- a/engine/access/rest/routes/subscribe_events_test.go +++ b/engine/access/rest/websockets/legacy/routes/subscribe_events_test.go @@ -1,4 +1,4 @@ -package routes +package routes_test import ( "crypto/rand" @@ -20,7 +20,8 @@ import ( "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" - "github.com/onflow/flow-go/engine/access/rest/request" + "github.com/onflow/flow-go/engine/access/rest/http/request" + "github.com/onflow/flow-go/engine/access/rest/router" "github.com/onflow/flow-go/engine/access/state_stream" "github.com/onflow/flow-go/engine/access/state_stream/backend" mockstatestream "github.com/onflow/flow-go/engine/access/state_stream/mock" @@ -245,13 +246,13 @@ func (s *SubscribeEventsSuite) TestSubscribeEvents() { req, err := getSubscribeEventsRequest(s.T(), test.startBlockID, test.startHeight, test.eventTypes, test.addresses, test.contracts, test.heartbeatInterval, test.headers) require.NoError(s.T(), err) - respRecorder := newTestHijackResponseRecorder() + respRecorder := router.NewTestHijackResponseRecorder() // closing the connection after 1 second go func() { time.Sleep(1 * time.Second) respRecorder.Close() }() - executeWsRequest(req, stateStreamBackend, respRecorder, chainID.Chain()) + router.ExecuteWsRequest(req, stateStreamBackend, respRecorder, chainID.Chain()) requireResponse(s.T(), respRecorder, expectedEventsResponses) }) } @@ -262,8 +263,8 @@ func (s *SubscribeEventsSuite) TestSubscribeEventsHandlesErrors() { stateStreamBackend := mockstatestream.NewAPI(s.T()) req, err := getSubscribeEventsRequest(s.T(), s.blocks[0].ID(), s.blocks[0].Header.Height, nil, nil, nil, 1, nil) require.NoError(s.T(), err) - respRecorder := newTestHijackResponseRecorder() - executeWsRequest(req, stateStreamBackend, respRecorder, chainID.Chain()) + respRecorder := router.NewTestHijackResponseRecorder() + router.ExecuteWsRequest(req, stateStreamBackend, respRecorder, chainID.Chain()) requireError(s.T(), respRecorder, "can only provide either block ID or start height") }) @@ -287,8 +288,8 @@ func (s *SubscribeEventsSuite) TestSubscribeEventsHandlesErrors() { req, err := getSubscribeEventsRequest(s.T(), invalidBlock.ID(), request.EmptyHeight, nil, nil, nil, 1, nil) require.NoError(s.T(), err) - respRecorder := newTestHijackResponseRecorder() - executeWsRequest(req, stateStreamBackend, respRecorder, chainID.Chain()) + respRecorder := router.NewTestHijackResponseRecorder() + router.ExecuteWsRequest(req, stateStreamBackend, respRecorder, chainID.Chain()) requireError(s.T(), respRecorder, "stream encountered an error: subscription error") }) @@ -296,8 +297,8 @@ func (s *SubscribeEventsSuite) TestSubscribeEventsHandlesErrors() { stateStreamBackend := mockstatestream.NewAPI(s.T()) req, err := getSubscribeEventsRequest(s.T(), s.blocks[0].ID(), request.EmptyHeight, []string{"foo"}, nil, nil, 1, nil) require.NoError(s.T(), err) - respRecorder := newTestHijackResponseRecorder() - executeWsRequest(req, stateStreamBackend, respRecorder, chainID.Chain()) + respRecorder := router.NewTestHijackResponseRecorder() + router.ExecuteWsRequest(req, stateStreamBackend, respRecorder, chainID.Chain()) requireError(s.T(), respRecorder, "invalid event type format") }) @@ -321,8 +322,8 @@ func (s *SubscribeEventsSuite) TestSubscribeEventsHandlesErrors() { req, err := getSubscribeEventsRequest(s.T(), s.blocks[0].ID(), request.EmptyHeight, nil, nil, nil, 1, nil) require.NoError(s.T(), err) - respRecorder := newTestHijackResponseRecorder() - executeWsRequest(req, stateStreamBackend, respRecorder, chainID.Chain()) + respRecorder := router.NewTestHijackResponseRecorder() + router.ExecuteWsRequest(req, stateStreamBackend, respRecorder, chainID.Chain()) requireError(s.T(), respRecorder, "subscription channel closed") }) } @@ -340,24 +341,24 @@ func getSubscribeEventsRequest(t *testing.T, q := u.Query() if startBlockId != flow.ZeroID { - q.Add(startBlockIdQueryParam, startBlockId.String()) + q.Add(router.StartBlockIdQueryParam, startBlockId.String()) } if startHeight != request.EmptyHeight { - q.Add(startHeightQueryParam, fmt.Sprintf("%d", startHeight)) + q.Add(router.StartHeightQueryParam, fmt.Sprintf("%d", startHeight)) } if len(eventTypes) > 0 { - q.Add(eventTypesQueryParams, strings.Join(eventTypes, ",")) + q.Add(router.EventTypesQueryParams, strings.Join(eventTypes, ",")) } if len(addresses) > 0 { - q.Add(addressesQueryParams, strings.Join(addresses, ",")) + q.Add(router.AddressesQueryParams, strings.Join(addresses, ",")) } if len(contracts) > 0 { - q.Add(contractsQueryParams, strings.Join(contracts, ",")) + q.Add(router.ContractsQueryParams, strings.Join(contracts, ",")) } - q.Add(heartbeatIntervalQueryParam, fmt.Sprintf("%d", heartbeatInterval)) + q.Add(router.HeartbeatIntervalQueryParam, fmt.Sprintf("%d", heartbeatInterval)) u.RawQuery = q.Encode() key, err := generateWebSocketKey() @@ -392,18 +393,18 @@ func generateWebSocketKey() (string, error) { return base64.StdEncoding.EncodeToString(keyBytes), nil } -func requireError(t *testing.T, recorder *testHijackResponseRecorder, expected string) { - <-recorder.closed - require.Contains(t, recorder.responseBuff.String(), expected) +func requireError(t *testing.T, recorder *router.TestHijackResponseRecorder, expected string) { + <-recorder.Closed + require.Contains(t, recorder.ResponseBuff.String(), expected) } // requireResponse validates that the response received from WebSocket communication matches the expected EventsResponse. // This function compares the BlockID, Events count, and individual event properties for each expected and actual // EventsResponse. It ensures that the response received from WebSocket matches the expected structure and content. -func requireResponse(t *testing.T, recorder *testHijackResponseRecorder, expected []*backend.EventsResponse) { - <-recorder.closed +func requireResponse(t *testing.T, recorder *router.TestHijackResponseRecorder, expected []*backend.EventsResponse) { + <-recorder.Closed // Convert the actual response from respRecorder to JSON bytes - actualJSON := recorder.responseBuff.Bytes() + actualJSON := recorder.ResponseBuff.Bytes() // Define a regular expression pattern to match JSON objects pattern := `\{"BlockID":".*?","Height":\d+,"Events":\[(\{.*?})*\],"BlockTimestamp":".*?"\}` matches := regexp.MustCompile(pattern).FindAll(actualJSON, -1) diff --git a/engine/access/rest/routes/websocket_handler.go b/engine/access/rest/websockets/legacy/websocket_handler.go similarity index 89% rename from engine/access/rest/routes/websocket_handler.go rename to engine/access/rest/websockets/legacy/websocket_handler.go index f2261baa76f..7132314b16c 100644 --- a/engine/access/rest/routes/websocket_handler.go +++ b/engine/access/rest/websockets/legacy/websocket_handler.go @@ -1,4 +1,4 @@ -package routes +package legacy import ( "context" @@ -11,8 +11,7 @@ import ( "github.com/rs/zerolog" "go.uber.org/atomic" - "github.com/onflow/flow-go/engine/access/rest/models" - "github.com/onflow/flow-go/engine/access/rest/request" + "github.com/onflow/flow-go/engine/access/rest/common" "github.com/onflow/flow-go/engine/access/state_stream" "github.com/onflow/flow-go/engine/access/state_stream/backend" "github.com/onflow/flow-go/engine/access/subscription" @@ -36,12 +35,12 @@ const ( type WebsocketController struct { logger zerolog.Logger conn *websocket.Conn // the WebSocket connection for communication with the client - api state_stream.API // the state_stream.API instance for managing event subscriptions - eventFilterConfig state_stream.EventFilterConfig // the configuration for filtering events + Api state_stream.API // the state_stream.API instance for managing event subscriptions + EventFilterConfig state_stream.EventFilterConfig // the configuration for filtering events maxStreams int32 // the maximum number of streams allowed activeStreamCount *atomic.Int32 // the current number of active streams readChannel chan error // channel which notify closing connection by the client and provide errors to the client - heartbeatInterval uint64 // the interval to deliver heartbeat messages to client[IN BLOCKS] + HeartbeatInterval uint64 // the interval to deliver heartbeat messages to client[IN BLOCKS] } // SetWebsocketConf used to set read and write deadlines for WebSocket connections and establishes a Pong handler to @@ -50,11 +49,11 @@ type WebsocketController struct { func (wsController *WebsocketController) SetWebsocketConf() error { err := wsController.conn.SetWriteDeadline(time.Now().Add(writeWait)) // Set the initial write deadline for the first ping message if err != nil { - return models.NewRestError(http.StatusInternalServerError, "Set the initial write deadline error: ", err) + return common.NewRestError(http.StatusInternalServerError, "Set the initial write deadline error: ", err) } err = wsController.conn.SetReadDeadline(time.Now().Add(pongWait)) // Set the initial read deadline for the first pong message if err != nil { - return models.NewRestError(http.StatusInternalServerError, "Set the initial read deadline error: ", err) + return common.NewRestError(http.StatusInternalServerError, "Set the initial read deadline error: ", err) } // Establish a Pong handler wsController.conn.SetPongHandler(func(string) error { @@ -79,7 +78,7 @@ func (wsController *WebsocketController) SetWebsocketConf() error { // process are logged using the provided logger. func (wsController *WebsocketController) wsErrorHandler(err error) { // rest status type error should be returned with status and user message provided - var statusErr models.StatusError + var statusErr common.StatusError var wsCode int var wsMsg string @@ -135,12 +134,12 @@ func (wsController *WebsocketController) writeEvents(sub subscription.Subscripti return } err := fmt.Errorf("subscription channel closed, no error occurred") - wsController.wsErrorHandler(models.NewRestError(http.StatusRequestTimeout, "subscription channel closed", err)) + wsController.wsErrorHandler(common.NewRestError(http.StatusRequestTimeout, "subscription channel closed", err)) return } err := wsController.conn.SetWriteDeadline(time.Now().Add(writeWait)) if err != nil { - wsController.wsErrorHandler(models.NewRestError(http.StatusInternalServerError, "failed to set the initial write deadline: ", err)) + wsController.wsErrorHandler(common.NewRestError(http.StatusInternalServerError, "failed to set the initial write deadline: ", err)) return } @@ -154,7 +153,7 @@ func (wsController *WebsocketController) writeEvents(sub subscription.Subscripti // message will be emitted. if len(resp.Events) == 0 { blocksSinceLastMessage++ - if blocksSinceLastMessage < wsController.heartbeatInterval { + if blocksSinceLastMessage < wsController.HeartbeatInterval { continue } blocksSinceLastMessage = 0 @@ -181,7 +180,7 @@ func (wsController *WebsocketController) writeEvents(sub subscription.Subscripti case <-ticker.C: err := wsController.conn.SetWriteDeadline(time.Now().Add(writeWait)) if err != nil { - wsController.wsErrorHandler(models.NewRestError(http.StatusInternalServerError, "failed to set the initial write deadline: ", err)) + wsController.wsErrorHandler(common.NewRestError(http.StatusInternalServerError, "failed to set the initial write deadline: ", err)) return } if err := wsController.conn.WriteMessage(websocket.PingMessage, nil); err != nil { @@ -228,14 +227,14 @@ func (wsController *WebsocketController) read() { // SubscribeHandlerFunc is a function that contains endpoint handling logic for subscribes, fetches necessary resources type SubscribeHandlerFunc func( ctx context.Context, - request *request.Request, + request *common.Request, wsController *WebsocketController, ) (subscription.Subscription, error) // WSHandler is websocket handler implementing custom websocket handler function and allows easier handling of errors and // responses as it wraps functionality for handling error and responses outside of endpoint handling. type WSHandler struct { - *HttpHandler + *common.HttpHandler subscribeFunc SubscribeHandlerFunc api state_stream.API @@ -253,6 +252,7 @@ func NewWSHandler( subscribeFunc SubscribeHandlerFunc, chain flow.Chain, stateStreamConfig backend.Config, + maxRequestSize int64, ) *WSHandler { handler := &WSHandler{ subscribeFunc: subscribeFunc, @@ -261,7 +261,7 @@ func NewWSHandler( maxStreams: int32(stateStreamConfig.MaxGlobalStreams), defaultHeartbeatInterval: stateStreamConfig.HeartbeatInterval, activeStreamCount: atomic.NewInt32(0), - HttpHandler: NewHttpHandler(logger, chain), + HttpHandler: common.NewHttpHandler(logger, chain, maxRequestSize), } return handler @@ -271,9 +271,9 @@ func NewWSHandler( // such as logging, error handling, request decorators func (h *WSHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { // create a logger - logger := h.Logger.With().Str("subscribe_url", r.URL.String()).Logger() + logger := h.HttpHandler.Logger.With().Str("subscribe_url", r.URL.String()).Logger() - err := h.VerifyRequest(w, r) + err := h.HttpHandler.VerifyRequest(w, r) if err != nil { // VerifyRequest sets the response error before returning return @@ -288,7 +288,7 @@ func (h *WSHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } conn, err := upgrader.Upgrade(w, r, nil) if err != nil { - h.errorHandler(w, models.NewRestError(http.StatusInternalServerError, "webSocket upgrade error: ", err), logger) + h.HttpHandler.ErrorHandler(w, common.NewRestError(http.StatusInternalServerError, "webSocket upgrade error: ", err), logger) return } defer conn.Close() @@ -296,12 +296,12 @@ func (h *WSHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { wsController := &WebsocketController{ logger: logger, conn: conn, - api: h.api, - eventFilterConfig: h.eventFilterConfig, + Api: h.api, + EventFilterConfig: h.eventFilterConfig, maxStreams: h.maxStreams, activeStreamCount: h.activeStreamCount, readChannel: make(chan error), - heartbeatInterval: h.defaultHeartbeatInterval, // set default heartbeat interval from state stream config + HeartbeatInterval: h.defaultHeartbeatInterval, // set default heartbeat interval from state stream config } err = wsController.SetWebsocketConf() @@ -312,7 +312,7 @@ func (h *WSHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { if wsController.activeStreamCount.Load() >= wsController.maxStreams { err := fmt.Errorf("maximum number of streams reached") - wsController.wsErrorHandler(models.NewRestError(http.StatusServiceUnavailable, err.Error(), err)) + wsController.wsErrorHandler(common.NewRestError(http.StatusServiceUnavailable, err.Error(), err)) return } wsController.activeStreamCount.Add(1) @@ -323,7 +323,7 @@ func (h *WSHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - sub, err := h.subscribeFunc(ctx, request.Decorate(r, h.HttpHandler.Chain), wsController) + sub, err := h.subscribeFunc(ctx, common.Decorate(r, h.HttpHandler.Chain), wsController) if err != nil { wsController.wsErrorHandler(err) return diff --git a/engine/access/rest_api_test.go b/engine/access/rest_api_test.go index 96c6aadf150..64dab073c1d 100644 --- a/engine/access/rest_api_test.go +++ b/engine/access/rest_api_test.go @@ -21,8 +21,9 @@ import ( accessmock "github.com/onflow/flow-go/engine/access/mock" "github.com/onflow/flow-go/engine/access/rest" - "github.com/onflow/flow-go/engine/access/rest/request" - "github.com/onflow/flow-go/engine/access/rest/routes" + "github.com/onflow/flow-go/engine/access/rest/common" + "github.com/onflow/flow-go/engine/access/rest/http/request" + "github.com/onflow/flow-go/engine/access/rest/router" "github.com/onflow/flow-go/engine/access/rpc" "github.com/onflow/flow-go/engine/access/rpc/backend" statestreambackend "github.com/onflow/flow-go/engine/access/state_stream/backend" @@ -424,7 +425,7 @@ func (suite *RestAPITestSuite) TestRequestSizeRestriction() { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() // make a request of size larger than the max permitted size - requestBytes := make([]byte, routes.MaxRequestSize+1) + requestBytes := make([]byte, common.DefaultMaxRequestSize+1) script := restclient.ScriptsBody{ Script: string(requestBytes), } @@ -451,13 +452,13 @@ func assertError(t *testing.T, resp *http.Response, err error, expectedCode int, func optionsForBlockByID() *restclient.BlocksApiBlocksIdGetOpts { return &restclient.BlocksApiBlocksIdGetOpts{ - Expand: optional.NewInterface([]string{routes.ExpandableFieldPayload}), + Expand: optional.NewInterface([]string{router.ExpandableFieldPayload}), Select_: optional.NewInterface([]string{"header.id"}), } } func optionsForBlockByStartEndHeight(startHeight, endHeight uint64) *restclient.BlocksApiBlocksGetOpts { return &restclient.BlocksApiBlocksGetOpts{ - Expand: optional.NewInterface([]string{routes.ExpandableFieldPayload}), + Expand: optional.NewInterface([]string{router.ExpandableFieldPayload}), Select_: optional.NewInterface([]string{"header.id", "header.height"}), StartHeight: optional.NewInterface(startHeight), EndHeight: optional.NewInterface(endHeight), @@ -466,7 +467,7 @@ func optionsForBlockByStartEndHeight(startHeight, endHeight uint64) *restclient. func optionsForBlockByHeights(heights []uint64) *restclient.BlocksApiBlocksGetOpts { return &restclient.BlocksApiBlocksGetOpts{ - Expand: optional.NewInterface([]string{routes.ExpandableFieldPayload}), + Expand: optional.NewInterface([]string{router.ExpandableFieldPayload}), Select_: optional.NewInterface([]string{"header.id", "header.height"}), Height: optional.NewInterface(heights), } @@ -474,7 +475,7 @@ func optionsForBlockByHeights(heights []uint64) *restclient.BlocksApiBlocksGetOp func optionsForFinalizedBlock(finalOrSealed string) *restclient.BlocksApiBlocksGetOpts { return &restclient.BlocksApiBlocksGetOpts{ - Expand: optional.NewInterface([]string{routes.ExpandableFieldPayload}), + Expand: optional.NewInterface([]string{router.ExpandableFieldPayload}), Select_: optional.NewInterface([]string{"header.id", "header.height"}), Height: optional.NewInterface(finalOrSealed), } diff --git a/engine/access/rpc/backend/backend.go b/engine/access/rpc/backend/backend.go index 530c7943a1b..d4666af9529 100644 --- a/engine/access/rpc/backend/backend.go +++ b/engine/access/rpc/backend/backend.go @@ -3,6 +3,7 @@ package backend import ( "context" "crypto/md5" //nolint:gosec + "errors" "fmt" "time" @@ -16,23 +17,18 @@ import ( "github.com/onflow/flow-go/engine/access/rpc/connection" "github.com/onflow/flow-go/engine/access/subscription" "github.com/onflow/flow-go/engine/common/rpc" + commonrpc "github.com/onflow/flow-go/engine/common/rpc" "github.com/onflow/flow-go/engine/common/version" "github.com/onflow/flow-go/fvm/blueprints" "github.com/onflow/flow-go/model/flow" - "github.com/onflow/flow-go/model/flow/filter" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/counters" "github.com/onflow/flow-go/module/execution" + "github.com/onflow/flow-go/module/state_synchronization" "github.com/onflow/flow-go/state/protocol" "github.com/onflow/flow-go/storage" ) -// minExecutionNodesCnt is the minimum number of execution nodes expected to have sent the execution receipt for a block -const minExecutionNodesCnt = 2 - -// maxAttemptsForExecutionReceipt is the maximum number of attempts to find execution receipts for a given block ID -const maxAttemptsForExecutionReceipt = 3 - // DefaultMaxHeightRange is the default maximum size of range requests. const DefaultMaxHeightRange = 250 @@ -47,11 +43,6 @@ const DefaultLoggedScriptsCacheSize = 1_000_000 // DefaultConnectionPoolSize is the default size for the connection pool to collection and execution nodes const DefaultConnectionPoolSize = 250 -var ( - preferredENIdentifiers flow.IdentifierList - fixedENIdentifiers flow.IdentifierList -) - // Backend implements the Access API. // // It is composed of several sub-backends that implement part of the Access API. @@ -88,39 +79,39 @@ type Backend struct { } type Params struct { - State protocol.State - CollectionRPC accessproto.AccessAPIClient - HistoricalAccessNodes []accessproto.AccessAPIClient - Blocks storage.Blocks - Headers storage.Headers - Collections storage.Collections - Transactions storage.Transactions - ExecutionReceipts storage.ExecutionReceipts - ExecutionResults storage.ExecutionResults - ChainID flow.ChainID - AccessMetrics module.AccessMetrics - ConnFactory connection.ConnectionFactory - RetryEnabled bool - MaxHeightRange uint - PreferredExecutionNodeIDs []string - FixedExecutionNodeIDs []string - Log zerolog.Logger - SnapshotHistoryLimit int - Communicator Communicator - TxResultCacheSize uint - TxErrorMessagesCacheSize uint - ScriptExecutor execution.ScriptExecutor - ScriptExecutionMode IndexQueryMode - CheckPayerBalance bool - EventQueryMode IndexQueryMode - BlockTracker subscription.BlockTracker - SubscriptionHandler *subscription.SubscriptionHandler - - EventsIndex *index.EventsIndex - TxResultQueryMode IndexQueryMode - TxResultsIndex *index.TransactionResultsIndex - LastFullBlockHeight *counters.PersistentStrictMonotonicCounter - VersionControl *version.VersionControl + State protocol.State + CollectionRPC accessproto.AccessAPIClient + HistoricalAccessNodes []accessproto.AccessAPIClient + Blocks storage.Blocks + Headers storage.Headers + Collections storage.Collections + Transactions storage.Transactions + ExecutionReceipts storage.ExecutionReceipts + ExecutionResults storage.ExecutionResults + TxResultErrorMessages storage.TransactionResultErrorMessages + ChainID flow.ChainID + AccessMetrics module.AccessMetrics + ConnFactory connection.ConnectionFactory + RetryEnabled bool + MaxHeightRange uint + Log zerolog.Logger + SnapshotHistoryLimit int + Communicator Communicator + TxResultCacheSize uint + ScriptExecutor execution.ScriptExecutor + ScriptExecutionMode IndexQueryMode + CheckPayerBalanceMode access.PayerBalanceMode + EventQueryMode IndexQueryMode + BlockTracker subscription.BlockTracker + SubscriptionHandler *subscription.SubscriptionHandler + + EventsIndex *index.EventsIndex + TxResultQueryMode IndexQueryMode + TxResultsIndex *index.TransactionResultsIndex + LastFullBlockHeight *counters.PersistentStrictMonotonicCounter + IndexReporter state_synchronization.IndexReporter + VersionControl *version.VersionControl + ExecNodeIdentitiesProvider *commonrpc.ExecutionNodeIdentitiesProvider } var _ TransactionErrorMessage = (*Backend)(nil) @@ -145,18 +136,6 @@ func New(params Params) (*Backend, error) { } } - // NOTE: The transaction error message cache is currently only used by the access node and not by the observer node. - // To avoid introducing unnecessary command line arguments in the observer, one case could be that the error - // message cache is nil for the observer node. - var txErrorMessagesCache *lru.Cache[flow.Identifier, string] - - if params.TxErrorMessagesCacheSize > 0 { - txErrorMessagesCache, err = lru.New[flow.Identifier, string](int(params.TxErrorMessagesCacheSize)) - if err != nil { - return nil, fmt.Errorf("failed to init cache for transaction error messages: %w", err) - } - } - // the system tx is hardcoded and never changes during runtime systemTx, err := blueprints.SystemChunkTransaction(params.ChainID.Chain()) if err != nil { @@ -179,28 +158,28 @@ func New(params Params) (*Backend, error) { BlockTracker: params.BlockTracker, // create the sub-backends backendScripts: backendScripts{ - log: params.Log, - headers: params.Headers, - executionReceipts: params.ExecutionReceipts, - connFactory: params.ConnFactory, - state: params.State, - metrics: params.AccessMetrics, - loggedScripts: loggedScripts, - nodeCommunicator: params.Communicator, - scriptExecutor: params.ScriptExecutor, - scriptExecMode: params.ScriptExecutionMode, + log: params.Log, + headers: params.Headers, + connFactory: params.ConnFactory, + state: params.State, + metrics: params.AccessMetrics, + loggedScripts: loggedScripts, + nodeCommunicator: params.Communicator, + scriptExecutor: params.ScriptExecutor, + scriptExecMode: params.ScriptExecutionMode, + execNodeIdentitiesProvider: params.ExecNodeIdentitiesProvider, }, backendEvents: backendEvents{ - log: params.Log, - chain: params.ChainID.Chain(), - state: params.State, - headers: params.Headers, - executionReceipts: params.ExecutionReceipts, - connFactory: params.ConnFactory, - maxHeightRange: params.MaxHeightRange, - nodeCommunicator: params.Communicator, - queryMode: params.EventQueryMode, - eventsIndex: params.EventsIndex, + log: params.Log, + chain: params.ChainID.Chain(), + state: params.State, + headers: params.Headers, + connFactory: params.ConnFactory, + maxHeightRange: params.MaxHeightRange, + nodeCommunicator: params.Communicator, + queryMode: params.EventQueryMode, + eventsIndex: params.EventsIndex, + execNodeIdentitiesProvider: params.ExecNodeIdentitiesProvider, }, backendBlockHeaders: backendBlockHeaders{ headers: params.Headers, @@ -211,14 +190,14 @@ func New(params Params) (*Backend, error) { state: params.State, }, backendAccounts: backendAccounts{ - log: params.Log, - state: params.State, - headers: params.Headers, - executionReceipts: params.ExecutionReceipts, - connFactory: params.ConnFactory, - nodeCommunicator: params.Communicator, - scriptExecutor: params.ScriptExecutor, - scriptExecMode: params.ScriptExecutionMode, + log: params.Log, + state: params.State, + headers: params.Headers, + connFactory: params.ConnFactory, + nodeCommunicator: params.Communicator, + scriptExecutor: params.ScriptExecutor, + scriptExecMode: params.ScriptExecutionMode, + execNodeIdentitiesProvider: params.ExecNodeIdentitiesProvider, }, backendExecutionResults: backendExecutionResults{ executionResults: params.ExecutionResults, @@ -246,7 +225,7 @@ func New(params Params) (*Backend, error) { versionControl: params.VersionControl, } - txValidator, err := configureTransactionValidator(params.State, params.ChainID, params.AccessMetrics, params.ScriptExecutor, params.CheckPayerBalance) + txValidator, err := configureTransactionValidator(params.State, params.ChainID, params.IndexReporter, params.AccessMetrics, params.ScriptExecutor, params.CheckPayerBalanceMode) if err != nil { return nil, fmt.Errorf("could not create transaction validator: %w", err) } @@ -257,7 +236,7 @@ func New(params Params) (*Backend, error) { staticCollectionRPC: params.CollectionRPC, chainID: params.ChainID, transactions: params.Transactions, - executionReceipts: params.ExecutionReceipts, + txResultErrorMessages: params.TxResultErrorMessages, transactionValidator: txValidator, transactionMetrics: params.AccessMetrics, retry: retry, @@ -265,10 +244,10 @@ func New(params Params) (*Backend, error) { previousAccessNodes: params.HistoricalAccessNodes, nodeCommunicator: params.Communicator, txResultCache: txResCache, - txErrorMessagesCache: txErrorMessagesCache, txResultQueryMode: params.TxResultQueryMode, systemTx: systemTx, systemTxID: systemTxID, + execNodeIdentitiesProvider: params.ExecNodeIdentitiesProvider, } // TODO: The TransactionErrorMessage interface should be reorganized in future, as it is implemented in backendTransactions but used in TransactionsLocalDataProvider, and its initialization is somewhat quirky. @@ -285,40 +264,19 @@ func New(params Params) (*Backend, error) { retry.SetBackend(b) - preferredENIdentifiers, err = identifierList(params.PreferredExecutionNodeIDs) - if err != nil { - return nil, fmt.Errorf("failed to convert node id string to Flow Identifier for preferred EN map: %w", err) - } - - fixedENIdentifiers, err = identifierList(params.FixedExecutionNodeIDs) - if err != nil { - return nil, fmt.Errorf("failed to convert node id string to Flow Identifier for fixed EN map: %w", err) - } - return b, nil } -func identifierList(ids []string) (flow.IdentifierList, error) { - idList := make(flow.IdentifierList, len(ids)) - for i, idStr := range ids { - id, err := flow.HexStringToIdentifier(idStr) - if err != nil { - return nil, fmt.Errorf("failed to convert node id string %s to Flow Identifier: %w", id, err) - } - idList[i] = id - } - return idList, nil -} - func configureTransactionValidator( state protocol.State, chainID flow.ChainID, + indexReporter state_synchronization.IndexReporter, transactionMetrics module.TransactionValidationMetrics, executor execution.ScriptExecutor, - checkPayerBalance bool, + checkPayerBalanceMode access.PayerBalanceMode, ) (*access.TransactionValidator, error) { return access.NewTransactionValidator( - access.NewProtocolStateBlocks(state), + access.NewProtocolStateBlocks(state, indexReporter), chainID.Chain(), transactionMetrics, access.TransactionValidationOptions{ @@ -330,7 +288,7 @@ func configureTransactionValidator( MaxGasLimit: flow.DefaultMaxTransactionGasLimit, MaxTransactionByteSize: flow.DefaultMaxTransactionByteSize, MaxCollectionByteSize: flow.DefaultMaxCollectionByteSize, - CheckPayerBalance: checkPayerBalance, + CheckPayerBalanceMode: checkPayerBalanceMode, }, executor, ) @@ -416,225 +374,43 @@ func (b *Backend) GetNetworkParameters(_ context.Context) access.NetworkParamete } } -// executionNodesForBlockID returns upto maxNodesCnt number of randomly chosen execution node identities -// which have executed the given block ID. -// If no such execution node is found, an InsufficientExecutionReceipts error is returned. -func executionNodesForBlockID( - ctx context.Context, - blockID flow.Identifier, - executionReceipts storage.ExecutionReceipts, - state protocol.State, - log zerolog.Logger, -) (flow.IdentitySkeletonList, error) { - var ( - executorIDs flow.IdentifierList - err error - ) - - // check if the block ID is of the root block. If it is then don't look for execution receipts since they - // will not be present for the root block. - rootBlock := state.Params().FinalizedRoot() - - if rootBlock.ID() == blockID { - executorIdentities, err := state.Final().Identities(filter.HasRole[flow.Identity](flow.RoleExecution)) - if err != nil { - return nil, fmt.Errorf("failed to retreive execution IDs for block ID %v: %w", blockID, err) - } - executorIDs = executorIdentities.NodeIDs() +// resolveHeightError processes errors returned during height-based queries. +// If the error is due to a block not being found, this function determines whether the queried +// height falls outside the node's accessible range and provides context-sensitive error messages +// based on spork and node root block heights. +// +// Parameters: +// - stateParams: Protocol parameters that contain spork root and node root block heights. +// - height: The queried block height. +// - genericErr: The initial error returned when the block is not found. +// +// Expected errors during normal operation: +// - storage.ErrNotFound - Indicates that the queried block does not exist in the local database. +func resolveHeightError( + stateParams protocol.Params, + height uint64, + genericErr error, +) error { + if !errors.Is(genericErr, storage.ErrNotFound) { + return genericErr + } + + sporkRootBlockHeight := stateParams.SporkRootBlockHeight() + nodeRootBlockHeader := stateParams.SealedRoot().Height + + if height < sporkRootBlockHeight { + return fmt.Errorf("block height %d is less than the spork root block height %d. Try to use a historic node: %w", + height, + sporkRootBlockHeight, + genericErr, + ) + } else if height < nodeRootBlockHeader { + return fmt.Errorf("block height %d is less than the node's root block height %d. Try to use a different Access node: %w", + height, + nodeRootBlockHeader, + genericErr, + ) } else { - // try to find at least minExecutionNodesCnt execution node ids from the execution receipts for the given blockID - for attempt := 0; attempt < maxAttemptsForExecutionReceipt; attempt++ { - executorIDs, err = findAllExecutionNodes(blockID, executionReceipts, log) - if err != nil { - return nil, err - } - - if len(executorIDs) >= minExecutionNodesCnt { - break - } - - // log the attempt - log.Debug().Int("attempt", attempt).Int("max_attempt", maxAttemptsForExecutionReceipt). - Int("execution_receipts_found", len(executorIDs)). - Str("block_id", blockID.String()). - Msg("insufficient execution receipts") - - // if one or less execution receipts may have been received then re-query - // in the hope that more might have been received by now - - select { - case <-ctx.Done(): - return nil, ctx.Err() - case <-time.After(100 * time.Millisecond << time.Duration(attempt)): - // retry after an exponential backoff - } - } - - receiptCnt := len(executorIDs) - // if less than minExecutionNodesCnt execution receipts have been received so far, then return random ENs - if receiptCnt < minExecutionNodesCnt { - newExecutorIDs, err := state.AtBlockID(blockID).Identities(filter.HasRole[flow.Identity](flow.RoleExecution)) - if err != nil { - return nil, fmt.Errorf("failed to retreive execution IDs for block ID %v: %w", blockID, err) - } - executorIDs = newExecutorIDs.NodeIDs() - } - } - - // choose from the preferred or fixed execution nodes - subsetENs, err := chooseExecutionNodes(state, executorIDs) - if err != nil { - return nil, fmt.Errorf("failed to retreive execution IDs for block ID %v: %w", blockID, err) - } - - if len(subsetENs) == 0 { - return nil, fmt.Errorf("no matching execution node found for block ID %v", blockID) - } - - return subsetENs, nil -} - -// findAllExecutionNodes find all the execution nodes ids from the execution receipts that have been received for the -// given blockID -func findAllExecutionNodes( - blockID flow.Identifier, - executionReceipts storage.ExecutionReceipts, - log zerolog.Logger, -) (flow.IdentifierList, error) { - // lookup the receipt's storage with the block ID - allReceipts, err := executionReceipts.ByBlockID(blockID) - if err != nil { - return nil, fmt.Errorf("failed to retreive execution receipts for block ID %v: %w", blockID, err) - } - - executionResultMetaList := make(flow.ExecutionReceiptMetaList, 0, len(allReceipts)) - for _, r := range allReceipts { - executionResultMetaList = append(executionResultMetaList, r.Meta()) - } - executionResultGroupedMetaList := executionResultMetaList.GroupByResultID() - - // maximum number of matching receipts found so far for any execution result id - maxMatchedReceiptCnt := 0 - // execution result id key for the highest number of matching receipts in the identicalReceipts map - var maxMatchedReceiptResultID flow.Identifier - - // find the largest list of receipts which have the same result ID - for resultID, executionReceiptList := range executionResultGroupedMetaList { - currentMatchedReceiptCnt := executionReceiptList.Size() - if currentMatchedReceiptCnt > maxMatchedReceiptCnt { - maxMatchedReceiptCnt = currentMatchedReceiptCnt - maxMatchedReceiptResultID = resultID - } - } - - // if there are more than one execution result for the same block ID, log as error - if executionResultGroupedMetaList.NumberGroups() > 1 { - identicalReceiptsStr := fmt.Sprintf("%v", flow.GetIDs(allReceipts)) - log.Error(). - Str("block_id", blockID.String()). - Str("execution_receipts", identicalReceiptsStr). - Msg("execution receipt mismatch") - } - - // pick the largest list of matching receipts - matchingReceiptMetaList := executionResultGroupedMetaList.GetGroup(maxMatchedReceiptResultID) - - metaReceiptGroupedByExecutorID := matchingReceiptMetaList.GroupByExecutorID() - - // collect all unique execution node ids from the receipts - var executorIDs flow.IdentifierList - for executorID := range metaReceiptGroupedByExecutorID { - executorIDs = append(executorIDs, executorID) - } - - return executorIDs, nil -} - -// chooseExecutionNodes finds the subset of execution nodes defined in the identity table by first -// choosing the preferred execution nodes which have executed the transaction. If no such preferred -// execution nodes are found, then the fixed execution nodes defined in the identity table are returned -// If neither preferred nor fixed nodes are defined, then all execution node matching the executor IDs are returned. -// e.g. If execution nodes in identity table are {1,2,3,4}, preferred ENs are defined as {2,3,4} -// and the executor IDs is {1,2,3}, then {2, 3} is returned as the chosen subset of ENs -func chooseExecutionNodes(state protocol.State, executorIDs flow.IdentifierList) (flow.IdentitySkeletonList, error) { - allENs, err := state.Final().Identities(filter.HasRole[flow.Identity](flow.RoleExecution)) - if err != nil { - return nil, fmt.Errorf("failed to retrieve all execution IDs: %w", err) + return genericErr } - - // choose from preferred EN IDs - if len(preferredENIdentifiers) > 0 { - chosenIDs := chooseFromPreferredENIDs(allENs, executorIDs) - return chosenIDs.ToSkeleton(), nil - } - - // if no preferred EN ID is found, then choose from the fixed EN IDs - if len(fixedENIdentifiers) > 0 { - // choose fixed ENs which have executed the transaction - chosenIDs := allENs.Filter(filter.And( - filter.HasNodeID[flow.Identity](fixedENIdentifiers...), - filter.HasNodeID[flow.Identity](executorIDs...), - )) - if len(chosenIDs) > 0 { - return chosenIDs.ToSkeleton(), nil - } - // if no such ENs are found, then just choose all fixed ENs - chosenIDs = allENs.Filter(filter.HasNodeID[flow.Identity](fixedENIdentifiers...)) - return chosenIDs.ToSkeleton(), nil - } - - // if no preferred or fixed ENs have been specified, then return all executor IDs i.e., no preference at all - return allENs.Filter(filter.HasNodeID[flow.Identity](executorIDs...)).ToSkeleton(), nil -} - -// chooseFromPreferredENIDs finds the subset of execution nodes if preferred execution nodes are defined. -// If preferredENIdentifiers is set and there are less than maxNodesCnt nodes selected, than the list is padded up to -// maxNodesCnt nodes using the following order: -// 1. Use any EN with a receipt. -// 2. Use any preferred node not already selected. -// 3. Use any EN not already selected. -func chooseFromPreferredENIDs(allENs flow.IdentityList, executorIDs flow.IdentifierList) flow.IdentityList { - var chosenIDs flow.IdentityList - - // filter for both preferred and executor IDs - chosenIDs = allENs.Filter(filter.And( - filter.HasNodeID[flow.Identity](preferredENIdentifiers...), - filter.HasNodeID[flow.Identity](executorIDs...), - )) - - if len(chosenIDs) >= maxNodesCnt { - return chosenIDs - } - - // function to add nodes to chosenIDs if they are not already included - addIfNotExists := func(candidates flow.IdentityList) { - for _, en := range candidates { - _, exists := chosenIDs.ByNodeID(en.NodeID) - if !exists { - chosenIDs = append(chosenIDs, en) - if len(chosenIDs) >= maxNodesCnt { - return - } - } - } - } - - // add any EN with a receipt - receiptENs := allENs.Filter(filter.HasNodeID[flow.Identity](executorIDs...)) - addIfNotExists(receiptENs) - if len(chosenIDs) >= maxNodesCnt { - return chosenIDs - } - - // add any preferred node not already selected - preferredENs := allENs.Filter(filter.HasNodeID[flow.Identity](preferredENIdentifiers...)) - addIfNotExists(preferredENs) - if len(chosenIDs) >= maxNodesCnt { - return chosenIDs - } - - // add any EN not already selected - addIfNotExists(allENs) - - return chosenIDs } diff --git a/engine/access/rpc/backend/backend_accounts.go b/engine/access/rpc/backend/backend_accounts.go index f0d16237c25..609d1c063a8 100644 --- a/engine/access/rpc/backend/backend_accounts.go +++ b/engine/access/rpc/backend/backend_accounts.go @@ -14,6 +14,7 @@ import ( "github.com/onflow/flow-go/engine/access/rpc/connection" "github.com/onflow/flow-go/engine/common/rpc" + commonrpc "github.com/onflow/flow-go/engine/common/rpc" "github.com/onflow/flow-go/engine/common/rpc/convert" fvmerrors "github.com/onflow/flow-go/fvm/errors" "github.com/onflow/flow-go/model/flow" @@ -24,14 +25,14 @@ import ( ) type backendAccounts struct { - log zerolog.Logger - state protocol.State - headers storage.Headers - executionReceipts storage.ExecutionReceipts - connFactory connection.ConnectionFactory - nodeCommunicator Communicator - scriptExecutor execution.ScriptExecutor - scriptExecMode IndexQueryMode + log zerolog.Logger + state protocol.State + headers storage.Headers + connFactory connection.ConnectionFactory + nodeCommunicator Communicator + scriptExecutor execution.ScriptExecutor + scriptExecMode IndexQueryMode + execNodeIdentitiesProvider *commonrpc.ExecutionNodeIdentitiesProvider } // GetAccount returns the account details at the latest sealed block. @@ -68,7 +69,7 @@ func (b *backendAccounts) GetAccountAtBlockHeight( ) (*flow.Account, error) { blockID, err := b.headers.BlockIDByHeight(height) if err != nil { - return nil, rpc.ConvertStorageError(err) + return nil, rpc.ConvertStorageError(resolveHeightError(b.state.Params(), height, err)) } account, err := b.getAccountAtBlock(ctx, address, blockID, height) @@ -108,7 +109,7 @@ func (b *backendAccounts) GetAccountBalanceAtBlockHeight( ) (uint64, error) { blockID, err := b.headers.BlockIDByHeight(height) if err != nil { - return 0, rpc.ConvertStorageError(err) + return 0, rpc.ConvertStorageError(resolveHeightError(b.state.Params(), height, err)) } balance, err := b.getAccountBalanceAtBlock(ctx, address, blockID, height) @@ -176,7 +177,7 @@ func (b *backendAccounts) GetAccountKeyAtBlockHeight( ) (*flow.AccountPublicKey, error) { blockID, err := b.headers.BlockIDByHeight(height) if err != nil { - return nil, rpc.ConvertStorageError(err) + return nil, rpc.ConvertStorageError(resolveHeightError(b.state.Params(), height, err)) } accountKey, err := b.getAccountKeyAtBlock(ctx, address, keyIndex, blockID, height) @@ -196,7 +197,7 @@ func (b *backendAccounts) GetAccountKeysAtBlockHeight( ) ([]flow.AccountPublicKey, error) { blockID, err := b.headers.BlockIDByHeight(height) if err != nil { - return nil, rpc.ConvertStorageError(err) + return nil, rpc.ConvertStorageError(resolveHeightError(b.state.Params(), height, err)) } accountKeys, err := b.getAccountKeysAtBlock(ctx, address, blockID, height) @@ -400,7 +401,7 @@ func (b *backendAccounts) getAccountFromLocalStorage( // make sure data is available for the requested block account, err := b.scriptExecutor.GetAccountAtBlockHeight(ctx, address, height) if err != nil { - return nil, convertAccountError(err, address, height) + return nil, convertAccountError(resolveHeightError(b.state.Params(), height, err), address, height) } return account, nil } @@ -419,7 +420,10 @@ func (b *backendAccounts) getAccountFromAnyExeNode( BlockId: blockID[:], } - execNodes, err := executionNodesForBlockID(ctx, blockID, b.executionReceipts, b.state, b.log) + execNodes, err := b.execNodeIdentitiesProvider.ExecutionNodesForBlockID( + ctx, + blockID, + ) if err != nil { return nil, rpc.ConvertError(err, "failed to find execution node to query", codes.Internal) } diff --git a/engine/access/rpc/backend/backend_accounts_test.go b/engine/access/rpc/backend/backend_accounts_test.go index 50ca272da03..129b5679e33 100644 --- a/engine/access/rpc/backend/backend_accounts_test.go +++ b/engine/access/rpc/backend/backend_accounts_test.go @@ -13,6 +13,7 @@ import ( access "github.com/onflow/flow-go/engine/access/mock" connectionmock "github.com/onflow/flow-go/engine/access/rpc/connection/mock" + commonrpc "github.com/onflow/flow-go/engine/common/rpc" "github.com/onflow/flow-go/engine/common/rpc/convert" "github.com/onflow/flow-go/model/flow" execmock "github.com/onflow/flow-go/module/execution/mock" @@ -21,6 +22,7 @@ import ( "github.com/onflow/flow-go/storage" storagemock "github.com/onflow/flow-go/storage/mock" "github.com/onflow/flow-go/utils/unittest" + "github.com/onflow/flow-go/utils/unittest/mocks" execproto "github.com/onflow/flow/protobuf/go/flow/execution" ) @@ -77,12 +79,18 @@ func (s *BackendAccountsSuite) SetupTest() { func (s *BackendAccountsSuite) defaultBackend() *backendAccounts { return &backendAccounts{ - log: s.log, - state: s.state, - headers: s.headers, - executionReceipts: s.receipts, - connFactory: s.connectionFactory, - nodeCommunicator: NewNodeCommunicator(false), + log: s.log, + state: s.state, + headers: s.headers, + connFactory: s.connectionFactory, + nodeCommunicator: NewNodeCommunicator(false), + execNodeIdentitiesProvider: commonrpc.NewExecutionNodeIdentitiesProvider( + s.log, + s.state, + s.receipts, + flow.IdentifierList{}, + flow.IdentifierList{}, + ), } } @@ -100,7 +108,7 @@ func (s *BackendAccountsSuite) setupExecutionNodes(block *flow.Block) { s.receipts.On("ByBlockID", block.ID()).Return(receipts, nil) s.connectionFactory.On("GetExecutionAPIClient", mock.Anything). - Return(s.execClient, &mockCloser{}, nil) + Return(s.execClient, &mocks.MockCloser{}, nil) } // setupENSuccessResponse configures the execution node client to return a successful response @@ -238,6 +246,8 @@ func (s *BackendAccountsSuite) TestGetAccountFromStorage_Fails() { scriptExecutor.On("GetAccountAtBlockHeight", mock.Anything, s.failingAddress, s.block.Header.Height). Return(nil, tt.err).Times(3) + s.state.On("Params").Return(s.params).Times(3) + s.Run(fmt.Sprintf("GetAccount - fails with %v", tt.err), func() { s.testGetAccount(ctx, backend, tt.statusCode) }) @@ -247,6 +257,9 @@ func (s *BackendAccountsSuite) TestGetAccountFromStorage_Fails() { }) s.Run(fmt.Sprintf("GetAccountAtBlockHeight - fails with %v", tt.err), func() { + s.params.On("SporkRootBlockHeight").Return(s.block.Header.Height-10, nil) + s.params.On("SealedRoot").Return(s.block.Header, nil) + s.testGetAccountAtBlockHeight(ctx, backend, tt.statusCode) }) } @@ -279,6 +292,9 @@ func (s *BackendAccountsSuite) TestGetAccountFromFailover_HappyPath() { }) s.Run(fmt.Sprintf("GetAccountAtBlockHeight - happy path - recovers %v", errToReturn), func() { + s.params.On("SporkRootBlockHeight").Return(s.block.Header.Height-10, nil) + s.params.On("SealedRoot").Return(s.block.Header, nil) + s.testGetAccountAtBlockHeight(ctx, backend, codes.OK) }) } diff --git a/engine/access/rpc/backend/backend_block_details.go b/engine/access/rpc/backend/backend_block_details.go index 698e66b0727..c0a21f97b76 100644 --- a/engine/access/rpc/backend/backend_block_details.go +++ b/engine/access/rpc/backend/backend_block_details.go @@ -80,7 +80,7 @@ func (b *backendBlockDetails) GetBlockByID(ctx context.Context, id flow.Identifi func (b *backendBlockDetails) GetBlockByHeight(ctx context.Context, height uint64) (*flow.Block, flow.BlockStatus, error) { block, err := b.blocks.ByHeight(height) if err != nil { - return nil, flow.BlockStatusUnknown, rpc.ConvertStorageError(err) + return nil, flow.BlockStatusUnknown, rpc.ConvertStorageError(resolveHeightError(b.state.Params(), height, err)) } stat, err := b.getBlockStatus(ctx, block) diff --git a/engine/access/rpc/backend/backend_block_headers.go b/engine/access/rpc/backend/backend_block_headers.go index a61fcab711a..d77bdc57819 100644 --- a/engine/access/rpc/backend/backend_block_headers.go +++ b/engine/access/rpc/backend/backend_block_headers.go @@ -69,7 +69,7 @@ func (b *backendBlockHeaders) GetBlockHeaderByID(ctx context.Context, id flow.Id func (b *backendBlockHeaders) GetBlockHeaderByHeight(ctx context.Context, height uint64) (*flow.Header, flow.BlockStatus, error) { header, err := b.headers.ByHeight(height) if err != nil { - return nil, flow.BlockStatusUnknown, rpc.ConvertStorageError(err) + return nil, flow.BlockStatusUnknown, rpc.ConvertStorageError(resolveHeightError(b.state.Params(), height, err)) } stat, err := b.getBlockStatus(ctx, header) diff --git a/engine/access/rpc/backend/backend_events.go b/engine/access/rpc/backend/backend_events.go index 2928e22aa7a..56f3207e8b7 100644 --- a/engine/access/rpc/backend/backend_events.go +++ b/engine/access/rpc/backend/backend_events.go @@ -28,16 +28,16 @@ import ( ) type backendEvents struct { - headers storage.Headers - executionReceipts storage.ExecutionReceipts - state protocol.State - chain flow.Chain - connFactory connection.ConnectionFactory - log zerolog.Logger - maxHeightRange uint - nodeCommunicator Communicator - queryMode IndexQueryMode - eventsIndex *index.EventsIndex + headers storage.Headers + state protocol.State + chain flow.Chain + connFactory connection.ConnectionFactory + log zerolog.Logger + maxHeightRange uint + nodeCommunicator Communicator + queryMode IndexQueryMode + eventsIndex *index.EventsIndex + execNodeIdentitiesProvider *rpc.ExecutionNodeIdentitiesProvider } // blockMetadata is used to capture information about requested blocks to avoid repeated blockID @@ -104,7 +104,7 @@ func (b *backendEvents) GetEventsForHeightRange( // and avoids calculating header.ID() for each block. blockID, err := b.headers.BlockIDByHeight(i) if err != nil { - return nil, rpc.ConvertStorageError(fmt.Errorf("failed to get blockID for %d: %w", i, err)) + return nil, rpc.ConvertStorageError(resolveHeightError(b.state.Params(), i, err)) } header, err := b.headers.ByBlockID(blockID) if err != nil { @@ -303,7 +303,10 @@ func (b *backendEvents) getBlockEventsFromExecutionNode( // choose the last block ID to find the list of execution nodes lastBlockID := blockIDs[len(blockIDs)-1] - execNodes, err := executionNodesForBlockID(ctx, lastBlockID, b.executionReceipts, b.state, b.log) + execNodes, err := b.execNodeIdentitiesProvider.ExecutionNodesForBlockID( + ctx, + lastBlockID, + ) if err != nil { return nil, rpc.ConvertError(err, "failed to retrieve events from execution node", codes.Internal) } diff --git a/engine/access/rpc/backend/backend_events_test.go b/engine/access/rpc/backend/backend_events_test.go index b1b1a8c438d..11b2d52ea99 100644 --- a/engine/access/rpc/backend/backend_events_test.go +++ b/engine/access/rpc/backend/backend_events_test.go @@ -21,6 +21,7 @@ import ( "github.com/onflow/flow-go/engine/access/index" access "github.com/onflow/flow-go/engine/access/mock" connectionmock "github.com/onflow/flow-go/engine/access/rpc/connection/mock" + commonrpc "github.com/onflow/flow-go/engine/common/rpc" "github.com/onflow/flow-go/engine/common/rpc/convert" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module/irrecoverable" @@ -30,6 +31,7 @@ import ( storagemock "github.com/onflow/flow-go/storage/mock" "github.com/onflow/flow-go/utils/unittest" "github.com/onflow/flow-go/utils/unittest/generator" + "github.com/onflow/flow-go/utils/unittest/mocks" ) var targetEvent string @@ -175,16 +177,22 @@ func (s *BackendEventsSuite) SetupTest() { func (s *BackendEventsSuite) defaultBackend() *backendEvents { return &backendEvents{ - log: s.log, - chain: s.chainID.Chain(), - state: s.state, - headers: s.headers, - executionReceipts: s.receipts, - connFactory: s.connectionFactory, - nodeCommunicator: NewNodeCommunicator(false), - maxHeightRange: DefaultMaxHeightRange, - queryMode: IndexQueryModeExecutionNodesOnly, - eventsIndex: s.eventsIndex, + log: s.log, + chain: s.chainID.Chain(), + state: s.state, + headers: s.headers, + connFactory: s.connectionFactory, + nodeCommunicator: NewNodeCommunicator(false), + maxHeightRange: DefaultMaxHeightRange, + queryMode: IndexQueryModeExecutionNodesOnly, + eventsIndex: s.eventsIndex, + execNodeIdentitiesProvider: commonrpc.NewExecutionNodeIdentitiesProvider( + s.log, + s.state, + s.receipts, + flow.IdentifierList{}, + flow.IdentifierList{}, + ), } } @@ -202,7 +210,7 @@ func (s *BackendEventsSuite) setupExecutionNodes(block *flow.Block) { s.receipts.On("ByBlockID", block.ID()).Return(receipts, nil) s.connectionFactory.On("GetExecutionAPIClient", mock.Anything). - Return(s.execClient, &mockCloser{}, nil) + Return(s.execClient, &mocks.MockCloser{}, nil) } // setupENSuccessResponse configures the execution node client to return a successful response @@ -431,6 +439,39 @@ func (s *BackendEventsSuite) TestGetEventsForHeightRange_HandlesErrors() { s.Assert().Equal(codes.OutOfRange, status.Code(err)) s.Assert().Nil(response) }) + + s.state.On("Params").Return(s.params) + + s.Run("returns error for startHeight < spork root height", func() { + backend := s.defaultBackend() + + sporkRootHeight := s.blocks[0].Header.Height - 10 + startHeight := sporkRootHeight - 1 + + s.params.On("SporkRootBlockHeight").Return(sporkRootHeight).Once() + s.params.On("SealedRoot").Return(s.rootHeader, nil).Once() + + response, err := backend.GetEventsForHeightRange(ctx, targetEvent, startHeight, endHeight, encoding) + s.Assert().Equal(codes.NotFound, status.Code(err)) + s.Assert().ErrorContains(err, "Try to use a historic node") + s.Assert().Nil(response) + }) + + s.Run("returns error for startHeight < node root height", func() { + backend := s.defaultBackend() + + sporkRootHeight := s.blocks[0].Header.Height - 10 + nodeRootHeader := unittest.BlockHeaderWithHeight(s.blocks[0].Header.Height) + startHeight := nodeRootHeader.Height - 5 + + s.params.On("SporkRootBlockHeight").Return(sporkRootHeight).Once() + s.params.On("SealedRoot").Return(nodeRootHeader, nil).Once() + + response, err := backend.GetEventsForHeightRange(ctx, targetEvent, startHeight, endHeight, encoding) + s.Assert().Equal(codes.NotFound, status.Code(err)) + s.Assert().ErrorContains(err, "Try to use a different Access node") + s.Assert().Nil(response) + }) } func (s *BackendEventsSuite) TestGetEventsForBlockIDs_HandlesErrors() { diff --git a/engine/access/rpc/backend/backend_scripts.go b/engine/access/rpc/backend/backend_scripts.go index 2578245f02b..b0237447d46 100644 --- a/engine/access/rpc/backend/backend_scripts.go +++ b/engine/access/rpc/backend/backend_scripts.go @@ -13,6 +13,7 @@ import ( "github.com/onflow/flow-go/engine/access/rpc/connection" "github.com/onflow/flow-go/engine/common/rpc" + commonrpc "github.com/onflow/flow-go/engine/common/rpc" fvmerrors "github.com/onflow/flow-go/fvm/errors" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" @@ -27,16 +28,16 @@ import ( const uniqueScriptLoggingTimeWindow = 10 * time.Minute type backendScripts struct { - log zerolog.Logger - headers storage.Headers - executionReceipts storage.ExecutionReceipts - state protocol.State - connFactory connection.ConnectionFactory - metrics module.BackendScriptsMetrics - loggedScripts *lru.Cache[[md5.Size]byte, time.Time] - nodeCommunicator Communicator - scriptExecutor execution.ScriptExecutor - scriptExecMode IndexQueryMode + log zerolog.Logger + headers storage.Headers + state protocol.State + connFactory connection.ConnectionFactory + metrics module.BackendScriptsMetrics + loggedScripts *lru.Cache[[md5.Size]byte, time.Time] + nodeCommunicator Communicator + scriptExecutor execution.ScriptExecutor + scriptExecMode IndexQueryMode + execNodeIdentitiesProvider *commonrpc.ExecutionNodeIdentitiesProvider } // scriptExecutionRequest encapsulates the data needed to execute a script to make it easier @@ -104,7 +105,7 @@ func (b *backendScripts) ExecuteScriptAtBlockHeight( ) ([]byte, error) { header, err := b.headers.ByHeight(blockHeight) if err != nil { - return nil, rpc.ConvertStorageError(err) + return nil, rpc.ConvertStorageError(resolveHeightError(b.state.Params(), blockHeight, err)) } return b.executeScript(ctx, newScriptExecutionRequest(header.ID(), blockHeight, script, arguments)) @@ -224,7 +225,7 @@ func (b *backendScripts) executeScriptOnAvailableExecutionNodes( r *scriptExecutionRequest, ) ([]byte, time.Duration, error) { // find few execution nodes which have executed the block earlier and provided an execution receipt for it - executors, err := executionNodesForBlockID(ctx, r.blockID, b.executionReceipts, b.state, b.log) + executors, err := b.execNodeIdentitiesProvider.ExecutionNodesForBlockID(ctx, r.blockID) if err != nil { return nil, 0, status.Errorf(codes.Internal, "failed to find script executors at blockId %v: %v", r.blockID.String(), err) } diff --git a/engine/access/rpc/backend/backend_scripts_test.go b/engine/access/rpc/backend/backend_scripts_test.go index 398d9ac33ec..43abac07a62 100644 --- a/engine/access/rpc/backend/backend_scripts_test.go +++ b/engine/access/rpc/backend/backend_scripts_test.go @@ -18,6 +18,7 @@ import ( access "github.com/onflow/flow-go/engine/access/mock" connectionmock "github.com/onflow/flow-go/engine/access/rpc/connection/mock" + commonrpc "github.com/onflow/flow-go/engine/common/rpc" fvmerrors "github.com/onflow/flow-go/fvm/errors" "github.com/onflow/flow-go/model/flow" execmock "github.com/onflow/flow-go/module/execution/mock" @@ -27,6 +28,7 @@ import ( "github.com/onflow/flow-go/storage" storagemock "github.com/onflow/flow-go/storage/mock" "github.com/onflow/flow-go/utils/unittest" + "github.com/onflow/flow-go/utils/unittest/mocks" ) var ( @@ -96,14 +98,20 @@ func (s *BackendScriptsSuite) defaultBackend() *backendScripts { s.Require().NoError(err) return &backendScripts{ - log: s.log, - metrics: metrics.NewNoopCollector(), - state: s.state, - headers: s.headers, - executionReceipts: s.receipts, - loggedScripts: loggedScripts, - connFactory: s.connectionFactory, - nodeCommunicator: NewNodeCommunicator(false), + log: s.log, + metrics: metrics.NewNoopCollector(), + state: s.state, + headers: s.headers, + loggedScripts: loggedScripts, + connFactory: s.connectionFactory, + nodeCommunicator: NewNodeCommunicator(false), + execNodeIdentitiesProvider: commonrpc.NewExecutionNodeIdentitiesProvider( + s.log, + s.state, + s.receipts, + flow.IdentifierList{}, + flow.IdentifierList{}, + ), } } @@ -121,7 +129,7 @@ func (s *BackendScriptsSuite) setupExecutionNodes(block *flow.Block) { s.receipts.On("ByBlockID", block.ID()).Return(receipts, nil) s.connectionFactory.On("GetExecutionAPIClient", mock.Anything). - Return(s.execClient, &mockCloser{}, nil) + Return(s.execClient, &mocks.MockCloser{}, nil) } // setupENSuccessResponse configures the execution client mock to return a successful response diff --git a/engine/access/rpc/backend/backend_stream_blocks_test.go b/engine/access/rpc/backend/backend_stream_blocks_test.go index 69aa9e67823..6e655c8afda 100644 --- a/engine/access/rpc/backend/backend_stream_blocks_test.go +++ b/engine/access/rpc/backend/backend_stream_blocks_test.go @@ -148,15 +148,14 @@ func (s *BackendBlocksSuite) SetupTest() { // backendParams returns the Params configuration for the backend. func (s *BackendBlocksSuite) backendParams() Params { return Params{ - State: s.state, - Blocks: s.blocks, - Headers: s.headers, - ChainID: s.chainID, - MaxHeightRange: DefaultMaxHeightRange, - SnapshotHistoryLimit: DefaultSnapshotHistoryLimit, - AccessMetrics: metrics.NewNoopCollector(), - Log: s.log, - TxErrorMessagesCacheSize: 1000, + State: s.state, + Blocks: s.blocks, + Headers: s.headers, + ChainID: s.chainID, + MaxHeightRange: DefaultMaxHeightRange, + SnapshotHistoryLimit: DefaultSnapshotHistoryLimit, + AccessMetrics: metrics.NewNoopCollector(), + Log: s.log, SubscriptionHandler: subscription.NewSubscriptionHandler( s.log, s.broadcaster, diff --git a/engine/access/rpc/backend/backend_stream_transactions_test.go b/engine/access/rpc/backend/backend_stream_transactions_test.go index ec8ff353bf3..24cdf601f17 100644 --- a/engine/access/rpc/backend/backend_stream_transactions_test.go +++ b/engine/access/rpc/backend/backend_stream_transactions_test.go @@ -192,22 +192,21 @@ func (s *TransactionStatusSuite) TearDownTest() { // backendParams returns the Params configuration for the backend. func (s *TransactionStatusSuite) backendParams() Params { return Params{ - State: s.state, - Blocks: s.blocks, - Headers: s.headers, - Collections: s.collections, - Transactions: s.transactions, - ExecutionReceipts: s.receipts, - ExecutionResults: s.results, - ChainID: s.chainID, - CollectionRPC: s.colClient, - MaxHeightRange: DefaultMaxHeightRange, - SnapshotHistoryLimit: DefaultSnapshotHistoryLimit, - Communicator: NewNodeCommunicator(false), - AccessMetrics: metrics.NewNoopCollector(), - Log: s.log, - TxErrorMessagesCacheSize: 1000, - BlockTracker: s.blockTracker, + State: s.state, + Blocks: s.blocks, + Headers: s.headers, + Collections: s.collections, + Transactions: s.transactions, + ExecutionReceipts: s.receipts, + ExecutionResults: s.results, + ChainID: s.chainID, + CollectionRPC: s.colClient, + MaxHeightRange: DefaultMaxHeightRange, + SnapshotHistoryLimit: DefaultSnapshotHistoryLimit, + Communicator: NewNodeCommunicator(false), + AccessMetrics: metrics.NewNoopCollector(), + Log: s.log, + BlockTracker: s.blockTracker, SubscriptionHandler: subscription.NewSubscriptionHandler( s.log, s.broadcaster, diff --git a/engine/access/rpc/backend/backend_test.go b/engine/access/rpc/backend/backend_test.go index 4974cbe6ce7..a3068e32645 100644 --- a/engine/access/rpc/backend/backend_test.go +++ b/engine/access/rpc/backend/backend_test.go @@ -17,7 +17,6 @@ import ( execproto "github.com/onflow/flow/protobuf/go/flow/execution" "github.com/rs/zerolog" "github.com/sony/gobreaker" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" @@ -30,6 +29,7 @@ import ( backendmock "github.com/onflow/flow-go/engine/access/rpc/backend/mock" "github.com/onflow/flow-go/engine/access/rpc/connection" connectionmock "github.com/onflow/flow-go/engine/access/rpc/connection/mock" + commonrpc "github.com/onflow/flow-go/engine/common/rpc" "github.com/onflow/flow-go/engine/common/rpc/convert" "github.com/onflow/flow-go/engine/common/version" "github.com/onflow/flow-go/fvm/blueprints" @@ -49,6 +49,7 @@ import ( storagemock "github.com/onflow/flow-go/storage/mock" "github.com/onflow/flow-go/utils/unittest" "github.com/onflow/flow-go/utils/unittest/generator" + "github.com/onflow/flow-go/utils/unittest/mocks" ) const TEST_MAX_HEIGHT = 100 @@ -73,6 +74,7 @@ type Suite struct { results *storagemock.ExecutionResults transactionResults *storagemock.LightTransactionResults events *storagemock.Events + txErrorMessages *storagemock.TransactionResultErrorMessages db *badger.DB dbDir string @@ -88,6 +90,9 @@ type Suite struct { chainID flow.ChainID systemTx *flow.TransactionBody + + fixedExecutionNodeIDs flow.IdentifierList + preferredExecutionNodeIDs flow.IdentifierList } func TestHandler(t *testing.T) { @@ -113,6 +118,7 @@ func (suite *Suite) SetupTest() { suite.collections = new(storagemock.Collections) suite.receipts = new(storagemock.ExecutionReceipts) suite.results = new(storagemock.ExecutionResults) + suite.txErrorMessages = storagemock.NewTransactionResultErrorMessages(suite.T()) suite.colClient = new(accessmock.AccessAPIClient) suite.execClient = new(accessmock.ExecutionAPIClient) suite.transactionResults = storagemock.NewLightTransactionResults(suite.T()) @@ -910,10 +916,6 @@ func (suite *Suite) TestGetTransactionResultByIndex() { suite.state.On("Final").Return(suite.snapshot, nil).Maybe() suite.snapshot.On("Identities", mock.Anything).Return(fixedENIDs, nil) - // create a mock connection factory - connFactory := connectionmock.NewConnectionFactory(suite.T()) - connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mockCloser{}, nil) - exeEventReq := &execproto.GetTransactionByIndexRequest{ BlockId: blockId[:], Index: index, @@ -923,10 +925,11 @@ func (suite *Suite) TestGetTransactionResultByIndex() { Events: nil, } + suite.fixedExecutionNodeIDs = fixedENIDs.NodeIDs() + params := suite.defaultBackendParams() // the connection factory should be used to get the execution node client - params.ConnFactory = connFactory - params.FixedExecutionNodeIDs = (fixedENIDs.NodeIDs()).Strings() + params.ConnFactory = suite.setupConnectionFactory() backend, err := New(params) suite.Require().NoError(err) @@ -964,7 +967,6 @@ func (suite *Suite) TestGetTransactionResultsByBlockID() { suite.state.On("Sealed").Return(suite.snapshot, nil).Maybe() ctx := context.Background() - params := suite.defaultBackendParams() block := unittest.BlockFixture() sporkRootBlockHeight := suite.state.Params().SporkRootBlockHeight() @@ -980,10 +982,6 @@ func (suite *Suite) TestGetTransactionResultsByBlockID() { suite.state.On("Final").Return(suite.snapshot, nil).Maybe() suite.snapshot.On("Identities", mock.Anything).Return(fixedENIDs, nil) - // create a mock connection factory - connFactory := connectionmock.NewConnectionFactory(suite.T()) - connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mockCloser{}, nil) - exeEventReq := &execproto.GetTransactionsByBlockIDRequest{ BlockId: blockId[:], } @@ -992,9 +990,11 @@ func (suite *Suite) TestGetTransactionResultsByBlockID() { TransactionResults: []*execproto.GetTransactionResultResponse{{}}, } + suite.fixedExecutionNodeIDs = fixedENIDs.NodeIDs() + + params := suite.defaultBackendParams() // the connection factory should be used to get the execution node client - params.ConnFactory = connFactory - params.FixedExecutionNodeIDs = (fixedENIDs.NodeIDs()).Strings() + params.ConnFactory = suite.setupConnectionFactory() backend, err := New(params) suite.Require().NoError(err) @@ -1073,10 +1073,6 @@ func (suite *Suite) TestTransactionStatusTransition() { suite.state.On("Final").Return(suite.snapshot, nil).Maybe() suite.snapshot.On("Identities", mock.Anything).Return(fixedENIDs, nil) - // create a mock connection factory - connFactory := connectionmock.NewConnectionFactory(suite.T()) - connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mockCloser{}, nil) - exeEventReq := &execproto.GetTransactionResultRequest{ BlockId: blockID[:], TransactionId: txID[:], @@ -1086,10 +1082,11 @@ func (suite *Suite) TestTransactionStatusTransition() { Events: nil, } + suite.fixedExecutionNodeIDs = fixedENIDs.NodeIDs() + params := suite.defaultBackendParams() // the connection factory should be used to get the execution node client - params.ConnFactory = connFactory - params.FixedExecutionNodeIDs = (fixedENIDs.NodeIDs()).Strings() + params.ConnFactory = suite.setupConnectionFactory() backend, err := New(params) suite.Require().NoError(err) @@ -1342,12 +1339,11 @@ func (suite *Suite) TestTransactionPendingToFinalizedStatusTransition() { params := suite.defaultBackendParams() params.ConnFactory = connFactory params.MaxHeightRange = TEST_MAX_HEIGHT + suite.preferredExecutionNodeIDs = flow.IdentifierList{receipts[0].ExecutorID} backend, err := New(params) suite.Require().NoError(err) - preferredENIdentifiers = flow.IdentifierList{receipts[0].ExecutorID} - // should return pending status when we have not observed collection for the transaction suite.Run("pending", func() { currentState = flow.TransactionStatusPending @@ -1452,10 +1448,6 @@ func (suite *Suite) TestGetLatestFinalizedBlock() { }) } -type mockCloser struct{} - -func (mc *mockCloser) Close() error { return nil } - func (suite *Suite) TestGetExecutionResultByID() { suite.state.On("Sealed").Return(suite.snapshot, nil).Maybe() @@ -1482,10 +1474,11 @@ func (suite *Suite) TestGetExecutionResultByID() { Return(executionResult, nil) suite.Run("nonexisting execution result for id", func() { + suite.fixedExecutionNodeIDs = validENIDs + params := suite.defaultBackendParams() params.ExecutionResults = results params.ConnFactory = connFactory - params.FixedExecutionNodeIDs = validENIDs.Strings() backend, err := New(params) suite.Require().NoError(err) @@ -1493,14 +1486,15 @@ func (suite *Suite) TestGetExecutionResultByID() { // execute request _, err = backend.GetExecutionResultByID(ctx, nonexistingID) - assert.Error(suite.T(), err) + suite.Assert().Error(err) }) suite.Run("existing execution result id", func() { + suite.fixedExecutionNodeIDs = validENIDs + params := suite.defaultBackendParams() params.ExecutionResults = results params.ConnFactory = connFactory - params.FixedExecutionNodeIDs = validENIDs.Strings() backend, err := New(params) suite.Require().NoError(err) @@ -1544,10 +1538,11 @@ func (suite *Suite) TestGetExecutionResultByBlockID() { Return(executionResult, nil) suite.Run("nonexisting execution results", func() { + suite.fixedExecutionNodeIDs = validENIDs + params := suite.defaultBackendParams() params.ExecutionResults = results params.ConnFactory = connFactory - params.FixedExecutionNodeIDs = validENIDs.Strings() backend, err := New(params) suite.Require().NoError(err) @@ -1555,14 +1550,15 @@ func (suite *Suite) TestGetExecutionResultByBlockID() { // execute request _, err = backend.GetExecutionResultForBlockID(ctx, nonexistingBlockID) - assert.Error(suite.T(), err) + suite.Assert().Error(err) }) suite.Run("existing execution results", func() { + suite.fixedExecutionNodeIDs = validENIDs + params := suite.defaultBackendParams() params.ExecutionResults = results params.ConnFactory = connFactory - params.FixedExecutionNodeIDs = validENIDs.Strings() backend, err := New(params) suite.Require().NoError(err) @@ -1731,199 +1727,6 @@ func (suite *Suite) TestGetNetworkParameters() { suite.Require().Equal(expectedChainID, actual.ChainID) } -// TestExecutionNodesForBlockID tests the common method backend.executionNodesForBlockID used for serving all API calls -// that need to talk to an execution node. -func (suite *Suite) TestExecutionNodesForBlockID() { - - totalReceipts := 5 - - block := unittest.BlockFixture() - - // generate one execution node identities for each receipt assuming that each ER is generated by a unique exec node - allExecutionNodes := unittest.IdentityListFixture(totalReceipts, unittest.WithRole(flow.RoleExecution)) - - // one execution result for all receipts for this block - executionResult := unittest.ExecutionResultFixture() - - // generate execution receipts - receipts := make(flow.ExecutionReceiptList, totalReceipts) - for j := 0; j < totalReceipts; j++ { - r := unittest.ReceiptForBlockFixture(&block) - r.ExecutorID = allExecutionNodes[j].NodeID - er := *executionResult - r.ExecutionResult = er - receipts[j] = r - } - - currentAttempt := 0 - attempt1Receipts, attempt2Receipts, attempt3Receipts := receipts, receipts, receipts - - // setup receipts storage mock to return different list of receipts on each call - suite.receipts. - On("ByBlockID", block.ID()).Return( - func(id flow.Identifier) flow.ExecutionReceiptList { - switch currentAttempt { - case 0: - currentAttempt++ - return attempt1Receipts - case 1: - currentAttempt++ - return attempt2Receipts - default: - currentAttempt = 0 - return attempt3Receipts - } - }, - func(id flow.Identifier) error { return nil }) - - suite.snapshot.On("Identities", mock.Anything).Return( - func(filter flow.IdentityFilter[flow.Identity]) flow.IdentityList { - // apply the filter passed in to the list of all the execution nodes - return allExecutionNodes.Filter(filter) - }, - func(flow.IdentityFilter[flow.Identity]) error { return nil }) - suite.state.On("Final").Return(suite.snapshot, nil).Maybe() - - testExecutionNodesForBlockID := func(preferredENs, fixedENs, expectedENs flow.IdentityList) { - - if preferredENs != nil { - preferredENIdentifiers = preferredENs.NodeIDs() - } - if fixedENs != nil { - fixedENIdentifiers = fixedENs.NodeIDs() - } - - if expectedENs == nil { - expectedENs = flow.IdentityList{} - } - - allExecNodes, err := executionNodesForBlockID(context.Background(), block.ID(), suite.receipts, suite.state, suite.log) - require.NoError(suite.T(), err) - - execNodeSelectorFactory := NodeSelectorFactory{circuitBreakerEnabled: false} - execSelector, err := execNodeSelectorFactory.SelectNodes(allExecNodes) - require.NoError(suite.T(), err) - - actualList := flow.IdentitySkeletonList{} - for actual := execSelector.Next(); actual != nil; actual = execSelector.Next() { - actualList = append(actualList, actual) - } - - { - expectedENs := expectedENs.ToSkeleton() - if len(expectedENs) > maxNodesCnt { - for _, actual := range actualList { - require.Contains(suite.T(), expectedENs, actual) - } - } else { - require.ElementsMatch(suite.T(), actualList, expectedENs) - } - } - } - // if we don't find sufficient receipts, executionNodesForBlockID should return a list of random ENs - suite.Run("insufficient receipts return random ENs in State", func() { - // return no receipts at all attempts - attempt1Receipts = flow.ExecutionReceiptList{} - attempt2Receipts = flow.ExecutionReceiptList{} - attempt3Receipts = flow.ExecutionReceiptList{} - suite.state.On("AtBlockID", mock.Anything).Return(suite.snapshot) - - allExecNodes, err := executionNodesForBlockID(context.Background(), block.ID(), suite.receipts, suite.state, suite.log) - require.NoError(suite.T(), err) - - execNodeSelectorFactory := NodeSelectorFactory{circuitBreakerEnabled: false} - execSelector, err := execNodeSelectorFactory.SelectNodes(allExecNodes) - require.NoError(suite.T(), err) - - actualList := flow.IdentitySkeletonList{} - for actual := execSelector.Next(); actual != nil; actual = execSelector.Next() { - actualList = append(actualList, actual) - } - - require.Equal(suite.T(), len(actualList), maxNodesCnt) - }) - - // if no preferred or fixed ENs are specified, the ExecutionNodesForBlockID function should - // return the exe node list without a filter - suite.Run("no preferred or fixed ENs", func() { - testExecutionNodesForBlockID(nil, nil, allExecutionNodes) - }) - // if only fixed ENs are specified, the ExecutionNodesForBlockID function should - // return the fixed ENs list - suite.Run("two fixed ENs with zero preferred EN", func() { - // mark the first two ENs as fixed - fixedENs := allExecutionNodes[0:2] - expectedList := fixedENs - testExecutionNodesForBlockID(nil, fixedENs, expectedList) - }) - // if only preferred ENs are specified, the ExecutionNodesForBlockID function should - // return the preferred ENs list - suite.Run("two preferred ENs with zero fixed EN", func() { - // mark the first two ENs as preferred - preferredENs := allExecutionNodes[0:2] - expectedList := allExecutionNodes[0:maxNodesCnt] - testExecutionNodesForBlockID(preferredENs, nil, expectedList) - }) - // if both are specified, the ExecutionNodesForBlockID function should - // return the preferred ENs list - suite.Run("four fixed ENs of which two are preferred ENs", func() { - // mark the first four ENs as fixed - fixedENs := allExecutionNodes[0:5] - // mark the first two of the fixed ENs as preferred ENs - preferredENs := fixedENs[0:2] - expectedList := fixedENs[0:maxNodesCnt] - testExecutionNodesForBlockID(preferredENs, fixedENs, expectedList) - }) - // if both are specified, but the preferred ENs don't match the ExecutorIDs in the ER, - // the ExecutionNodesForBlockID function should return the fixed ENs list - suite.Run("four fixed ENs of which two are preferred ENs but have not generated the ER", func() { - // mark the first two ENs as fixed - fixedENs := allExecutionNodes[0:2] - // specify two ENs not specified in the ERs as preferred - preferredENs := unittest.IdentityListFixture(2, unittest.WithRole(flow.RoleExecution)) - // add one more node ID besides of the fixed ENs list cause expected length of the list should be maxNodesCnt - expectedList := append(fixedENs, allExecutionNodes[2]) - testExecutionNodesForBlockID(preferredENs, fixedENs, expectedList) - }) - // if execution receipts are not yet available, the ExecutionNodesForBlockID function should retry twice - suite.Run("retry execution receipt query", func() { - // on first attempt, no execution receipts are available - attempt1Receipts = flow.ExecutionReceiptList{} - // on second attempt ony one is available - attempt2Receipts = flow.ExecutionReceiptList{receipts[0]} - // on third attempt all receipts are available - attempt3Receipts = receipts - currentAttempt = 0 - // mark the first two ENs as preferred - preferredENs := allExecutionNodes[0:2] - expectedList := allExecutionNodes[0:maxNodesCnt] - testExecutionNodesForBlockID(preferredENs, nil, expectedList) - }) - // if preferredENIdentifiers was set and there are less than maxNodesCnt nodes selected than check the order - // of adding ENs ids - suite.Run("add nodes in the correct order", func() { - // mark the first EN as preferred - preferredENIdentifiers = allExecutionNodes[0:1].NodeIDs() - // mark the fourth EN with receipt - executorIDs := allExecutionNodes[3:4].NodeIDs() - - receiptNodes := allExecutionNodes[3:4] // any EN with a receipt - preferredNodes := allExecutionNodes[0:1] // preferred EN node not already selected - additionalNode := allExecutionNodes[1:2] // any EN not already selected - - expectedOrder := flow.IdentityList{ - receiptNodes[0], - preferredNodes[0], - additionalNode[0], - } - - chosenIDs := chooseFromPreferredENIDs(allExecutionNodes, executorIDs) - - require.ElementsMatch(suite.T(), chosenIDs, expectedOrder) - require.Equal(suite.T(), len(chosenIDs), maxNodesCnt) - }) -} - // TestGetTransactionResultEventEncodingVersion tests the GetTransactionResult function with different event encoding versions. func (suite *Suite) TestGetTransactionResultEventEncodingVersion() { suite.state.On("Sealed").Return(suite.snapshot, nil).Maybe() @@ -1966,14 +1769,11 @@ func (suite *Suite) TestGetTransactionResultEventEncodingVersion() { suite.state.On("Final").Return(suite.snapshot, nil).Maybe() suite.snapshot.On("Identities", mock.Anything).Return(fixedENIDs, nil) - // create a mock connection factory - connFactory := connectionmock.NewConnectionFactory(suite.T()) - connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mockCloser{}, nil) + suite.fixedExecutionNodeIDs = fixedENIDs.NodeIDs() params := suite.defaultBackendParams() // the connection factory should be used to get the execution node client - params.ConnFactory = connFactory - params.FixedExecutionNodeIDs = (fixedENIDs.NodeIDs()).Strings() + params.ConnFactory = suite.setupConnectionFactory() backend, err := New(params) suite.Require().NoError(err) @@ -2032,14 +1832,11 @@ func (suite *Suite) TestGetTransactionResultByIndexAndBlockIdEventEncodingVersio suite.state.On("Final").Return(suite.snapshot, nil).Maybe() suite.snapshot.On("Identities", mock.Anything).Return(fixedENIDs, nil) - // create a mock connection factory - connFactory := connectionmock.NewConnectionFactory(suite.T()) - connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mockCloser{}, nil) + suite.fixedExecutionNodeIDs = fixedENIDs.NodeIDs() params := suite.defaultBackendParams() // the connection factory should be used to get the execution node client - params.ConnFactory = connFactory - params.FixedExecutionNodeIDs = (fixedENIDs.NodeIDs()).Strings() + params.ConnFactory = suite.setupConnectionFactory() backend, err := New(params) suite.Require().NoError(err) @@ -2132,20 +1929,17 @@ func (suite *Suite) TestNodeCommunicator() { suite.state.On("Final").Return(suite.snapshot, nil).Maybe() suite.snapshot.On("Identities", mock.Anything).Return(fixedENIDs, nil) - // create a mock connection factory - connFactory := connectionmock.NewConnectionFactory(suite.T()) - connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mockCloser{}, nil) - exeEventReq := &execproto.GetTransactionsByBlockIDRequest{ BlockId: blockId[:], } + // Left only one preferred execution node + suite.fixedExecutionNodeIDs = fixedENIDs.NodeIDs() + suite.preferredExecutionNodeIDs = flow.IdentifierList{fixedENIDs[0].NodeID} + params := suite.defaultBackendParams() // the connection factory should be used to get the execution node client - params.ConnFactory = connFactory - params.FixedExecutionNodeIDs = (fixedENIDs.NodeIDs()).Strings() - // Left only one preferred execution node - params.PreferredExecutionNodeIDs = []string{fixedENIDs[0].NodeID.String()} + params.ConnFactory = suite.setupConnectionFactory() backend, err := New(params) suite.Require().NoError(err) @@ -2195,7 +1989,7 @@ func (suite *Suite) setupReceipts(block *flow.Block) ([]*flow.ExecutionReceipt, func (suite *Suite) setupConnectionFactory() connection.ConnectionFactory { // create a mock connection factory connFactory := connectionmock.NewConnectionFactory(suite.T()) - connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mockCloser{}, nil) + connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mocks.MockCloser{}, nil) return connFactory } @@ -2220,24 +2014,107 @@ func generateEncodedEvents(t *testing.T, n int) ([]flow.Event, []flow.Event) { func (suite *Suite) defaultBackendParams() Params { return Params{ - State: suite.state, - Blocks: suite.blocks, - Headers: suite.headers, - Collections: suite.collections, - Transactions: suite.transactions, - ExecutionReceipts: suite.receipts, - ExecutionResults: suite.results, - ChainID: suite.chainID, - CollectionRPC: suite.colClient, - MaxHeightRange: DefaultMaxHeightRange, - SnapshotHistoryLimit: DefaultSnapshotHistoryLimit, - Communicator: NewNodeCommunicator(false), - AccessMetrics: metrics.NewNoopCollector(), - Log: suite.log, - TxErrorMessagesCacheSize: 1000, - BlockTracker: nil, - TxResultQueryMode: IndexQueryModeExecutionNodesOnly, - LastFullBlockHeight: suite.lastFullBlockHeight, - VersionControl: suite.versionControl, + State: suite.state, + Blocks: suite.blocks, + Headers: suite.headers, + Collections: suite.collections, + Transactions: suite.transactions, + ExecutionReceipts: suite.receipts, + ExecutionResults: suite.results, + ChainID: suite.chainID, + CollectionRPC: suite.colClient, + MaxHeightRange: DefaultMaxHeightRange, + SnapshotHistoryLimit: DefaultSnapshotHistoryLimit, + Communicator: NewNodeCommunicator(false), + AccessMetrics: metrics.NewNoopCollector(), + Log: suite.log, + BlockTracker: nil, + TxResultQueryMode: IndexQueryModeExecutionNodesOnly, + LastFullBlockHeight: suite.lastFullBlockHeight, + VersionControl: suite.versionControl, + ExecNodeIdentitiesProvider: commonrpc.NewExecutionNodeIdentitiesProvider( + suite.log, + suite.state, + suite.receipts, + suite.preferredExecutionNodeIDs, + suite.fixedExecutionNodeIDs, + ), + } +} + +// TestResolveHeightError tests the resolveHeightError function for various scenarios where the block height +// is below the spork root height, below the node root height, above the node root height, or when a different +// error is provided. It validates that resolveHeightError returns an appropriate error message for each case. +// +// Test cases: +// 1) If height is below the spork root height, it suggests using a historic node. +// 2) If height is below the node root height, it suggests using a different Access node. +// 3) If height is above the node root height, it returns the original error without modification. +// 4) If a non-storage-related error is provided, it returns the error as is. +func (suite *Suite) TestResolveHeightError() { + tests := []struct { + name string + height uint64 + sporkRootHeight uint64 + nodeRootHeight uint64 + genericErr error + expectedErrorMsg string + expectOriginalErr bool + }{ + { + name: "height below spork root height", + height: uint64(50), + sporkRootHeight: uint64(100), + nodeRootHeight: uint64(200), + genericErr: storage.ErrNotFound, + expectedErrorMsg: "block height %d is less than the spork root block height 100. Try to use a historic node: %v"}, + { + name: "height below node root height", + height: uint64(150), + sporkRootHeight: uint64(100), + nodeRootHeight: uint64(200), + genericErr: storage.ErrNotFound, + expectedErrorMsg: "block height %d is less than the node's root block height 200. Try to use a different Access node: %v", + expectOriginalErr: false, + }, + { + name: "height above node root height", + height: uint64(205), + sporkRootHeight: uint64(100), + nodeRootHeight: uint64(200), + genericErr: storage.ErrNotFound, + expectedErrorMsg: "%v", + expectOriginalErr: true, + }, + { + name: "non-storage related error", + height: uint64(150), + sporkRootHeight: uint64(100), + nodeRootHeight: uint64(200), + genericErr: fmt.Errorf("some other error"), + expectedErrorMsg: "%v", + expectOriginalErr: true, + }, + } + + for _, test := range tests { + suite.T().Run(test.name, func(t *testing.T) { + stateParams := protocol.NewParams(suite.T()) + + if errors.Is(test.genericErr, storage.ErrNotFound) { + stateParams.On("SporkRootBlockHeight").Return(test.sporkRootHeight).Once() + sealedRootHeader := unittest.BlockHeaderWithHeight(test.nodeRootHeight) + stateParams.On("SealedRoot").Return(sealedRootHeader, nil).Once() + } + + err := resolveHeightError(stateParams, test.height, test.genericErr) + + if test.expectOriginalErr { + suite.Assert().True(errors.Is(err, test.genericErr)) + } else { + expectedError := fmt.Sprintf(test.expectedErrorMsg, test.height, test.genericErr) + suite.Assert().Equal(err.Error(), expectedError) + } + }) } } diff --git a/engine/access/rpc/backend/backend_transactions.go b/engine/access/rpc/backend/backend_transactions.go index 7436863f9af..5ba1cecb038 100644 --- a/engine/access/rpc/backend/backend_transactions.go +++ b/engine/access/rpc/backend/backend_transactions.go @@ -17,6 +17,7 @@ import ( "github.com/onflow/flow-go/access" "github.com/onflow/flow-go/engine/access/rpc/connection" "github.com/onflow/flow-go/engine/common/rpc" + commonrpc "github.com/onflow/flow-go/engine/common/rpc" "github.com/onflow/flow-go/engine/common/rpc/convert" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" @@ -25,26 +26,31 @@ import ( "github.com/onflow/flow-go/storage" ) +const DefaultFailedErrorMessage = "failed" + type backendTransactions struct { *TransactionsLocalDataProvider - staticCollectionRPC accessproto.AccessAPIClient // rpc client tied to a fixed collection node - transactions storage.Transactions - executionReceipts storage.ExecutionReceipts - chainID flow.ChainID - transactionMetrics module.TransactionMetrics - transactionValidator *access.TransactionValidator - retry *Retry - connFactory connection.ConnectionFactory - - previousAccessNodes []accessproto.AccessAPIClient - log zerolog.Logger - nodeCommunicator Communicator - txResultCache *lru.Cache[flow.Identifier, *access.TransactionResult] - txErrorMessagesCache *lru.Cache[flow.Identifier, string] // cache for transactions error messages, indexed by hash(block_id, tx_id). - txResultQueryMode IndexQueryMode - - systemTxID flow.Identifier - systemTx *flow.TransactionBody + staticCollectionRPC accessproto.AccessAPIClient // rpc client tied to a fixed collection node + transactions storage.Transactions + // NOTE: The transaction error message is currently only used by the access node and not by the observer node. + // To avoid introducing unnecessary command line arguments in the observer, one case could be that the error + // message cache is nil for the observer node. + txResultErrorMessages storage.TransactionResultErrorMessages + chainID flow.ChainID + transactionMetrics module.TransactionMetrics + transactionValidator *access.TransactionValidator + retry *Retry + connFactory connection.ConnectionFactory + + previousAccessNodes []accessproto.AccessAPIClient + log zerolog.Logger + nodeCommunicator Communicator + txResultCache *lru.Cache[flow.Identifier, *access.TransactionResult] + txResultQueryMode IndexQueryMode + + systemTxID flow.Identifier + systemTx *flow.TransactionBody + execNodeIdentitiesProvider *commonrpc.ExecutionNodeIdentitiesProvider } var _ TransactionErrorMessage = (*backendTransactions)(nil) @@ -405,7 +411,10 @@ func (b *backendTransactions) getTransactionResultsByBlockIDFromExecutionNode( BlockId: blockID[:], } - execNodes, err := executionNodesForBlockID(ctx, blockID, b.executionReceipts, b.state, b.log) + execNodes, err := b.execNodeIdentitiesProvider.ExecutionNodesForBlockID( + ctx, + blockID, + ) if err != nil { if IsInsufficientExecutionReceipts(err) { return nil, status.Errorf(codes.NotFound, err.Error()) @@ -559,7 +568,10 @@ func (b *backendTransactions) getTransactionResultByIndexFromExecutionNode( Index: index, } - execNodes, err := executionNodesForBlockID(ctx, blockID, b.executionReceipts, b.state, b.log) + execNodes, err := b.execNodeIdentitiesProvider.ExecutionNodesForBlockID( + ctx, + blockID, + ) if err != nil { if IsInsufficientExecutionReceipts(err) { return nil, status.Errorf(codes.NotFound, err.Error()) @@ -743,7 +755,10 @@ func (b *backendTransactions) getTransactionResultFromExecutionNode( TransactionId: transactionID[:], } - execNodes, err := executionNodesForBlockID(ctx, blockID, b.executionReceipts, b.state, b.log) + execNodes, err := b.execNodeIdentitiesProvider.ExecutionNodesForBlockID( + ctx, + blockID, + ) if err != nil { // if no execution receipt were found, return a NotFound GRPC error if IsInsufficientExecutionReceipts(err) { @@ -962,28 +977,29 @@ func (b *backendTransactions) tryGetTransactionResultByIndex( } // LookupErrorMessageByTransactionID returns transaction error message for specified transaction. -// If an error message for transaction can be found in the cache then it will be used to serve the request, otherwise -// an RPC call will be made to the EN to fetch that error message, fetched value will be cached in the LRU cache. +// If transaction error messages are stored locally, they will be checked first in local storage. +// If error messages are not stored locally, an RPC call will be made to the EN to fetch message. +// // Expected errors during normal operation: -// - InsufficientExecutionReceipts - found insufficient receipts for given block ID. +// - InsufficientExecutionReceipts - found insufficient receipts for the given block ID. // - status.Error - remote GRPC call to EN has failed. func (b *backendTransactions) LookupErrorMessageByTransactionID( ctx context.Context, blockID flow.Identifier, + height uint64, transactionID flow.Identifier, ) (string, error) { - var cacheKey flow.Identifier - var value string - - if b.txErrorMessagesCache != nil { - cacheKey = flow.MakeIDFromFingerPrint(append(blockID[:], transactionID[:]...)) - value, cached := b.txErrorMessagesCache.Get(cacheKey) - if cached { - return value, nil + if b.txResultErrorMessages != nil { + res, err := b.txResultErrorMessages.ByBlockIDTransactionID(blockID, transactionID) + if err == nil { + return res.ErrorMessage, nil } } - execNodes, err := executionNodesForBlockID(ctx, blockID, b.executionReceipts, b.state, b.log) + execNodes, err := b.execNodeIdentitiesProvider.ExecutionNodesForBlockID( + ctx, + blockID, + ) if err != nil { if IsInsufficientExecutionReceipts(err) { return "", status.Errorf(codes.NotFound, err.Error()) @@ -997,23 +1013,30 @@ func (b *backendTransactions) LookupErrorMessageByTransactionID( resp, err := b.getTransactionErrorMessageFromAnyEN(ctx, execNodes, req) if err != nil { - return "", fmt.Errorf("could not fetch error message from ENs: %w", err) - } - value = resp.ErrorMessage + // If no execution nodes return a valid response, + // return a static message "failed". + txResult, err := b.txResultsIndex.ByBlockIDTransactionID(blockID, height, transactionID) + if err != nil { + return "", rpc.ConvertStorageError(err) + } - if b.txErrorMessagesCache != nil { - b.txErrorMessagesCache.Add(cacheKey, value) + if txResult.Failed { + return DefaultFailedErrorMessage, nil + } + + // in case tx result is not failed + return "", nil } - return value, nil + return resp.ErrorMessage, nil } -// LookupErrorMessageByIndex returns transaction error message for specified transaction using its index. -// If an error message for transaction can be found in cache then it will be used to serve the request, otherwise -// an RPC call will be made to the EN to fetch that error message, fetched value will be cached in the LRU cache. +// LookupErrorMessageByIndex returns the transaction error message for a specified transaction using its index. +// If transaction error messages are stored locally, they will be checked first in local storage. +// If error messages are not stored locally, an RPC call will be made to the EN to fetch message. +// // Expected errors during normal operation: -// - status.Error[codes.NotFound] - transaction result for given block ID and tx index is not available. -// - InsufficientExecutionReceipts - found insufficient receipts for given block ID. +// - InsufficientExecutionReceipts - found insufficient receipts for the given block ID. // - status.Error - remote GRPC call to EN has failed. func (b *backendTransactions) LookupErrorMessageByIndex( ctx context.Context, @@ -1021,23 +1044,17 @@ func (b *backendTransactions) LookupErrorMessageByIndex( height uint64, index uint32, ) (string, error) { - txResult, err := b.txResultsIndex.ByBlockIDTransactionIndex(blockID, height, index) - if err != nil { - return "", rpc.ConvertStorageError(err) - } - - var cacheKey flow.Identifier - var value string - - if b.txErrorMessagesCache != nil { - cacheKey = flow.MakeIDFromFingerPrint(append(blockID[:], txResult.TransactionID[:]...)) - value, cached := b.txErrorMessagesCache.Get(cacheKey) - if cached { - return value, nil + if b.txResultErrorMessages != nil { + res, err := b.txResultErrorMessages.ByBlockIDTransactionIndex(blockID, index) + if err == nil { + return res.ErrorMessage, nil } } - execNodes, err := executionNodesForBlockID(ctx, blockID, b.executionReceipts, b.state, b.log) + execNodes, err := b.execNodeIdentitiesProvider.ExecutionNodesForBlockID( + ctx, + blockID, + ) if err != nil { if IsInsufficientExecutionReceipts(err) { return "", status.Errorf(codes.NotFound, err.Error()) @@ -1051,55 +1068,53 @@ func (b *backendTransactions) LookupErrorMessageByIndex( resp, err := b.getTransactionErrorMessageByIndexFromAnyEN(ctx, execNodes, req) if err != nil { - return "", fmt.Errorf("could not fetch error message from ENs: %w", err) - } - value = resp.ErrorMessage + // If no execution nodes return a valid response, + // return a static message "failed" + txResult, err := b.txResultsIndex.ByBlockIDTransactionIndex(blockID, height, index) + if err != nil { + return "", rpc.ConvertStorageError(err) + } + + if txResult.Failed { + return DefaultFailedErrorMessage, nil + } - if b.txErrorMessagesCache != nil { - b.txErrorMessagesCache.Add(cacheKey, value) + // in case tx result is not failed + return "", nil } - return value, nil + return resp.ErrorMessage, nil } // LookupErrorMessagesByBlockID returns all error messages for failed transactions by blockID. -// An RPC call will be made to the EN to fetch missing errors messages, fetched value will be cached in the LRU cache. +// If transaction error messages are stored locally, they will be checked first in local storage. +// If error messages are not stored locally, an RPC call will be made to the EN to fetch messages. +// // Expected errors during normal operation: -// - status.Error[codes.NotFound] - transaction results for given block ID are not available. -// - InsufficientExecutionReceipts - found insufficient receipts for given block ID. +// - InsufficientExecutionReceipts - found insufficient receipts for the given block ID. // - status.Error - remote GRPC call to EN has failed. func (b *backendTransactions) LookupErrorMessagesByBlockID( ctx context.Context, blockID flow.Identifier, height uint64, ) (map[flow.Identifier]string, error) { - txResults, err := b.txResultsIndex.ByBlockID(blockID, height) - if err != nil { - return nil, rpc.ConvertStorageError(err) - } + result := make(map[flow.Identifier]string) - results := make(map[flow.Identifier]string) - - if b.txErrorMessagesCache != nil { - needToFetch := false - for _, txResult := range txResults { - if txResult.Failed { - cacheKey := flow.MakeIDFromFingerPrint(append(blockID[:], txResult.TransactionID[:]...)) - if value, ok := b.txErrorMessagesCache.Get(cacheKey); ok { - results[txResult.TransactionID] = value - } else { - needToFetch = true - } + if b.txResultErrorMessages != nil { + res, err := b.txResultErrorMessages.ByBlockID(blockID) + if err == nil { + for _, value := range res { + result[value.TransactionID] = value.ErrorMessage } - } - // all transactions were served from cache or there were no failed transactions - if !needToFetch { - return results, nil + return result, nil } } - execNodes, err := executionNodesForBlockID(ctx, blockID, b.executionReceipts, b.state, b.log) + execNodes, err := b.execNodeIdentitiesProvider.ExecutionNodesForBlockID( + ctx, + blockID, + ) if err != nil { if IsInsufficientExecutionReceipts(err) { return nil, status.Errorf(codes.NotFound, err.Error()) @@ -1110,18 +1125,28 @@ func (b *backendTransactions) LookupErrorMessagesByBlockID( BlockId: convert.IdentifierToMessage(blockID), } - resp, err := b.getTransactionErrorMessagesFromAnyEN(ctx, execNodes, req) + resp, _, err := b.GetTransactionErrorMessagesFromAnyEN(ctx, execNodes, req) if err != nil { - return nil, fmt.Errorf("could not fetch error message from ENs: %w", err) + // If no execution nodes return a valid response, + // return a static message "failed" + txResults, err := b.txResultsIndex.ByBlockID(blockID, height) + if err != nil { + return nil, rpc.ConvertStorageError(err) + } + + for _, txResult := range txResults { + if txResult.Failed { + result[txResult.TransactionID] = DefaultFailedErrorMessage + } + } + + return result, nil } - result := make(map[flow.Identifier]string, len(resp)) + for _, value := range resp { - if b.txErrorMessagesCache != nil { - cacheKey := flow.MakeIDFromFingerPrint(append(req.BlockId, value.TransactionId...)) - b.txErrorMessagesCache.Add(cacheKey, value.ErrorMessage) - } result[convert.MessageToIdentifier(value.TransactionId)] = value.ErrorMessage } + return result, nil } @@ -1209,26 +1234,29 @@ func (b *backendTransactions) getTransactionErrorMessageByIndexFromAnyEN( return resp, nil } -// getTransactionErrorMessagesFromAnyEN performs an RPC call using available nodes passed as argument. List of nodes must be non-empty otherwise an error will be returned. +// GetTransactionErrorMessagesFromAnyEN performs an RPC call using available nodes passed as argument. List of nodes must be non-empty otherwise an error will be returned. // Expected errors during normal operation: // - status.Error - GRPC call failed, some of possible codes are: // - codes.NotFound - request cannot be served by EN because of absence of data. // - codes.Unavailable - remote node is not unavailable. -func (b *backendTransactions) getTransactionErrorMessagesFromAnyEN( +func (b *backendTransactions) GetTransactionErrorMessagesFromAnyEN( ctx context.Context, execNodes flow.IdentitySkeletonList, req *execproto.GetTransactionErrorMessagesByBlockIDRequest, -) ([]*execproto.GetTransactionErrorMessagesResponse_Result, error) { +) ([]*execproto.GetTransactionErrorMessagesResponse_Result, *flow.IdentitySkeleton, error) { // if we were passed 0 execution nodes add a specific error if len(execNodes) == 0 { - return nil, errors.New("zero execution nodes") + return nil, nil, errors.New("zero execution nodes") } var resp *execproto.GetTransactionErrorMessagesResponse + var execNode *flow.IdentitySkeleton + errToReturn := b.nodeCommunicator.CallAvailableNode( execNodes, func(node *flow.IdentitySkeleton) error { var err error + execNode = node resp, err = b.tryGetTransactionErrorMessagesByBlockIDFromEN(ctx, node, req) if err == nil { b.log.Debug(). @@ -1245,10 +1273,10 @@ func (b *backendTransactions) getTransactionErrorMessagesFromAnyEN( // log the errors if errToReturn != nil { b.log.Err(errToReturn).Msg("failed to get transaction error messages from execution nodes") - return nil, errToReturn + return nil, nil, errToReturn } - return resp.GetResults(), nil + return resp.GetResults(), execNode, nil } // Expected errors during normal operation: diff --git a/engine/access/rpc/backend/backend_transactions_test.go b/engine/access/rpc/backend/backend_transactions_test.go index 5d1c513cef8..65062d0606c 100644 --- a/engine/access/rpc/backend/backend_transactions_test.go +++ b/engine/access/rpc/backend/backend_transactions_test.go @@ -29,6 +29,7 @@ import ( "github.com/onflow/flow-go/storage" "github.com/onflow/flow-go/utils/unittest" "github.com/onflow/flow-go/utils/unittest/generator" + "github.com/onflow/flow-go/utils/unittest/mocks" ) const expectedErrorMsg = "expected test error" @@ -338,96 +339,193 @@ func (suite *Suite) TestGetTransactionResultUnknownFromCache() { }) } -// TestLookupTransactionErrorMessage_HappyPath tests lookup of a transaction error message. In a happy path if it wasn't found in the cache, it -// has to be fetched from the execution node, otherwise served from the cache. -// If the transaction has not failed, the error message must be empty. -func (suite *Suite) TestLookupTransactionErrorMessage_HappyPath() { +// TestLookupTransactionErrorMessageByTransactionID_HappyPath verifies the lookup of a transaction error message +// by block id and transaction id. +// It tests two cases: +// 1. Happy path where the error message is fetched from the EN if it's not found in the cache. +// 2. Happy path where the error message is served from the storage database if it exists. +func (suite *Suite) TestLookupTransactionErrorMessageByTransactionID_HappyPath() { block := unittest.BlockFixture() blockId := block.ID() failedTx := unittest.TransactionFixture() failedTxId := failedTx.ID() + failedTxIndex := rand.Uint32() + // Setup mock receipts and execution node identities. _, fixedENIDs := suite.setupReceipts(&block) suite.state.On("Final").Return(suite.snapshot, nil).Maybe() suite.snapshot.On("Identities", mock.Anything).Return(fixedENIDs, nil) - // create a mock connection factory - connFactory := connectionmock.NewConnectionFactory(suite.T()) - connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mockCloser{}, nil) + suite.fixedExecutionNodeIDs = fixedENIDs.NodeIDs() params := suite.defaultBackendParams() - // the connection factory should be used to get the execution node client - params.ConnFactory = connFactory - params.FixedExecutionNodeIDs = fixedENIDs.NodeIDs().Strings() + params.TxResultErrorMessages = suite.txErrorMessages - backend, err := New(params) - suite.Require().NoError(err) + // Test case: transaction error message is fetched from the EN. + suite.Run("happy path from EN", func() { + // the connection factory should be used to get the execution node client + params.ConnFactory = suite.setupConnectionFactory() - expectedErrorMsg := "some error" + // Mock the cache lookup for the transaction error message, returning "not found". + suite.txErrorMessages.On("ByBlockIDTransactionID", blockId, failedTxId). + Return(nil, storage.ErrNotFound).Once() - exeEventReq := &execproto.GetTransactionErrorMessageRequest{ - BlockId: blockId[:], - TransactionId: failedTxId[:], - } + backend, err := New(params) + suite.Require().NoError(err) - exeEventResp := &execproto.GetTransactionErrorMessageResponse{ - TransactionId: failedTxId[:], - ErrorMessage: expectedErrorMsg, - } + // Mock the execution node API call to fetch the error message. + exeEventReq := &execproto.GetTransactionErrorMessageRequest{ + BlockId: blockId[:], + TransactionId: failedTxId[:], + } + exeEventResp := &execproto.GetTransactionErrorMessageResponse{ + TransactionId: failedTxId[:], + ErrorMessage: expectedErrorMsg, + } + suite.execClient.On("GetTransactionErrorMessage", mock.Anything, exeEventReq).Return(exeEventResp, nil).Once() - suite.execClient.On("GetTransactionErrorMessage", mock.Anything, exeEventReq).Return(exeEventResp, nil).Once() + // Perform the lookup and assert that the error message is retrieved correctly. + errMsg, err := backend.LookupErrorMessageByTransactionID(context.Background(), blockId, block.Header.Height, failedTxId) + suite.Require().NoError(err) + suite.Require().Equal(expectedErrorMsg, errMsg) + suite.assertAllExpectations() + }) - errMsg, err := backend.LookupErrorMessageByTransactionID(context.Background(), blockId, failedTxId) - suite.Require().NoError(err) - suite.Require().Equal(expectedErrorMsg, errMsg) + // Test case: transaction error message is fetched from the storage database. + suite.Run("happy path from storage db", func() { + backend, err := New(params) + suite.Require().NoError(err) - // ensure the transaction error message is cached after retrieval; we do this by mocking the grpc call - // only once - errMsg, err = backend.LookupErrorMessageByTransactionID(context.Background(), blockId, failedTxId) - suite.Require().NoError(err) - suite.Require().Equal(expectedErrorMsg, errMsg) - suite.assertAllExpectations() + // Mock the cache lookup for the transaction error message, returning a stored result. + suite.txErrorMessages.On("ByBlockIDTransactionID", blockId, failedTxId). + Return(&flow.TransactionResultErrorMessage{ + TransactionID: failedTxId, + ErrorMessage: expectedErrorMsg, + Index: failedTxIndex, + ExecutorID: unittest.IdentifierFixture(), + }, nil).Once() + + // Perform the lookup and assert that the error message is retrieved correctly from storage. + errMsg, err := backend.LookupErrorMessageByTransactionID(context.Background(), blockId, block.Header.Height, failedTxId) + suite.Require().NoError(err) + suite.Require().Equal(expectedErrorMsg, errMsg) + suite.assertAllExpectations() + }) } -// TestLookupTransactionErrorMessage_FailedToFetch tests lookup of a transaction error message, when a transaction result -// is not in the cache and needs to be fetched from EN, but the EN fails to return it. -func (suite *Suite) TestLookupTransactionErrorMessage_FailedToFetch() { +// TestLookupTransactionErrorMessageByTransactionID_FailedToFetch tests the case when a transaction error message +// is not in the cache and needs to be fetched from the EN, but the EN fails to return it. +// It tests three cases: +// 1. The transaction is not found in the transaction results, leading to a "NotFound" error. +// 2. The transaction result is not failed, and the error message is empty. +// 3. The transaction result is failed, and the error message "failed" are returned. +func (suite *Suite) TestLookupTransactionErrorMessageByTransactionID_FailedToFetch() { block := unittest.BlockFixture() blockId := block.ID() failedTx := unittest.TransactionFixture() failedTxId := failedTx.ID() + // Setup mock receipts and execution node identities. _, fixedENIDs := suite.setupReceipts(&block) suite.state.On("Final").Return(suite.snapshot, nil).Maybe() suite.snapshot.On("Identities", mock.Anything).Return(fixedENIDs, nil) - // create a mock connection factory - connFactory := connectionmock.NewConnectionFactory(suite.T()) - connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mockCloser{}, nil) + // Create a mock index reporter + reporter := syncmock.NewIndexReporter(suite.T()) + reporter.On("LowestIndexedHeight").Return(block.Header.Height, nil) + reporter.On("HighestIndexedHeight").Return(block.Header.Height+10, nil) + + suite.fixedExecutionNodeIDs = fixedENIDs.NodeIDs() params := suite.defaultBackendParams() - // the connection factory should be used to get the execution node client - params.ConnFactory = connFactory - params.FixedExecutionNodeIDs = fixedENIDs.NodeIDs().Strings() + // The connection factory should be used to get the execution node client + params.ConnFactory = suite.setupConnectionFactory() + // Initialize the transaction results index with the mock reporter. + params.TxResultsIndex = index.NewTransactionResultsIndex(index.NewReporter(), suite.transactionResults) + err := params.TxResultsIndex.Initialize(reporter) + suite.Require().NoError(err) + + params.TxResultErrorMessages = suite.txErrorMessages backend, err := New(params) suite.Require().NoError(err) - // lookup should try each of the 2 ENs in fixedENIDs - suite.execClient.On("GetTransactionErrorMessage", mock.Anything, mock.Anything).Return(nil, - status.Error(codes.Unavailable, "")).Twice() + // Test case: failed to fetch from EN, transaction is unknown. + suite.Run("failed to fetch from EN, unknown tx", func() { + // lookup should try each of the 2 ENs in fixedENIDs + suite.execClient.On("GetTransactionErrorMessage", mock.Anything, mock.Anything).Return(nil, + status.Error(codes.Unavailable, "")).Twice() - errMsg, err := backend.LookupErrorMessageByTransactionID(context.Background(), blockId, failedTxId) - suite.Require().Error(err) - suite.Require().Equal(codes.Unavailable, status.Code(err)) - suite.Require().Empty(errMsg) + // Setup mock that the transaction and tx error message is not found in the storage. + suite.txErrorMessages.On("ByBlockIDTransactionID", blockId, failedTxId). + Return(nil, storage.ErrNotFound).Once() + suite.transactionResults.On("ByBlockIDTransactionID", blockId, failedTxId). + Return(nil, storage.ErrNotFound).Once() + + // Perform the lookup and expect a "NotFound" error with an empty error message. + errMsg, err := backend.LookupErrorMessageByTransactionID(context.Background(), blockId, block.Header.Height, failedTxId) + suite.Require().Error(err) + suite.Require().Equal(codes.NotFound, status.Code(err)) + suite.Require().Empty(errMsg) + suite.assertAllExpectations() + }) + + // Test case: failed to fetch from EN, but the transaction result is not failed. + suite.Run("failed to fetch from EN, tx result is not failed", func() { + // Lookup should try each of the 2 ENs in fixedENIDs + suite.execClient.On("GetTransactionErrorMessage", mock.Anything, mock.Anything).Return(nil, + status.Error(codes.Unavailable, "")).Twice() + + // Setup mock that the transaction error message is not found in storage. + suite.txErrorMessages.On("ByBlockIDTransactionID", blockId, failedTxId). + Return(nil, storage.ErrNotFound).Once() + + // Setup mock that the transaction result exists and is not failed. + suite.transactionResults.On("ByBlockIDTransactionID", blockId, failedTxId). + Return(&flow.LightTransactionResult{ + TransactionID: failedTxId, + Failed: false, + ComputationUsed: 0, + }, nil).Once() + + // Perform the lookup and expect no error and an empty error message. + errMsg, err := backend.LookupErrorMessageByTransactionID(context.Background(), blockId, block.Header.Height, failedTxId) + suite.Require().NoError(err) + suite.Require().Empty(errMsg) + suite.assertAllExpectations() + }) - suite.assertAllExpectations() + // Test case: failed to fetch from EN, but the transaction result is failed. + suite.Run("failed to fetch from EN, tx result is failed", func() { + // lookup should try each of the 2 ENs in fixedENIDs + suite.execClient.On("GetTransactionErrorMessage", mock.Anything, mock.Anything).Return(nil, + status.Error(codes.Unavailable, "")).Twice() + + // Setup mock that the transaction error message is not found in storage. + suite.txErrorMessages.On("ByBlockIDTransactionID", blockId, failedTxId). + Return(nil, storage.ErrNotFound).Once() + + // Setup mock that the transaction result exists and is failed. + suite.transactionResults.On("ByBlockIDTransactionID", blockId, failedTxId). + Return(&flow.LightTransactionResult{ + TransactionID: failedTxId, + Failed: true, + ComputationUsed: 0, + }, nil).Once() + + // Perform the lookup and expect the failed error message to be returned. + errMsg, err := backend.LookupErrorMessageByTransactionID(context.Background(), blockId, block.Header.Height, failedTxId) + suite.Require().NoError(err) + suite.Require().Equal(errMsg, DefaultFailedErrorMessage) + suite.assertAllExpectations() + }) } -// TestLookupTransactionErrorMessageByIndex_HappyPath tests lookup of a transaction error message by index. -// In a happy path if it wasn't found in the cache, it has to be fetched from the execution node, otherwise served from the cache. -// If the transaction has not failed, the error message must be empty. +// TestLookupTransactionErrorMessageByIndex_HappyPath verifies the lookup of a transaction error message +// by block ID and transaction index. +// It tests two cases: +// 1. Happy path where the error message is fetched from the EN if it is not found in the cache. +// 2. Happy path where the error message is served from the storage database if it exists. func (suite *Suite) TestLookupTransactionErrorMessageByIndex_HappyPath() { block := unittest.BlockFixture() blockId := block.ID() @@ -435,154 +533,186 @@ func (suite *Suite) TestLookupTransactionErrorMessageByIndex_HappyPath() { failedTxId := failedTx.ID() failedTxIndex := rand.Uint32() - suite.transactionResults.On("ByBlockIDTransactionIndex", blockId, failedTxIndex). - Return(&flow.LightTransactionResult{ - TransactionID: failedTxId, - Failed: true, - ComputationUsed: 0, - }, nil).Twice() - + // Setup mock receipts and execution node identities. _, fixedENIDs := suite.setupReceipts(&block) suite.state.On("Final").Return(suite.snapshot, nil).Maybe() suite.snapshot.On("Identities", mock.Anything).Return(fixedENIDs, nil) - // create a mock connection factory - connFactory := connectionmock.NewConnectionFactory(suite.T()) - connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mockCloser{}, nil) - - // create a mock index reporter - reporter := syncmock.NewIndexReporter(suite.T()) - reporter.On("LowestIndexedHeight").Return(block.Header.Height, nil) - reporter.On("HighestIndexedHeight").Return(block.Header.Height+10, nil) + suite.fixedExecutionNodeIDs = fixedENIDs.NodeIDs() params := suite.defaultBackendParams() + params.TxResultErrorMessages = suite.txErrorMessages - // the connection factory should be used to get the execution node client - params.ConnFactory = connFactory - params.FixedExecutionNodeIDs = fixedENIDs.NodeIDs().Strings() - - params.TxResultsIndex = index.NewTransactionResultsIndex(index.NewReporter(), suite.transactionResults) - err := params.TxResultsIndex.Initialize(reporter) - suite.Require().NoError(err) - - backend, err := New(params) - suite.Require().NoError(err) - - expectedErrorMsg := "some error" - - exeEventReq := &execproto.GetTransactionErrorMessageByIndexRequest{ - BlockId: blockId[:], - Index: failedTxIndex, - } - - exeEventResp := &execproto.GetTransactionErrorMessageResponse{ - TransactionId: failedTxId[:], - ErrorMessage: expectedErrorMsg, - } - - suite.execClient.On("GetTransactionErrorMessageByIndex", mock.Anything, exeEventReq).Return(exeEventResp, nil).Once() - - errMsg, err := backend.LookupErrorMessageByIndex(context.Background(), blockId, block.Header.Height, failedTxIndex) - suite.Require().NoError(err) - suite.Require().Equal(expectedErrorMsg, errMsg) - - // ensure the transaction error message is cached after retrieval; we do this by mocking the grpc call - // only once - errMsg, err = backend.LookupErrorMessageByIndex(context.Background(), blockId, block.Header.Height, failedTxIndex) - suite.Require().NoError(err) - suite.Require().Equal(expectedErrorMsg, errMsg) - suite.assertAllExpectations() -} - -// TestLookupTransactionErrorMessageByIndex_UnknownTransaction tests lookup of a transaction error message by index, -// when a transaction result has not been synced yet, in this case nothing we can do but return an error. -func (suite *Suite) TestLookupTransactionErrorMessageByIndex_UnknownTransaction() { - block := unittest.BlockFixture() - blockId := block.ID() - failedTxIndex := rand.Uint32() - - suite.transactionResults.On("ByBlockIDTransactionIndex", blockId, failedTxIndex). - Return(nil, storage.ErrNotFound).Once() + // Test case: transaction error message is fetched from the EN. + suite.Run("happy path from EN", func() { + // the connection factory should be used to get the execution node client + params.ConnFactory = suite.setupConnectionFactory() - // create a mock index reporter - reporter := syncmock.NewIndexReporter(suite.T()) - reporter.On("LowestIndexedHeight").Return(block.Header.Height, nil) - reporter.On("HighestIndexedHeight").Return(block.Header.Height+10, nil) + // Mock the cache lookup for the transaction error message, returning "not found". + suite.txErrorMessages.On("ByBlockIDTransactionIndex", blockId, failedTxIndex). + Return(nil, storage.ErrNotFound).Once() - params := suite.defaultBackendParams() + backend, err := New(params) + suite.Require().NoError(err) - params.TxResultsIndex = index.NewTransactionResultsIndex(index.NewReporter(), suite.transactionResults) - err := params.TxResultsIndex.Initialize(reporter) - suite.Require().NoError(err) + // Mock the execution node API call to fetch the error message. + exeEventReq := &execproto.GetTransactionErrorMessageByIndexRequest{ + BlockId: blockId[:], + Index: failedTxIndex, + } + exeEventResp := &execproto.GetTransactionErrorMessageResponse{ + TransactionId: failedTxId[:], + ErrorMessage: expectedErrorMsg, + } + suite.execClient.On("GetTransactionErrorMessageByIndex", mock.Anything, exeEventReq).Return(exeEventResp, nil).Once() - backend, err := New(params) - suite.Require().NoError(err) + // Perform the lookup and assert that the error message is retrieved correctly. + errMsg, err := backend.LookupErrorMessageByIndex(context.Background(), blockId, block.Header.Height, failedTxIndex) + suite.Require().NoError(err) + suite.Require().Equal(expectedErrorMsg, errMsg) + suite.assertAllExpectations() + }) - errMsg, err := backend.LookupErrorMessageByIndex(context.Background(), blockId, block.Header.Height, failedTxIndex) - suite.Require().Error(err) - suite.Require().Equal(codes.NotFound, status.Code(err)) - suite.Require().Empty(errMsg) + // Test case: transaction error message is fetched from the storage database. + suite.Run("happy path from storage db", func() { + backend, err := New(params) + suite.Require().NoError(err) - suite.assertAllExpectations() + // Mock the cache lookup for the transaction error message, returning a stored result. + suite.txErrorMessages.On("ByBlockIDTransactionIndex", blockId, failedTxIndex). + Return(&flow.TransactionResultErrorMessage{ + TransactionID: failedTxId, + ErrorMessage: expectedErrorMsg, + Index: failedTxIndex, + ExecutorID: unittest.IdentifierFixture(), + }, nil).Once() + + // Perform the lookup and assert that the error message is retrieved correctly from storage. + errMsg, err := backend.LookupErrorMessageByIndex(context.Background(), blockId, block.Header.Height, failedTxIndex) + suite.Require().NoError(err) + suite.Require().Equal(expectedErrorMsg, errMsg) + suite.assertAllExpectations() + }) } -// TestLookupTransactionErrorMessageByIndex_FailedToFetch tests lookup of a transaction error message by index, -// when a transaction result is not in the cache and needs to be fetched from EN, but the EN fails to return it. +// TestLookupTransactionErrorMessageByIndex_FailedToFetch verifies the behavior of looking up a transaction error message by index +// when the error message is not in the cache, and fetching it from the EN fails. +// It tests three cases: +// 1. The transaction is not found in the transaction results, leading to a "NotFound" error. +// 2. The transaction result is not failed, and the error message is empty. +// 3. The transaction result is failed, and the error message "failed" are returned. func (suite *Suite) TestLookupTransactionErrorMessageByIndex_FailedToFetch() { block := unittest.BlockFixture() blockId := block.ID() + failedTxIndex := rand.Uint32() failedTx := unittest.TransactionFixture() failedTxId := failedTx.ID() - failedTxIndex := rand.Uint32() - - suite.transactionResults.On("ByBlockIDTransactionIndex", blockId, failedTxIndex). - Return(&flow.LightTransactionResult{ - TransactionID: failedTxId, - Failed: true, - ComputationUsed: 0, - }, nil).Once() + // Setup mock receipts and execution node identities. _, fixedENIDs := suite.setupReceipts(&block) suite.state.On("Final").Return(suite.snapshot, nil).Maybe() suite.snapshot.On("Identities", mock.Anything).Return(fixedENIDs, nil) - // create a mock connection factory + // Create a mock connection factory connFactory := connectionmock.NewConnectionFactory(suite.T()) - connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mockCloser{}, nil) + connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mocks.MockCloser{}, nil) - // create a mock index reporter + // Create a mock index reporter reporter := syncmock.NewIndexReporter(suite.T()) reporter.On("LowestIndexedHeight").Return(block.Header.Height, nil) reporter.On("HighestIndexedHeight").Return(block.Header.Height+10, nil) + suite.fixedExecutionNodeIDs = fixedENIDs.NodeIDs() + params := suite.defaultBackendParams() // the connection factory should be used to get the execution node client params.ConnFactory = connFactory - params.FixedExecutionNodeIDs = fixedENIDs.NodeIDs().Strings() - + // Initialize the transaction results index with the mock reporter. params.TxResultsIndex = index.NewTransactionResultsIndex(index.NewReporter(), suite.transactionResults) err := params.TxResultsIndex.Initialize(reporter) suite.Require().NoError(err) + params.TxResultErrorMessages = suite.txErrorMessages + backend, err := New(params) suite.Require().NoError(err) - // lookup should try each of the 2 ENs in fixedENIDs - suite.execClient.On("GetTransactionErrorMessageByIndex", mock.Anything, mock.Anything).Return(nil, - status.Error(codes.Unavailable, "")).Twice() + // Test case: failed to fetch from EN, transaction is unknown. + suite.Run("failed to fetch from EN, unknown tx", func() { + // lookup should try each of the 2 ENs in fixedENIDs + suite.execClient.On("GetTransactionErrorMessageByIndex", mock.Anything, mock.Anything).Return(nil, + status.Error(codes.Unavailable, "")).Twice() + + // Setup mock that the transaction and tx error message is not found in the storage. + suite.txErrorMessages.On("ByBlockIDTransactionIndex", blockId, failedTxIndex). + Return(nil, storage.ErrNotFound).Once() + suite.transactionResults.On("ByBlockIDTransactionIndex", blockId, failedTxIndex). + Return(nil, storage.ErrNotFound).Once() + + // Perform the lookup and expect a "NotFound" error with an empty error message. + errMsg, err := backend.LookupErrorMessageByIndex(context.Background(), blockId, block.Header.Height, failedTxIndex) + suite.Require().Error(err) + suite.Require().Equal(codes.NotFound, status.Code(err)) + suite.Require().Empty(errMsg) + suite.assertAllExpectations() + }) - errMsg, err := backend.LookupErrorMessageByIndex(context.Background(), blockId, block.Header.Height, failedTxIndex) - suite.Require().Error(err) - suite.Require().Equal(codes.Unavailable, status.Code(err)) - suite.Require().Empty(errMsg) + // Test case: failed to fetch from EN, but the transaction result is not failed. + suite.Run("failed to fetch from EN, tx result is not failed", func() { + // lookup should try each of the 2 ENs in fixedENIDs + suite.execClient.On("GetTransactionErrorMessageByIndex", mock.Anything, mock.Anything).Return(nil, + status.Error(codes.Unavailable, "")).Twice() + + // Setup mock that the transaction error message is not found in storage. + suite.txErrorMessages.On("ByBlockIDTransactionIndex", blockId, failedTxIndex). + Return(nil, storage.ErrNotFound).Once() + + // Setup mock that the transaction result exists and is not failed. + suite.transactionResults.On("ByBlockIDTransactionIndex", blockId, failedTxIndex). + Return(&flow.LightTransactionResult{ + TransactionID: failedTxId, + Failed: false, + ComputationUsed: 0, + }, nil).Once() + + // Perform the lookup and expect no error and an empty error message. + errMsg, err := backend.LookupErrorMessageByIndex(context.Background(), blockId, block.Header.Height, failedTxIndex) + suite.Require().NoError(err) + suite.Require().Empty(errMsg) + suite.assertAllExpectations() + }) - suite.assertAllExpectations() + // Test case: failed to fetch from EN, but the transaction result is failed. + suite.Run("failed to fetch from EN, tx result is failed", func() { + // lookup should try each of the 2 ENs in fixedENIDs + suite.execClient.On("GetTransactionErrorMessageByIndex", mock.Anything, mock.Anything).Return(nil, + status.Error(codes.Unavailable, "")).Twice() + + // Setup mock that the transaction error message is not found in storage. + suite.txErrorMessages.On("ByBlockIDTransactionIndex", blockId, failedTxIndex). + Return(nil, storage.ErrNotFound).Once() + + // Setup mock that the transaction result exists and is failed. + suite.transactionResults.On("ByBlockIDTransactionIndex", blockId, failedTxIndex). + Return(&flow.LightTransactionResult{ + TransactionID: failedTxId, + Failed: true, + ComputationUsed: 0, + }, nil).Once() + + // Perform the lookup and expect the failed error message to be returned. + errMsg, err := backend.LookupErrorMessageByIndex(context.Background(), blockId, block.Header.Height, failedTxIndex) + suite.Require().NoError(err) + suite.Require().Equal(errMsg, DefaultFailedErrorMessage) + suite.assertAllExpectations() + }) } -// TestLookupTransactionErrorMessages_HappyPath tests lookup of a transaction error messages by block ID. -// In a happy path, it has to be fetched from the execution node if there are no cached results. -// All fetched transactions have to be cached for future calls. -func (suite *Suite) TestLookupTransactionErrorMessages_HappyPath() { +// TestLookupTransactionErrorMessagesByBlockID_HappyPath verifies the lookup of transaction error messages by block ID. +// It tests two cases: +// 1. Happy path where the error messages are fetched from the EN if they are not found in the cache. +// 2. Happy path where the error messages are served from the storage database if they exist. +func (suite *Suite) TestLookupTransactionErrorMessagesByBlockID_HappyPath() { block := unittest.BlockFixture() blockId := block.ID() @@ -595,211 +725,227 @@ func (suite *Suite) TestLookupTransactionErrorMessages_HappyPath() { }) } - suite.transactionResults.On("ByBlockID", blockId). - Return(resultsByBlockID, nil).Twice() - _, fixedENIDs := suite.setupReceipts(&block) suite.state.On("Final").Return(suite.snapshot, nil).Maybe() suite.snapshot.On("Identities", mock.Anything).Return(fixedENIDs, nil) - // create a mock connection factory - connFactory := connectionmock.NewConnectionFactory(suite.T()) - connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mockCloser{}, nil) - - // create a mock index reporter - reporter := syncmock.NewIndexReporter(suite.T()) - reporter.On("LowestIndexedHeight").Return(block.Header.Height, nil) - reporter.On("HighestIndexedHeight").Return(block.Header.Height+10, nil) + suite.fixedExecutionNodeIDs = fixedENIDs.NodeIDs() params := suite.defaultBackendParams() + params.TxResultErrorMessages = suite.txErrorMessages - // the connection factory should be used to get the execution node client - params.ConnFactory = connFactory - params.FixedExecutionNodeIDs = fixedENIDs.NodeIDs().Strings() - - params.TxResultsIndex = index.NewTransactionResultsIndex(index.NewReporter(), suite.transactionResults) - err := params.TxResultsIndex.Initialize(reporter) - suite.Require().NoError(err) + // Test case: transaction error messages is fetched from the EN. + suite.Run("happy path from EN", func() { + // the connection factory should be used to get the execution node client + params.ConnFactory = suite.setupConnectionFactory() - backend, err := New(params) - suite.Require().NoError(err) + // Mock the cache lookup for the transaction error messages, returning "not found". + suite.txErrorMessages.On("ByBlockID", blockId). + Return(nil, storage.ErrNotFound).Once() - expectedErrorMsg := "some error" + backend, err := New(params) + suite.Require().NoError(err) - exeEventReq := &execproto.GetTransactionErrorMessagesByBlockIDRequest{ - BlockId: blockId[:], - } + // Mock the execution node API call to fetch the error messages. + exeEventReq := &execproto.GetTransactionErrorMessagesByBlockIDRequest{ + BlockId: blockId[:], + } + exeErrMessagesResp := &execproto.GetTransactionErrorMessagesResponse{} + for _, result := range resultsByBlockID { + r := result + if r.Failed { + errMsg := fmt.Sprintf("%s.%s", expectedErrorMsg, r.TransactionID) + exeErrMessagesResp.Results = append(exeErrMessagesResp.Results, &execproto.GetTransactionErrorMessagesResponse_Result{ + TransactionId: r.TransactionID[:], + ErrorMessage: errMsg, + }) + } + } + suite.execClient.On("GetTransactionErrorMessagesByBlockID", mock.Anything, exeEventReq). + Return(exeErrMessagesResp, nil). + Once() - exeEventResp := &execproto.GetTransactionErrorMessagesResponse{} - for _, result := range resultsByBlockID { - r := result - if r.Failed { - errMsg := fmt.Sprintf("%s.%s", expectedErrorMsg, r.TransactionID) - exeEventResp.Results = append(exeEventResp.Results, &execproto.GetTransactionErrorMessagesResponse_Result{ - TransactionId: r.TransactionID[:], - ErrorMessage: errMsg, - }) + // Perform the lookup and assert that the error message is retrieved correctly. + errMessages, err := backend.LookupErrorMessagesByBlockID(context.Background(), blockId, block.Header.Height) + suite.Require().NoError(err) + suite.Require().Len(errMessages, len(exeErrMessagesResp.Results)) + for _, expectedResult := range exeErrMessagesResp.Results { + errMsg, ok := errMessages[convert.MessageToIdentifier(expectedResult.TransactionId)] + suite.Require().True(ok) + suite.Assert().Equal(expectedResult.ErrorMessage, errMsg) } - } + suite.assertAllExpectations() + }) - suite.execClient.On("GetTransactionErrorMessagesByBlockID", mock.Anything, exeEventReq). - Return(exeEventResp, nil). - Once() + // Test case: transaction error messages is fetched from the storage database. + suite.Run("happy path from storage db", func() { + backend, err := New(params) + suite.Require().NoError(err) - errMessages, err := backend.LookupErrorMessagesByBlockID(context.Background(), blockId, block.Header.Height) - suite.Require().NoError(err) - suite.Require().Len(errMessages, len(exeEventResp.Results)) - for _, expectedResult := range exeEventResp.Results { - errMsg, ok := errMessages[convert.MessageToIdentifier(expectedResult.TransactionId)] - suite.Require().True(ok) - suite.Assert().Equal(expectedResult.ErrorMessage, errMsg) - } + // Mock the cache lookup for the transaction error messages, returning a stored result. + var txErrorMessages []flow.TransactionResultErrorMessage + for i, result := range resultsByBlockID { + if result.Failed { + errMsg := fmt.Sprintf("%s.%s", expectedErrorMsg, result.TransactionID) + + txErrorMessages = append(txErrorMessages, + flow.TransactionResultErrorMessage{ + TransactionID: result.TransactionID, + ErrorMessage: errMsg, + Index: uint32(i), + ExecutorID: unittest.IdentifierFixture(), + }) + } + } + suite.txErrorMessages.On("ByBlockID", blockId). + Return(txErrorMessages, nil).Once() - // ensure the transaction error message is cached after retrieval; we do this by mocking the grpc call - // only once - errMessages, err = backend.LookupErrorMessagesByBlockID(context.Background(), blockId, block.Header.Height) - suite.Require().NoError(err) - suite.Require().Len(errMessages, len(exeEventResp.Results)) - for _, expectedResult := range exeEventResp.Results { - errMsg, ok := errMessages[convert.MessageToIdentifier(expectedResult.TransactionId)] - suite.Require().True(ok) - suite.Assert().Equal(expectedResult.ErrorMessage, errMsg) - } - suite.assertAllExpectations() + // Perform the lookup and assert that the error message is retrieved correctly from storage. + errMessages, err := backend.LookupErrorMessagesByBlockID(context.Background(), blockId, block.Header.Height) + suite.Require().NoError(err) + suite.Require().Len(errMessages, len(txErrorMessages)) + for _, expected := range txErrorMessages { + errMsg, ok := errMessages[expected.TransactionID] + suite.Require().True(ok) + suite.Assert().Equal(expected.ErrorMessage, errMsg) + } + suite.assertAllExpectations() + }) } -// TestLookupTransactionErrorMessages_HappyPath_NoFailedTxns tests lookup of a transaction error messages by block ID. -// In a happy path where a block with no failed txns is requested. We don't want to perform an RPC call in this case. -func (suite *Suite) TestLookupTransactionErrorMessages_HappyPath_NoFailedTxns() { +// TestLookupTransactionErrorMessagesByBlockID_FailedToFetch tests lookup of a transaction error messages by block ID, +// when a transaction result is not in the cache and needs to be fetched from EN, but the EN fails to return it. +// It tests three cases: +// 1. The transaction is not found in the transaction results, leading to a "NotFound" error. +// 2. The transaction result is not failed, and the error message is empty. +// 3. The transaction result is failed, and the error message "failed" are returned. +func (suite *Suite) TestLookupTransactionErrorMessagesByBlockID_FailedToFetch() { block := unittest.BlockFixture() blockId := block.ID() - resultsByBlockID := []flow.LightTransactionResult{ - { - TransactionID: unittest.IdentifierFixture(), - Failed: false, - ComputationUsed: 0, - }, - { - TransactionID: unittest.IdentifierFixture(), - Failed: false, - ComputationUsed: 0, - }, - } - - suite.transactionResults.On("ByBlockID", blockId). - Return(resultsByBlockID, nil).Once() + // Setup mock receipts and execution node identities. + _, fixedENIDs := suite.setupReceipts(&block) + suite.state.On("Final").Return(suite.snapshot, nil).Maybe() + suite.snapshot.On("Identities", mock.Anything).Return(fixedENIDs, nil) - // create a mock index reporter + // Create a mock index reporter reporter := syncmock.NewIndexReporter(suite.T()) reporter.On("LowestIndexedHeight").Return(block.Header.Height, nil) reporter.On("HighestIndexedHeight").Return(block.Header.Height+10, nil) - params := suite.defaultBackendParams() - - params.TxResultsIndex = index.NewTransactionResultsIndex(index.NewReporter(), suite.transactionResults) - err := params.TxResultsIndex.Initialize(reporter) - suite.Require().NoError(err) - - backend, err := New(params) - suite.Require().NoError(err) - - errMessages, err := backend.LookupErrorMessagesByBlockID(context.Background(), blockId, block.Header.Height) - suite.Require().NoError(err) - suite.Require().Empty(errMessages) - suite.assertAllExpectations() -} - -// TestLookupTransactionErrorMessages_UnknownTransaction tests lookup of a transaction error messages by block ID, -// when a transaction results for block has not been synced yet, in this case nothing we can do but return an error. -func (suite *Suite) TestLookupTransactionErrorMessages_UnknownTransaction() { - block := unittest.BlockFixture() - blockId := block.ID() - - suite.transactionResults.On("ByBlockID", blockId). - Return(nil, storage.ErrNotFound).Once() - - // create a mock index reporter - reporter := syncmock.NewIndexReporter(suite.T()) - reporter.On("LowestIndexedHeight").Return(block.Header.Height, nil) - reporter.On("HighestIndexedHeight").Return(block.Header.Height+10, nil) + suite.fixedExecutionNodeIDs = fixedENIDs.NodeIDs() params := suite.defaultBackendParams() - + // the connection factory should be used to get the execution node client + params.ConnFactory = suite.setupConnectionFactory() + // Initialize the transaction results index with the mock reporter. params.TxResultsIndex = index.NewTransactionResultsIndex(index.NewReporter(), suite.transactionResults) err := params.TxResultsIndex.Initialize(reporter) suite.Require().NoError(err) + params.TxResultErrorMessages = suite.txErrorMessages + backend, err := New(params) suite.Require().NoError(err) - errMsg, err := backend.LookupErrorMessagesByBlockID(context.Background(), blockId, block.Header.Height) - suite.Require().Error(err) - suite.Require().Equal(codes.NotFound, status.Code(err)) - suite.Require().Empty(errMsg) + // Test case: failed to fetch from EN, transaction is unknown. + suite.Run("failed to fetch from EN, unknown tx", func() { + // lookup should try each of the 2 ENs in fixedENIDs + suite.execClient.On("GetTransactionErrorMessagesByBlockID", mock.Anything, mock.Anything).Return(nil, + status.Error(codes.Unavailable, "")).Twice() - suite.assertAllExpectations() -} - -// TestLookupTransactionErrorMessages_FailedToFetch tests lookup of a transaction error messages by block ID, -// when a transaction result is not in the cache and needs to be fetched from EN, but the EN fails to return it. -func (suite *Suite) TestLookupTransactionErrorMessages_FailedToFetch() { - block := unittest.BlockFixture() - blockId := block.ID() + // Setup mock that the transaction and tx error messages is not found in the storage. + suite.txErrorMessages.On("ByBlockID", blockId). + Return(nil, storage.ErrNotFound).Once() + suite.transactionResults.On("ByBlockID", blockId). + Return(nil, storage.ErrNotFound).Once() - resultsByBlockID := []flow.LightTransactionResult{ - { - TransactionID: unittest.IdentifierFixture(), - Failed: true, - ComputationUsed: 0, - }, - { - TransactionID: unittest.IdentifierFixture(), - Failed: true, - ComputationUsed: 0, - }, - } - - suite.transactionResults.On("ByBlockID", blockId). - Return(resultsByBlockID, nil).Once() - - _, fixedENIDs := suite.setupReceipts(&block) - suite.state.On("Final").Return(suite.snapshot, nil).Maybe() - suite.snapshot.On("Identities", mock.Anything).Return(fixedENIDs, nil) - - // create a mock connection factory - connFactory := connectionmock.NewConnectionFactory(suite.T()) - connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mockCloser{}, nil) - - // create a mock index reporter - reporter := syncmock.NewIndexReporter(suite.T()) - reporter.On("LowestIndexedHeight").Return(block.Header.Height, nil) - reporter.On("HighestIndexedHeight").Return(block.Header.Height+10, nil) + // Perform the lookup and expect a "NotFound" error with an empty error message. + errMsg, err := backend.LookupErrorMessagesByBlockID(context.Background(), blockId, block.Header.Height) + suite.Require().Error(err) + suite.Require().Equal(codes.NotFound, status.Code(err)) + suite.Require().Empty(errMsg) + suite.assertAllExpectations() + }) - params := suite.defaultBackendParams() - // the connection factory should be used to get the execution node client - params.ConnFactory = connFactory - params.FixedExecutionNodeIDs = fixedENIDs.NodeIDs().Strings() + // Test case: failed to fetch from EN, but the transaction result is not failed. + suite.Run("failed to fetch from EN, tx result is not failed", func() { + // lookup should try each of the 2 ENs in fixedENIDs + suite.execClient.On("GetTransactionErrorMessagesByBlockID", mock.Anything, mock.Anything).Return(nil, + status.Error(codes.Unavailable, "")).Twice() + + // Setup mock that the transaction error message is not found in storage. + suite.txErrorMessages.On("ByBlockID", blockId). + Return(nil, storage.ErrNotFound).Once() + + // Setup mock that the transaction results exists and is not failed. + suite.transactionResults.On("ByBlockID", blockId). + Return([]flow.LightTransactionResult{ + { + TransactionID: unittest.IdentifierFixture(), + Failed: false, + ComputationUsed: 0, + }, + { + TransactionID: unittest.IdentifierFixture(), + Failed: false, + ComputationUsed: 0, + }, + }, nil).Once() + + // Perform the lookup and expect no error and an empty error messages. + errMsg, err := backend.LookupErrorMessagesByBlockID(context.Background(), blockId, block.Header.Height) + suite.Require().NoError(err) + suite.Require().Empty(errMsg) + suite.assertAllExpectations() + }) - params.TxResultsIndex = index.NewTransactionResultsIndex(index.NewReporter(), suite.transactionResults) - err := params.TxResultsIndex.Initialize(reporter) - suite.Require().NoError(err) + // Test case: failed to fetch from EN, but the transaction result is failed. + suite.Run("failed to fetch from EN, tx result is failed", func() { + failedResultsByBlockID := []flow.LightTransactionResult{ + { + TransactionID: unittest.IdentifierFixture(), + Failed: true, + ComputationUsed: 0, + }, + { + TransactionID: unittest.IdentifierFixture(), + Failed: true, + ComputationUsed: 0, + }, + } - backend, err := New(params) - suite.Require().NoError(err) + // lookup should try each of the 2 ENs in fixedENIDs + suite.execClient.On("GetTransactionErrorMessagesByBlockID", mock.Anything, mock.Anything).Return(nil, + status.Error(codes.Unavailable, "")).Twice() - // pretend the first transaction has been cached, but there are multiple failed txns so still a request has to be made. - backend.txErrorMessagesCache.Add(resultsByBlockID[0].TransactionID, "some error") + // Setup mock that the transaction error messages is not found in storage. + suite.txErrorMessages.On("ByBlockID", blockId). + Return(nil, storage.ErrNotFound).Once() - suite.execClient.On("GetTransactionErrorMessagesByBlockID", mock.Anything, mock.Anything).Return(nil, - status.Error(codes.Unavailable, "")).Twice() + // Setup mock that the transaction results exists and is failed. + suite.transactionResults.On("ByBlockID", blockId). + Return(failedResultsByBlockID, nil).Once() - errMsg, err := backend.LookupErrorMessagesByBlockID(context.Background(), blockId, block.Header.Height) - suite.Require().Error(err) - suite.Require().Equal(codes.Unavailable, status.Code(err)) - suite.Require().Empty(errMsg) + // Setup mock expected the transaction error messages after retrieving the failed result. + expectedTxErrorMessages := make(map[flow.Identifier]string) + for _, result := range failedResultsByBlockID { + if result.Failed { + expectedTxErrorMessages[result.TransactionID] = DefaultFailedErrorMessage + } + } - suite.assertAllExpectations() + // Perform the lookup and expect the failed error messages to be returned. + errMsg, err := backend.LookupErrorMessagesByBlockID(context.Background(), blockId, block.Header.Height) + suite.Require().NoError(err) + suite.Require().Len(errMsg, len(expectedTxErrorMessages)) + for txID, expectedMessage := range expectedTxErrorMessages { + actualMessage, ok := errMsg[txID] + suite.Require().True(ok) + suite.Assert().Equal(expectedMessage, actualMessage) + } + suite.assertAllExpectations() + }) } // TestGetSystemTransaction_HappyPath tests that GetSystemTransaction call returns system chunk transaction. @@ -851,13 +997,9 @@ func (suite *Suite) TestGetSystemTransactionResult_HappyPath() { On("ByBlockID", block.ID()). Return(flow.ExecutionReceiptList{receipt1}, nil) - // create a mock connection factory - connFactory := connectionmock.NewConnectionFactory(suite.T()) - connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mockCloser{}, nil) - // the connection factory should be used to get the execution node client params := suite.defaultBackendParams() - params.ConnFactory = connFactory + params.ConnFactory = suite.setupConnectionFactory() exeEventReq := &execproto.GetTransactionsByBlockIDRequest{ BlockId: blockID[:], @@ -1046,13 +1188,9 @@ func (suite *Suite) TestGetSystemTransactionResult_FailedEncodingConversion() { On("ByBlockID", block.ID()). Return(flow.ExecutionReceiptList{receipt1}, nil) - // create a mock connection factory - connFactory := connectionmock.NewConnectionFactory(suite.T()) - connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mockCloser{}, nil) - // the connection factory should be used to get the execution node client params := suite.defaultBackendParams() - params.ConnFactory = connFactory + params.ConnFactory = suite.setupConnectionFactory() exeEventReq := &execproto.GetTransactionsByBlockIDRequest{ BlockId: blockID[:], @@ -1175,10 +1313,6 @@ func (suite *Suite) TestTransactionResultFromStorage() { suite.snapshot.On("Identities", mock.Anything).Return(fixedENIDs, nil) suite.snapshot.On("Head", mock.Anything).Return(block.Header, nil) - // create a mock connection factory - connFactory := connectionmock.NewConnectionFactory(suite.T()) - connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mockCloser{}, nil) - // create a mock index reporter reporter := syncmock.NewIndexReporter(suite.T()) reporter.On("LowestIndexedHeight").Return(block.Header.Height, nil) @@ -1188,13 +1322,13 @@ func (suite *Suite) TestTransactionResultFromStorage() { err := indexReporter.Initialize(reporter) suite.Require().NoError(err) + suite.fixedExecutionNodeIDs = fixedENIDs.NodeIDs() + // Set up the backend parameters and the backend instance params := suite.defaultBackendParams() // the connection factory should be used to get the execution node client - params.ConnFactory = connFactory - params.FixedExecutionNodeIDs = fixedENIDs.NodeIDs().Strings() + params.ConnFactory = suite.setupConnectionFactory() params.TxResultQueryMode = IndexQueryModeLocalOnly - params.EventsIndex = index.NewEventsIndex(indexReporter, suite.events) params.TxResultsIndex = index.NewTransactionResultsIndex(indexReporter, suite.transactionResults) @@ -1266,10 +1400,6 @@ func (suite *Suite) TestTransactionByIndexFromStorage() { suite.snapshot.On("Identities", mock.Anything).Return(fixedENIDs, nil) suite.snapshot.On("Head", mock.Anything).Return(block.Header, nil) - // Create a mock connection factory - connFactory := connectionmock.NewConnectionFactory(suite.T()) - connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mockCloser{}, nil) - // create a mock index reporter reporter := syncmock.NewIndexReporter(suite.T()) reporter.On("LowestIndexedHeight").Return(block.Header.Height, nil) @@ -1279,13 +1409,13 @@ func (suite *Suite) TestTransactionByIndexFromStorage() { err := indexReporter.Initialize(reporter) suite.Require().NoError(err) + suite.fixedExecutionNodeIDs = fixedENIDs.NodeIDs() + // Set up the backend parameters and the backend instance params := suite.defaultBackendParams() // the connection factory should be used to get the execution node client - params.ConnFactory = connFactory - params.FixedExecutionNodeIDs = fixedENIDs.NodeIDs().Strings() + params.ConnFactory = suite.setupConnectionFactory() params.TxResultQueryMode = IndexQueryModeLocalOnly - params.EventsIndex = index.NewEventsIndex(indexReporter, suite.events) params.TxResultsIndex = index.NewTransactionResultsIndex(indexReporter, suite.transactionResults) @@ -1363,10 +1493,6 @@ func (suite *Suite) TestTransactionResultsByBlockIDFromStorage() { suite.snapshot.On("Identities", mock.Anything).Return(fixedENIDs, nil) suite.snapshot.On("Head", mock.Anything).Return(block.Header, nil) - // create a mock connection factory - connFactory := connectionmock.NewConnectionFactory(suite.T()) - connFactory.On("GetExecutionAPIClient", mock.Anything).Return(suite.execClient, &mockCloser{}, nil) - // create a mock index reporter reporter := syncmock.NewIndexReporter(suite.T()) reporter.On("LowestIndexedHeight").Return(block.Header.Height, nil) @@ -1376,15 +1502,14 @@ func (suite *Suite) TestTransactionResultsByBlockIDFromStorage() { err := indexReporter.Initialize(reporter) suite.Require().NoError(err) + suite.fixedExecutionNodeIDs = fixedENIDs.NodeIDs() + // Set up the state and snapshot mocks and the backend instance params := suite.defaultBackendParams() // the connection factory should be used to get the execution node client - params.ConnFactory = connFactory - params.FixedExecutionNodeIDs = fixedENIDs.NodeIDs().Strings() - + params.ConnFactory = suite.setupConnectionFactory() params.EventsIndex = index.NewEventsIndex(indexReporter, suite.events) params.TxResultsIndex = index.NewTransactionResultsIndex(indexReporter, suite.transactionResults) - params.TxResultQueryMode = IndexQueryModeLocalOnly backend, err := New(params) diff --git a/engine/access/rpc/backend/node_selector.go b/engine/access/rpc/backend/node_selector.go index c7d2ada5fb4..4aab3a89ca5 100644 --- a/engine/access/rpc/backend/node_selector.go +++ b/engine/access/rpc/backend/node_selector.go @@ -3,12 +3,10 @@ package backend import ( "fmt" + commonrpc "github.com/onflow/flow-go/engine/common/rpc" "github.com/onflow/flow-go/model/flow" ) -// maxNodesCnt is the maximum number of nodes that will be contacted to complete an API request. -const maxNodesCnt = 3 - // NodeSelector is an interface that represents the ability to select node identities that the access node is trying to reach. // It encapsulates the internal logic of node selection and provides a way to change implementations for different types // of nodes. Implementations of this interface should define the Next method, which returns the next node identity to be @@ -26,13 +24,26 @@ type NodeSelectorFactory struct { circuitBreakerEnabled bool } +// NewNodeSelectorFactory creates a new instance of NodeSelectorFactory with the provided circuit breaker configuration. +// +// When `circuitBreakerEnabled` is set to true, nodes will be selected using a pseudo-random sampling mechanism and picked in-order. +// When set to false, nodes will be selected in the order they are proposed, without any changes. +// +// Parameters: +// - circuitBreakerEnabled: A boolean that controls whether the circuit breaker is enabled for node selection. +func NewNodeSelectorFactory(circuitBreakerEnabled bool) *NodeSelectorFactory { + return &NodeSelectorFactory{ + circuitBreakerEnabled: circuitBreakerEnabled, + } +} + // SelectNodes selects the configured number of node identities from the provided list of nodes // and returns the node selector to iterate through them. func (n *NodeSelectorFactory) SelectNodes(nodes flow.IdentitySkeletonList) (NodeSelector, error) { var err error // If the circuit breaker is disabled, the legacy logic should be used, which selects only a specified number of nodes. if !n.circuitBreakerEnabled { - nodes, err = nodes.Sample(maxNodesCnt) + nodes, err = nodes.Sample(commonrpc.MaxNodesCnt) if err != nil { return nil, fmt.Errorf("sampling failed: %w", err) } diff --git a/engine/access/rpc/backend/script_executor_test.go b/engine/access/rpc/backend/script_executor_test.go index 542d84e417d..70445b8ab8d 100644 --- a/engine/access/rpc/backend/script_executor_test.go +++ b/engine/access/rpc/backend/script_executor_test.go @@ -126,7 +126,7 @@ func (s *ScriptExecutorSuite) SetupTest() { s.dbDir = unittest.TempDir(s.T()) db := pebbleStorage.NewBootstrappedRegistersWithPathForTest(s.T(), s.dbDir, s.height, s.height) - pebbleRegisters, err := pebbleStorage.NewRegisters(db) + pebbleRegisters, err := pebbleStorage.NewRegisters(db, pebbleStorage.PruningDisabled) s.Require().NoError(err) s.registerIndex = pebbleRegisters diff --git a/engine/access/rpc/backend/transactions_local_data_provider.go b/engine/access/rpc/backend/transactions_local_data_provider.go index f68fb35ed4b..921580452ec 100644 --- a/engine/access/rpc/backend/transactions_local_data_provider.go +++ b/engine/access/rpc/backend/transactions_local_data_provider.go @@ -31,18 +31,16 @@ type TransactionErrorMessage interface { // Expected errors during normal operation: // - InsufficientExecutionReceipts - found insufficient receipts for given block ID. // - status.Error - remote GRPC call to EN has failed. - LookupErrorMessageByTransactionID(ctx context.Context, blockID flow.Identifier, transactionID flow.Identifier) (string, error) + LookupErrorMessageByTransactionID(ctx context.Context, blockID flow.Identifier, height uint64, transactionID flow.Identifier) (string, error) // LookupErrorMessageByIndex is a function type for getting transaction error message by index. // Expected errors during normal operation: - // - status.Error[codes.NotFound] - transaction result for given block ID and tx index is not available. // - InsufficientExecutionReceipts - found insufficient receipts for given block ID. // - status.Error - remote GRPC call to EN has failed. LookupErrorMessageByIndex(ctx context.Context, blockID flow.Identifier, height uint64, index uint32) (string, error) // LookupErrorMessagesByBlockID is a function type for getting transaction error messages by block ID. // Expected errors during normal operation: - // - status.Error[codes.NotFound] - transaction results for given block ID are not available. // - InsufficientExecutionReceipts - found insufficient receipts for given block ID. // - status.Error - remote GRPC call to EN has failed. LookupErrorMessagesByBlockID(ctx context.Context, blockID flow.Identifier, height uint64) (map[flow.Identifier]string, error) @@ -84,7 +82,7 @@ func (t *TransactionsLocalDataProvider) GetTransactionResultFromStorage( var txErrorMessage string var txStatusCode uint = 0 if txResult.Failed { - txErrorMessage, err = t.txErrorMessages.LookupErrorMessageByTransactionID(ctx, blockID, transactionID) + txErrorMessage, err = t.txErrorMessages.LookupErrorMessageByTransactionID(ctx, blockID, block.Header.Height, transactionID) if err != nil { return nil, err } diff --git a/engine/access/rpc/engine.go b/engine/access/rpc/engine.go index a42c8495345..145e3d62143 100644 --- a/engine/access/rpc/engine.go +++ b/engine/access/rpc/engine.go @@ -8,8 +8,6 @@ import ( "net/http" "sync" - "github.com/onflow/flow-go/module/state_synchronization" - "github.com/rs/zerolog" "google.golang.org/grpc/credentials" @@ -25,6 +23,7 @@ import ( "github.com/onflow/flow-go/module/events" "github.com/onflow/flow-go/module/grpcserver" "github.com/onflow/flow-go/module/irrecoverable" + "github.com/onflow/flow-go/module/state_synchronization" "github.com/onflow/flow-go/state/protocol" ) diff --git a/engine/access/state_stream/backend/handler.go b/engine/access/state_stream/backend/handler.go index 11281da9486..b2066440bb8 100644 --- a/engine/access/state_stream/backend/handler.go +++ b/engine/access/state_stream/backend/handler.go @@ -381,7 +381,10 @@ func (h *Handler) handleEventsResponse(send sendSubscribeEventsResponseFunc, hea return status.Errorf(codes.Internal, "could not convert events to entity: %v", err) } - index := messageIndex.Increment() + index := messageIndex.Value() + if ok := messageIndex.Set(index + 1); !ok { + return status.Errorf(codes.Internal, "message index already incremented to %d", messageIndex.Value()) + } err = send(&executiondata.SubscribeEventsResponse{ BlockHeight: resp.Height, @@ -495,7 +498,10 @@ func (h *Handler) handleAccountStatusesResponse( return err } - index := messageIndex.Increment() + index := messageIndex.Value() + if ok := messageIndex.Set(index + 1); !ok { + return status.Errorf(codes.Internal, "message index already incremented to %d", messageIndex.Value()) + } err = send(&executiondata.SubscribeAccountStatusesResponse{ BlockId: convert.IdentifierToMessage(resp.BlockID), diff --git a/engine/collection/compliance/core.go b/engine/collection/compliance/core.go index c341d7cb146..dc5432d2925 100644 --- a/engine/collection/compliance/core.go +++ b/engine/collection/compliance/core.go @@ -260,7 +260,7 @@ func (c *Core) processBlockAndDescendants(proposal flow.Slashable[*cluster.Block }) // notify VoteAggregator about the invalid block - err = c.voteAggregator.InvalidBlock(model.ProposalFromFlow(header)) + err = c.voteAggregator.InvalidBlock(model.SignedProposalFromFlow(header)) if err != nil { if mempool.IsBelowPrunedThresholdError(err) { log.Warn().Msg("received invalid block, but is below pruned threshold") @@ -317,7 +317,7 @@ func (c *Core) processBlockProposal(proposal *cluster.Block) error { Logger() log.Info().Msg("processing block proposal") - hotstuffProposal := model.ProposalFromFlow(header) + hotstuffProposal := model.SignedProposalFromFlow(header) err := c.validator.ValidateProposal(hotstuffProposal) if err != nil { if model.IsInvalidProposalError(err) { diff --git a/engine/collection/compliance/core_test.go b/engine/collection/compliance/core_test.go index 2ed1292ee32..6fd27bc3963 100644 --- a/engine/collection/compliance/core_test.go +++ b/engine/collection/compliance/core_test.go @@ -206,7 +206,7 @@ func (cs *CoreSuite) TestOnBlockProposalValidParent() { // store the data for retrieval cs.headerDB[block.Header.ParentID] = cs.head - hotstuffProposal := model.ProposalFromFlow(block.Header) + hotstuffProposal := model.SignedProposalFromFlow(block.Header) cs.validator.On("ValidateProposal", hotstuffProposal).Return(nil) cs.voteAggregator.On("AddBlock", hotstuffProposal).Once() cs.hotstuff.On("SubmitProposal", hotstuffProposal) @@ -232,7 +232,7 @@ func (cs *CoreSuite) TestOnBlockProposalValidAncestor() { cs.headerDB[parent.ID()] = &parent cs.headerDB[ancestor.ID()] = &ancestor - hotstuffProposal := model.ProposalFromFlow(block.Header) + hotstuffProposal := model.SignedProposalFromFlow(block.Header) cs.validator.On("ValidateProposal", hotstuffProposal).Return(nil) cs.voteAggregator.On("AddBlock", hotstuffProposal).Once() cs.hotstuff.On("SubmitProposal", hotstuffProposal).Once() @@ -280,7 +280,7 @@ func (cs *CoreSuite) TestOnBlockProposal_FailsHotStuffValidation() { parent := unittest.ClusterBlockWithParent(&ancestor) block := unittest.ClusterBlockWithParent(&parent) proposal := messages.NewClusterBlockProposal(&block) - hotstuffProposal := model.ProposalFromFlow(block.Header) + hotstuffProposal := model.SignedProposalFromFlow(block.Header) // store the data for retrieval cs.headerDB[parent.ID()] = &parent @@ -363,7 +363,7 @@ func (cs *CoreSuite) TestOnBlockProposal_FailsProtocolStateValidation() { parent := unittest.ClusterBlockWithParent(&ancestor) block := unittest.ClusterBlockWithParent(&parent) proposal := messages.NewClusterBlockProposal(&block) - hotstuffProposal := model.ProposalFromFlow(block.Header) + hotstuffProposal := model.SignedProposalFromFlow(block.Header) // store the data for retrieval cs.headerDB[parent.ID()] = &parent @@ -476,7 +476,7 @@ func (cs *CoreSuite) TestProcessBlockAndDescendants() { cs.childrenDB[parentID] = append(cs.childrenDB[parentID], pending3) for _, block := range []cluster.Block{parent, block1, block2, block3} { - hotstuffProposal := model.ProposalFromFlow(block.Header) + hotstuffProposal := model.SignedProposalFromFlow(block.Header) cs.validator.On("ValidateProposal", hotstuffProposal).Return(nil) cs.voteAggregator.On("AddBlock", hotstuffProposal).Once() cs.hotstuff.On("SubmitProposal", hotstuffProposal).Once() @@ -552,7 +552,7 @@ func (cs *CoreSuite) TestProposalBufferingOrder() { } cs.hotstuff.On("SubmitProposal", mock.Anything).Times(4).Run( func(args mock.Arguments) { - header := args.Get(0).(*model.Proposal).Block + header := args.Get(0).(*model.SignedProposal).Block assert.Equal(cs.T(), order[index], header.BlockID, "should submit correct header to hotstuff") index++ cs.headerDB[header.BlockID] = proposalsLookup[header.BlockID] diff --git a/engine/collection/compliance/engine_test.go b/engine/collection/compliance/engine_test.go index 5ad01b19566..5c3a2c9cd99 100644 --- a/engine/collection/compliance/engine_test.go +++ b/engine/collection/compliance/engine_test.go @@ -165,7 +165,7 @@ func (cs *EngineSuite) TestSubmittingMultipleEntries() { for i := 0; i < blockCount; i++ { block := unittest.ClusterBlockWithParent(cs.head) proposal := messages.NewClusterBlockProposal(&block) - hotstuffProposal := model.ProposalFromFlow(block.Header) + hotstuffProposal := model.SignedProposalFromFlow(block.Header) cs.hotstuff.On("SubmitProposal", hotstuffProposal).Return().Once() cs.voteAggregator.On("AddBlock", hotstuffProposal).Once() cs.validator.On("ValidateProposal", hotstuffProposal).Return(nil).Once() @@ -183,7 +183,7 @@ func (cs *EngineSuite) TestSubmittingMultipleEntries() { block := unittest.ClusterBlockWithParent(cs.head) proposal := messages.NewClusterBlockProposal(&block) - hotstuffProposal := model.ProposalFromFlow(block.Header) + hotstuffProposal := model.SignedProposalFromFlow(block.Header) cs.hotstuff.On("SubmitProposal", hotstuffProposal).Once() cs.voteAggregator.On("AddBlock", hotstuffProposal).Once() cs.validator.On("ValidateProposal", hotstuffProposal).Return(nil).Once() diff --git a/engine/collection/ingest/engine.go b/engine/collection/ingest/engine.go index 438e6bea88b..ae21f71253f 100644 --- a/engine/collection/ingest/engine.go +++ b/engine/collection/ingest/engine.go @@ -61,7 +61,7 @@ func New( logger := log.With().Str("engine", "ingest").Logger() transactionValidator := access.NewTransactionValidatorWithLimiter( - access.NewProtocolStateBlocks(state), + access.NewProtocolStateBlocks(state, nil), chain, access.TransactionValidationOptions{ Expiry: flow.DefaultTransactionExpiry, diff --git a/engine/collection/ingest/engine_test.go b/engine/collection/ingest/engine_test.go index 97eab113c97..f96cebc7908 100644 --- a/engine/collection/ingest/engine_test.go +++ b/engine/collection/ingest/engine_test.go @@ -172,6 +172,25 @@ func (suite *Suite) TestInvalidTransaction() { suite.Assert().True(errors.As(err, &access.InvalidScriptError{})) }) + // In some cases the Cadence parser will panic rather than return an error. + // If this happens, we should recover from the panic and return an InvalidScriptError. + // See: https://github.com/onflow/cadence/issues/3428, https://github.com/dapperlabs/flow-go/issues/6964 + suite.Run("transaction script exceeds parse token limit (Cadence parser panic should be caught)", func() { + const tokenLimit = 1 << 19 + script := "{};" + for len(script) < tokenLimit { + script += script + } + + tx := unittest.TransactionBodyFixture() + tx.ReferenceBlockID = suite.root.ID() + tx.Script = []byte("transaction { execute {" + script + "}}") + + err := suite.engine.ProcessTransaction(&tx) + suite.Assert().Error(err) + suite.Assert().True(errors.As(err, &access.InvalidScriptError{})) + }) + suite.Run("invalid signature format", func() { signer := flow.Testnet.Chain().ServiceAddress() keyIndex := uint32(0) diff --git a/engine/collection/message_hub/message_hub.go b/engine/collection/message_hub/message_hub.go index f2241dffb73..896d5ce0b5a 100644 --- a/engine/collection/message_hub/message_hub.go +++ b/engine/collection/message_hub/message_hub.go @@ -398,7 +398,7 @@ func (h *MessageHub) OnOwnProposal(proposal *flow.Header, targetPublicationTime return } - hotstuffProposal := model.ProposalFromFlow(proposal) + hotstuffProposal := model.SignedProposalFromFlow(proposal) // notify vote aggregator that new block proposal is available, in case we are next leader h.voteAggregator.AddBlock(hotstuffProposal) // non-blocking diff --git a/engine/collection/message_hub/message_hub_test.go b/engine/collection/message_hub/message_hub_test.go index d6032fa8e6f..d1f66a1af90 100644 --- a/engine/collection/message_hub/message_hub_test.go +++ b/engine/collection/message_hub/message_hub_test.go @@ -273,7 +273,7 @@ func (s *MessageHubSuite) TestOnOwnProposal() { expectedBroadcastMsg := messages.NewClusterBlockProposal(&block) submitted := make(chan struct{}) // closed when proposal is submitted to hotstuff - hotstuffProposal := model.ProposalFromFlow(block.Header) + hotstuffProposal := model.SignedProposalFromFlow(block.Header) s.voteAggregator.On("AddBlock", hotstuffProposal).Once() s.hotstuff.On("SubmitProposal", hotstuffProposal). Run(func(args mock.Arguments) { close(submitted) }). @@ -334,7 +334,7 @@ func (s *MessageHubSuite) TestProcessMultipleMessagesHappyPath() { s.payloads.On("ByBlockID", proposal.Header.ID()).Return(proposal.Payload, nil) // unset chain and height to make sure they are correctly reconstructed - hotstuffProposal := model.ProposalFromFlow(proposal.Header) + hotstuffProposal := model.SignedProposalFromFlow(proposal.Header) s.voteAggregator.On("AddBlock", hotstuffProposal) s.hotstuff.On("SubmitProposal", hotstuffProposal) expectedBroadcastMsg := messages.NewClusterBlockProposal(&proposal) diff --git a/engine/common/follower/compliance_core.go b/engine/common/follower/compliance_core.go index c8ebf1b7a82..2a65a2f3df2 100644 --- a/engine/common/follower/compliance_core.go +++ b/engine/common/follower/compliance_core.go @@ -111,7 +111,7 @@ func (c *ComplianceCore) OnBlockRange(originID flow.Identifier, batch []*flow.Bl firstBlock := batch[0].Header lastBlock := batch[len(batch)-1].Header - hotstuffProposal := model.ProposalFromFlow(lastBlock) + hotstuffProposal := model.SignedProposalFromFlow(lastBlock) log := c.log.With(). Hex("origin_id", originID[:]). Str("chain_id", lastBlock.ChainID.String()). @@ -161,7 +161,11 @@ func (c *ComplianceCore) OnBlockRange(originID flow.Identifier, batch []*flow.Bl // a critical liveness assumption - see EpochCommitSafetyThreshold in protocol.Params for details. // -> In this case, it is ok for the protocol to halt. Consequently, we can just disregard // the block, which will probably lead to this node eventually halting. - log.Err(err).Msg("unable to validate proposal with view from unknown epoch") + log.Err(err).Msg( + "Unable to validate proposal with view from unknown epoch. While there is noting wrong with the node, " + + "this could be a symptom of (i) the node being severely behind, (ii) there is a byzantine proposer in " + + "the network, or (iii) there was no finalization progress for hundreds of views. This should be " + + "investigated to confirm the cause is the benign scenario (i).") return nil } return fmt.Errorf("unexpected error validating proposal: %w", err) diff --git a/engine/common/follower/compliance_core_test.go b/engine/common/follower/compliance_core_test.go index 522fc26160e..8930d2e19ce 100644 --- a/engine/common/follower/compliance_core_test.go +++ b/engine/common/follower/compliance_core_test.go @@ -97,7 +97,7 @@ func (s *CoreSuite) TestProcessingSingleBlock() { block := unittest.BlockWithParentFixture(s.finalizedBlock) // incoming block has to be validated - s.validator.On("ValidateProposal", model.ProposalFromFlow(block.Header)).Return(nil).Once() + s.validator.On("ValidateProposal", model.SignedProposalFromFlow(block.Header)).Return(nil).Once() err := s.core.OnBlockRange(s.originID, []*flow.Block{block}) require.NoError(s.T(), err) @@ -114,7 +114,7 @@ func (s *CoreSuite) TestAddFinalizedBlock() { block.Header.View = s.finalizedBlock.View - 1 // block is below finalized view // incoming block has to be validated - s.validator.On("ValidateProposal", model.ProposalFromFlow(block.Header)).Return(nil).Once() + s.validator.On("ValidateProposal", model.SignedProposalFromFlow(block.Header)).Return(nil).Once() err := s.core.OnBlockRange(s.originID, []*flow.Block{&block}) require.NoError(s.T(), err) @@ -140,7 +140,7 @@ func (s *CoreSuite) TestProcessingRangeHappyPath() { wg.Done() }).Return().Once() } - s.validator.On("ValidateProposal", model.ProposalFromFlow(blocks[len(blocks)-1].Header)).Return(nil).Once() + s.validator.On("ValidateProposal", model.SignedProposalFromFlow(blocks[len(blocks)-1].Header)).Return(nil).Once() err := s.core.OnBlockRange(s.originID, blocks) require.NoError(s.T(), err) @@ -154,7 +154,7 @@ func (s *CoreSuite) TestProcessingNotOrderedBatch() { blocks := unittest.ChainFixtureFrom(10, s.finalizedBlock) blocks[2], blocks[3] = blocks[3], blocks[2] - s.validator.On("ValidateProposal", model.ProposalFromFlow(blocks[len(blocks)-1].Header)).Return(nil).Once() + s.validator.On("ValidateProposal", model.SignedProposalFromFlow(blocks[len(blocks)-1].Header)).Return(nil).Once() err := s.core.OnBlockRange(s.originID, blocks) require.ErrorIs(s.T(), err, cache.ErrDisconnectedBatch) @@ -164,7 +164,7 @@ func (s *CoreSuite) TestProcessingNotOrderedBatch() { func (s *CoreSuite) TestProcessingInvalidBlock() { blocks := unittest.ChainFixtureFrom(10, s.finalizedBlock) - invalidProposal := model.ProposalFromFlow(blocks[len(blocks)-1].Header) + invalidProposal := model.SignedProposalFromFlow(blocks[len(blocks)-1].Header) sentinelError := model.NewInvalidProposalErrorf(invalidProposal, "") s.validator.On("ValidateProposal", invalidProposal).Return(sentinelError).Once() s.followerConsumer.On("OnInvalidBlockDetected", flow.Slashable[model.InvalidProposalError]{ @@ -189,7 +189,7 @@ func (s *CoreSuite) TestProcessingBlocksAfterShutdown() { // to the protocol state blocks := unittest.ChainFixtureFrom(10, s.finalizedBlock) - s.validator.On("ValidateProposal", model.ProposalFromFlow(blocks[len(blocks)-1].Header)).Return(nil).Once() + s.validator.On("ValidateProposal", model.SignedProposalFromFlow(blocks[len(blocks)-1].Header)).Return(nil).Once() err := s.core.OnBlockRange(s.originID, blocks) require.NoError(s.T(), err) diff --git a/engine/common/provider/engine.go b/engine/common/provider/engine.go index 12593b614ef..745ed7dd585 100644 --- a/engine/common/provider/engine.go +++ b/engine/common/provider/engine.go @@ -266,7 +266,7 @@ func (e *Engine) onEntityRequest(request *internal.EntityRequest) error { e.log.Info(). Str("origin_id", request.OriginId.String()). Strs("entity_ids", flow.IdentifierList(entityIDs).Strings()). - Uint64("nonce", request.Nonce). // to match with the the entity request received log + Uint64("nonce", request.Nonce). // to match with the entity request received log Msg("entity response sent") return nil diff --git a/engine/common/requester/engine.go b/engine/common/requester/engine.go index b1e35bcb642..3a9031e3b5a 100644 --- a/engine/common/requester/engine.go +++ b/engine/common/requester/engine.go @@ -421,15 +421,6 @@ func (e *Engine) dispatchRequest() (bool, error) { } e.requests[req.Nonce] = req - if e.log.Debug().Enabled() { - e.log.Debug(). - Hex("provider", logging.ID(providerID)). - Uint64("nonce", req.Nonce). - Strs("entities", logging.IDs(entityIDs)). - TimeDiff("duration", time.Now(), requestStart). - Msg("entity request sent") - } - // NOTE: we forget about requests after the expiry of the shortest retry time // from the entities in the list; this means that we purge requests aggressively. // However, most requests should be responded to on the first attempt and clearing @@ -443,11 +434,15 @@ func (e *Engine) dispatchRequest() (bool, error) { delete(e.requests, req.Nonce) }() + if e.log.Debug().Enabled() { + e.log.Debug(). + Hex("provider", logging.ID(providerID)). + Uint64("nonce", req.Nonce). + Strs("entities", logging.IDs(entityIDs)). + TimeDiff("duration", time.Now(), requestStart). + Msg("entity request sent") + } e.metrics.MessageSent(e.channel.String(), metrics.MessageEntityRequest) - e.log.Debug(). - Uint64("nonce", req.Nonce). - Strs("entity_ids", flow.IdentifierList(req.EntityIDs).Strings()). - Msg("entity request sent") return true, nil } diff --git a/engine/common/rpc/execution_node_identities_provider.go b/engine/common/rpc/execution_node_identities_provider.go new file mode 100644 index 00000000000..75a1f0b33bf --- /dev/null +++ b/engine/common/rpc/execution_node_identities_provider.go @@ -0,0 +1,298 @@ +package rpc + +import ( + "context" + "fmt" + "time" + + "github.com/rs/zerolog" + + "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/model/flow/filter" + "github.com/onflow/flow-go/state/protocol" + "github.com/onflow/flow-go/storage" +) + +// minExecutionNodesCnt is the minimum number of execution nodes expected to have sent the execution receipt for a block +const minExecutionNodesCnt = 2 + +// maxAttemptsForExecutionReceipt is the maximum number of attempts to find execution receipts for a given block ID +const maxAttemptsForExecutionReceipt = 3 + +// MaxNodesCnt is the maximum number of nodes that will be contacted to complete an API request. +const MaxNodesCnt = 3 + +func IdentifierList(ids []string) (flow.IdentifierList, error) { + idList := make(flow.IdentifierList, len(ids)) + for i, idStr := range ids { + id, err := flow.HexStringToIdentifier(idStr) + if err != nil { + return nil, fmt.Errorf("failed to convert node id string %s to Flow Identifier: %w", id, err) + } + idList[i] = id + } + return idList, nil +} + +// ExecutionNodeIdentitiesProvider is a container for elements required to retrieve +// execution node identities for a given block ID. +type ExecutionNodeIdentitiesProvider struct { + log zerolog.Logger + + executionReceipts storage.ExecutionReceipts + state protocol.State + + preferredENIdentifiers flow.IdentifierList + fixedENIdentifiers flow.IdentifierList +} + +// NewExecutionNodeIdentitiesProvider creates and returns a new instance of +// ExecutionNodeIdentitiesProvider. +// +// Parameters: +// - log: The logger to use for logging. +// - state: The protocol state used for retrieving block information. +// - executionReceipts: A storage.ExecutionReceipts object that contains the execution receipts +// for blocks. +// - preferredENIdentifiers: A flow.IdentifierList of preferred execution node identifiers that +// are prioritized during selection. +// - fixedENIdentifiers: A flow.IdentifierList of fixed execution node identifiers that are +// always considered if available. +func NewExecutionNodeIdentitiesProvider( + log zerolog.Logger, + state protocol.State, + executionReceipts storage.ExecutionReceipts, + preferredENIdentifiers flow.IdentifierList, + fixedENIdentifiers flow.IdentifierList, +) *ExecutionNodeIdentitiesProvider { + return &ExecutionNodeIdentitiesProvider{ + log: log, + executionReceipts: executionReceipts, + state: state, + preferredENIdentifiers: preferredENIdentifiers, + fixedENIdentifiers: fixedENIdentifiers, + } +} + +// ExecutionNodesForBlockID returns upto maxNodesCnt number of randomly chosen execution node identities +// which have executed the given block ID. +// If no such execution node is found, an InsufficientExecutionReceipts error is returned. +func (e *ExecutionNodeIdentitiesProvider) ExecutionNodesForBlockID( + ctx context.Context, + blockID flow.Identifier, +) (flow.IdentitySkeletonList, error) { + var ( + executorIDs flow.IdentifierList + err error + ) + + // check if the block ID is of the root block. If it is then don't look for execution receipts since they + // will not be present for the root block. + rootBlock := e.state.Params().FinalizedRoot() + + if rootBlock.ID() == blockID { + executorIdentities, err := e.state.Final().Identities(filter.HasRole[flow.Identity](flow.RoleExecution)) + if err != nil { + return nil, fmt.Errorf("failed to retreive execution IDs for block ID %v: %w", blockID, err) + } + executorIDs = executorIdentities.NodeIDs() + } else { + // try to find at least minExecutionNodesCnt execution node ids from the execution receipts for the given blockID + for attempt := 0; attempt < maxAttemptsForExecutionReceipt; attempt++ { + executorIDs, err = e.findAllExecutionNodes(blockID) + if err != nil { + return nil, err + } + + if len(executorIDs) >= minExecutionNodesCnt { + break + } + + // log the attempt + e.log.Debug().Int("attempt", attempt).Int("max_attempt", maxAttemptsForExecutionReceipt). + Int("execution_receipts_found", len(executorIDs)). + Str("block_id", blockID.String()). + Msg("insufficient execution receipts") + + // if one or less execution receipts may have been received then re-query + // in the hope that more might have been received by now + + select { + case <-ctx.Done(): + return nil, ctx.Err() + case <-time.After(100 * time.Millisecond << time.Duration(attempt)): + // retry after an exponential backoff + } + } + + receiptCnt := len(executorIDs) + // if less than minExecutionNodesCnt execution receipts have been received so far, then return random ENs + if receiptCnt < minExecutionNodesCnt { + newExecutorIDs, err := e.state.AtBlockID(blockID).Identities(filter.HasRole[flow.Identity](flow.RoleExecution)) + if err != nil { + return nil, fmt.Errorf("failed to retreive execution IDs for block ID %v: %w", blockID, err) + } + executorIDs = newExecutorIDs.NodeIDs() + } + } + + // choose from the preferred or fixed execution nodes + subsetENs, err := e.chooseExecutionNodes(executorIDs) + if err != nil { + return nil, fmt.Errorf("failed to retreive execution IDs for block ID %v: %w", blockID, err) + } + + if len(subsetENs) == 0 { + return nil, fmt.Errorf("no matching execution node found for block ID %v", blockID) + } + + return subsetENs, nil +} + +// findAllExecutionNodes find all the execution nodes ids from the execution receipts that have been received for the +// given blockID +func (e *ExecutionNodeIdentitiesProvider) findAllExecutionNodes( + blockID flow.Identifier, +) (flow.IdentifierList, error) { + // lookup the receipt's storage with the block ID + allReceipts, err := e.executionReceipts.ByBlockID(blockID) + if err != nil { + return nil, fmt.Errorf("failed to retreive execution receipts for block ID %v: %w", blockID, err) + } + + executionResultMetaList := make(flow.ExecutionReceiptMetaList, 0, len(allReceipts)) + for _, r := range allReceipts { + executionResultMetaList = append(executionResultMetaList, r.Meta()) + } + executionResultGroupedMetaList := executionResultMetaList.GroupByResultID() + + // maximum number of matching receipts found so far for any execution result id + maxMatchedReceiptCnt := 0 + // execution result id key for the highest number of matching receipts in the identicalReceipts map + var maxMatchedReceiptResultID flow.Identifier + + // find the largest list of receipts which have the same result ID + for resultID, executionReceiptList := range executionResultGroupedMetaList { + currentMatchedReceiptCnt := executionReceiptList.Size() + if currentMatchedReceiptCnt > maxMatchedReceiptCnt { + maxMatchedReceiptCnt = currentMatchedReceiptCnt + maxMatchedReceiptResultID = resultID + } + } + + // if there are more than one execution result for the same block ID, log as error + if executionResultGroupedMetaList.NumberGroups() > 1 { + identicalReceiptsStr := fmt.Sprintf("%v", flow.GetIDs(allReceipts)) + e.log.Error(). + Str("block_id", blockID.String()). + Str("execution_receipts", identicalReceiptsStr). + Msg("execution receipt mismatch") + } + + // pick the largest list of matching receipts + matchingReceiptMetaList := executionResultGroupedMetaList.GetGroup(maxMatchedReceiptResultID) + + metaReceiptGroupedByExecutorID := matchingReceiptMetaList.GroupByExecutorID() + + // collect all unique execution node ids from the receipts + var executorIDs flow.IdentifierList + for executorID := range metaReceiptGroupedByExecutorID { + executorIDs = append(executorIDs, executorID) + } + + return executorIDs, nil +} + +// chooseExecutionNodes finds the subset of execution nodes defined in the identity table by first +// choosing the preferred execution nodes which have executed the transaction. If no such preferred +// execution nodes are found, then the fixed execution nodes defined in the identity table are returned +// If neither preferred nor fixed nodes are defined, then all execution node matching the executor IDs are returned. +// e.g. If execution nodes in identity table are {1,2,3,4}, preferred ENs are defined as {2,3,4} +// and the executor IDs is {1,2,3}, then {2, 3} is returned as the chosen subset of ENs +func (e *ExecutionNodeIdentitiesProvider) chooseExecutionNodes( + executorIDs flow.IdentifierList, +) (flow.IdentitySkeletonList, error) { + allENs, err := e.state.Final().Identities(filter.HasRole[flow.Identity](flow.RoleExecution)) + if err != nil { + return nil, fmt.Errorf("failed to retrieve all execution IDs: %w", err) + } + + // choose from preferred EN IDs + if len(e.preferredENIdentifiers) > 0 { + chosenIDs := e.ChooseFromPreferredENIDs(allENs, executorIDs) + return chosenIDs.ToSkeleton(), nil + } + + // if no preferred EN ID is found, then choose from the fixed EN IDs + if len(e.fixedENIdentifiers) > 0 { + // choose fixed ENs which have executed the transaction + chosenIDs := allENs.Filter(filter.And( + filter.HasNodeID[flow.Identity](e.fixedENIdentifiers...), + filter.HasNodeID[flow.Identity](executorIDs...), + )) + if len(chosenIDs) > 0 { + return chosenIDs.ToSkeleton(), nil + } + // if no such ENs are found, then just choose all fixed ENs + chosenIDs = allENs.Filter(filter.HasNodeID[flow.Identity](e.fixedENIdentifiers...)) + return chosenIDs.ToSkeleton(), nil + } + + // if no preferred or fixed ENs have been specified, then return all executor IDs i.e., no preference at all + return allENs.Filter(filter.HasNodeID[flow.Identity](executorIDs...)).ToSkeleton(), nil +} + +// ChooseFromPreferredENIDs finds the subset of execution nodes if preferred execution nodes are defined. +// If preferredENIdentifiers is set and there are less than maxNodesCnt nodes selected, than the list is padded up to +// maxNodesCnt nodes using the following order: +// 1. Use any EN with a receipt. +// 2. Use any preferred node not already selected. +// 3. Use any EN not already selected. +func (e *ExecutionNodeIdentitiesProvider) ChooseFromPreferredENIDs( + allENs flow.IdentityList, + executorIDs flow.IdentifierList, +) flow.IdentityList { + var chosenIDs flow.IdentityList + + // filter for both preferred and executor IDs + chosenIDs = allENs.Filter(filter.And( + filter.HasNodeID[flow.Identity](e.preferredENIdentifiers...), + filter.HasNodeID[flow.Identity](executorIDs...), + )) + + if len(chosenIDs) >= MaxNodesCnt { + return chosenIDs + } + + // function to add nodes to chosenIDs if they are not already included + addIfNotExists := func(candidates flow.IdentityList) { + for _, en := range candidates { + _, exists := chosenIDs.ByNodeID(en.NodeID) + if !exists { + chosenIDs = append(chosenIDs, en) + if len(chosenIDs) >= MaxNodesCnt { + return + } + } + } + } + + // add any EN with a receipt + receiptENs := allENs.Filter(filter.HasNodeID[flow.Identity](executorIDs...)) + addIfNotExists(receiptENs) + if len(chosenIDs) >= MaxNodesCnt { + return chosenIDs + } + + // add any preferred node not already selected + preferredENs := allENs.Filter(filter.HasNodeID[flow.Identity](e.preferredENIdentifiers...)) + addIfNotExists(preferredENs) + if len(chosenIDs) >= MaxNodesCnt { + return chosenIDs + } + + // add any EN not already selected + addIfNotExists(allENs) + + return chosenIDs +} diff --git a/engine/common/rpc/execution_node_identities_provider_test.go b/engine/common/rpc/execution_node_identities_provider_test.go new file mode 100644 index 00000000000..2b033e3dac0 --- /dev/null +++ b/engine/common/rpc/execution_node_identities_provider_test.go @@ -0,0 +1,266 @@ +package rpc_test + +import ( + "context" + "testing" + + "github.com/rs/zerolog" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + + "github.com/onflow/flow-go/engine/access/rpc/backend" + commonrpc "github.com/onflow/flow-go/engine/common/rpc" + "github.com/onflow/flow-go/model/flow" + protocol "github.com/onflow/flow-go/state/protocol/mock" + storagemock "github.com/onflow/flow-go/storage/mock" + "github.com/onflow/flow-go/utils/unittest" +) + +// ENIdentitiesProviderSuite is a test suite for testing the ExecutionNodeIdentitiesProvider. +type ENIdentitiesProviderSuite struct { + suite.Suite + + state *protocol.State + snapshot *protocol.Snapshot + log zerolog.Logger + + receipts *storagemock.ExecutionReceipts +} + +func TestHandler(t *testing.T) { + suite.Run(t, new(ENIdentitiesProviderSuite)) +} + +// SetupTest initializes the test suite with mock state and receipts storage. +func (suite *ENIdentitiesProviderSuite) SetupTest() { + suite.log = zerolog.New(zerolog.NewConsoleWriter()) + suite.state = new(protocol.State) + suite.snapshot = new(protocol.Snapshot) + suite.receipts = new(storagemock.ExecutionReceipts) + + header := unittest.BlockHeaderFixture() + params := new(protocol.Params) + params.On("FinalizedRoot").Return(header, nil) + suite.state.On("Params").Return(params) +} + +// TestExecutionNodesForBlockID tests the ExecutionNodesForBlockID function. +// This function is responsible for retrieving execution nodes used to serve +// all API calls that interact with execution nodes. +func (suite *ENIdentitiesProviderSuite) TestExecutionNodesForBlockID() { + totalReceipts := 5 + + block := unittest.BlockFixture() + + // generate one execution node identities for each receipt assuming that each ER is generated by a unique exec node + allExecutionNodes := unittest.IdentityListFixture(totalReceipts, unittest.WithRole(flow.RoleExecution)) + + // one execution result for all receipts for this block + executionResult := unittest.ExecutionResultFixture() + + // generate execution receipts + receipts := make(flow.ExecutionReceiptList, totalReceipts) + for j := 0; j < totalReceipts; j++ { + r := unittest.ReceiptForBlockFixture(&block) + r.ExecutorID = allExecutionNodes[j].NodeID + er := *executionResult + r.ExecutionResult = er + receipts[j] = r + } + + currentAttempt := 0 + attempt1Receipts, attempt2Receipts, attempt3Receipts := receipts, receipts, receipts + + // setup receipts storage mock to return different list of receipts on each call + suite.receipts. + On("ByBlockID", block.ID()).Return( + func(id flow.Identifier) flow.ExecutionReceiptList { + switch currentAttempt { + case 0: + currentAttempt++ + return attempt1Receipts + case 1: + currentAttempt++ + return attempt2Receipts + default: + currentAttempt = 0 + return attempt3Receipts + } + }, + func(id flow.Identifier) error { return nil }) + + suite.snapshot.On("Identities", mock.Anything).Return( + func(filter flow.IdentityFilter[flow.Identity]) flow.IdentityList { + // apply the filter passed in to the list of all the execution nodes + return allExecutionNodes.Filter(filter) + }, + func(flow.IdentityFilter[flow.Identity]) error { return nil }) + suite.state.On("Final").Return(suite.snapshot, nil).Maybe() + + var preferredENIdentifiers flow.IdentifierList + var fixedENIdentifiers flow.IdentifierList + + testExecutionNodesForBlockID := func(preferredENs, fixedENs, expectedENs flow.IdentityList) { + + if preferredENs != nil { + preferredENIdentifiers = preferredENs.NodeIDs() + } + if fixedENs != nil { + fixedENIdentifiers = fixedENs.NodeIDs() + } + + if expectedENs == nil { + expectedENs = flow.IdentityList{} + } + + execNodeIdentitiesProvider := commonrpc.NewExecutionNodeIdentitiesProvider( + suite.log, + suite.state, + suite.receipts, + preferredENIdentifiers, + fixedENIdentifiers, + ) + + allExecNodes, err := execNodeIdentitiesProvider.ExecutionNodesForBlockID(context.Background(), block.ID()) + require.NoError(suite.T(), err) + + execNodeSelectorFactory := backend.NewNodeSelectorFactory(false) + execSelector, err := execNodeSelectorFactory.SelectNodes(allExecNodes) + require.NoError(suite.T(), err) + + actualList := flow.IdentitySkeletonList{} + for actual := execSelector.Next(); actual != nil; actual = execSelector.Next() { + actualList = append(actualList, actual) + } + + { + expectedENs := expectedENs.ToSkeleton() + if len(expectedENs) > commonrpc.MaxNodesCnt { + for _, actual := range actualList { + require.Contains(suite.T(), expectedENs, actual) + } + } else { + require.ElementsMatch(suite.T(), actualList, expectedENs) + } + } + } + // if we don't find sufficient receipts, ExecutionNodesForBlockID should return a list of random ENs + suite.Run("insufficient receipts return random ENs in State", func() { + // return no receipts at all attempts + attempt1Receipts = flow.ExecutionReceiptList{} + attempt2Receipts = flow.ExecutionReceiptList{} + attempt3Receipts = flow.ExecutionReceiptList{} + suite.state.On("AtBlockID", mock.Anything).Return(suite.snapshot) + + execNodeIdentitiesProvider := commonrpc.NewExecutionNodeIdentitiesProvider( + suite.log, + suite.state, + suite.receipts, + flow.IdentifierList{}, + flow.IdentifierList{}, + ) + + allExecNodes, err := execNodeIdentitiesProvider.ExecutionNodesForBlockID(context.Background(), block.ID()) + require.NoError(suite.T(), err) + + execNodeSelectorFactory := backend.NewNodeSelectorFactory(false) + execSelector, err := execNodeSelectorFactory.SelectNodes(allExecNodes) + require.NoError(suite.T(), err) + + actualList := flow.IdentitySkeletonList{} + for actual := execSelector.Next(); actual != nil; actual = execSelector.Next() { + actualList = append(actualList, actual) + } + + require.Equal(suite.T(), len(actualList), commonrpc.MaxNodesCnt) + }) + + // if no preferred or fixed ENs are specified, the ExecutionNodesForBlockID function should + // return the exe node list without a filter + suite.Run("no preferred or fixed ENs", func() { + testExecutionNodesForBlockID(nil, nil, allExecutionNodes) + }) + // if only fixed ENs are specified, the ExecutionNodesForBlockID function should + // return the fixed ENs list + suite.Run("two fixed ENs with zero preferred EN", func() { + // mark the first two ENs as fixed + fixedENs := allExecutionNodes[0:2] + expectedList := fixedENs + testExecutionNodesForBlockID(nil, fixedENs, expectedList) + }) + // if only preferred ENs are specified, the ExecutionNodesForBlockID function should + // return the preferred ENs list + suite.Run("two preferred ENs with zero fixed EN", func() { + // mark the first two ENs as preferred + preferredENs := allExecutionNodes[0:2] + expectedList := allExecutionNodes[0:commonrpc.MaxNodesCnt] + testExecutionNodesForBlockID(preferredENs, nil, expectedList) + }) + // if both are specified, the ExecutionNodesForBlockID function should + // return the preferred ENs list + suite.Run("four fixed ENs of which two are preferred ENs", func() { + // mark the first four ENs as fixed + fixedENs := allExecutionNodes[0:5] + // mark the first two of the fixed ENs as preferred ENs + preferredENs := fixedENs[0:2] + expectedList := fixedENs[0:commonrpc.MaxNodesCnt] + testExecutionNodesForBlockID(preferredENs, fixedENs, expectedList) + }) + // if both are specified, but the preferred ENs don't match the ExecutorIDs in the ER, + // the ExecutionNodesForBlockID function should return the fixed ENs list + suite.Run("four fixed ENs of which two are preferred ENs but have not generated the ER", func() { + // mark the first two ENs as fixed + fixedENs := allExecutionNodes[0:2] + // specify two ENs not specified in the ERs as preferred + preferredENs := unittest.IdentityListFixture(2, unittest.WithRole(flow.RoleExecution)) + // add one more node ID besides of the fixed ENs list cause expected length of the list should be maxNodesCnt + expectedList := append(fixedENs, allExecutionNodes[2]) + testExecutionNodesForBlockID(preferredENs, fixedENs, expectedList) + }) + // if execution receipts are not yet available, the ExecutionNodesForBlockID function should retry twice + suite.Run("retry execution receipt query", func() { + // on first attempt, no execution receipts are available + attempt1Receipts = flow.ExecutionReceiptList{} + // on second attempt ony one is available + attempt2Receipts = flow.ExecutionReceiptList{receipts[0]} + // on third attempt all receipts are available + attempt3Receipts = receipts + currentAttempt = 0 + // mark the first two ENs as preferred + preferredENs := allExecutionNodes[0:2] + expectedList := allExecutionNodes[0:commonrpc.MaxNodesCnt] + testExecutionNodesForBlockID(preferredENs, nil, expectedList) + }) + // if preferredENIdentifiers was set and there are less than maxNodesCnt nodes selected than check the order + // of adding ENs ids + suite.Run("add nodes in the correct order", func() { + // mark the first EN as preferred + preferredENIdentifiers = allExecutionNodes[0:1].NodeIDs() + // mark the fourth EN with receipt + executorIDs := allExecutionNodes[3:4].NodeIDs() + + receiptNodes := allExecutionNodes[3:4] // any EN with a receipt + preferredNodes := allExecutionNodes[0:1] // preferred EN node not already selected + additionalNode := allExecutionNodes[1:2] // any EN not already selected + + expectedOrder := flow.IdentityList{ + receiptNodes[0], + preferredNodes[0], + additionalNode[0], + } + + execNodeIdentitiesProvider := commonrpc.NewExecutionNodeIdentitiesProvider( + suite.log, + suite.state, + suite.receipts, + preferredENIdentifiers, + flow.IdentifierList{}, + ) + + chosenIDs := execNodeIdentitiesProvider.ChooseFromPreferredENIDs(allExecutionNodes, executorIDs) + + require.ElementsMatch(suite.T(), chosenIDs, expectedOrder) + require.Equal(suite.T(), len(chosenIDs), commonrpc.MaxNodesCnt) + }) +} diff --git a/engine/common/stop/stop_control.go b/engine/common/stop/stop_control.go new file mode 100644 index 00000000000..81d5eea52d2 --- /dev/null +++ b/engine/common/stop/stop_control.go @@ -0,0 +1,157 @@ +package stop + +import ( + "fmt" + + "github.com/coreos/go-semver/semver" + "github.com/rs/zerolog" + "go.uber.org/atomic" + + "github.com/onflow/flow-go/module/component" + "github.com/onflow/flow-go/module/counters" + "github.com/onflow/flow-go/module/executiondatasync/execution_data" + "github.com/onflow/flow-go/module/irrecoverable" +) + +type VersionMetadata struct { + // incompatibleBlockHeight is the height of the block that is incompatible with the current node version. + incompatibleBlockHeight uint64 + // updatedVersion is the expected node version to continue working with new blocks. + updatedVersion string +} + +// StopControl is responsible for managing the stopping behavior of the node +// when an incompatible block height is encountered. +type StopControl struct { + component.Component + cm *component.ComponentManager + + log zerolog.Logger + + versionData *atomic.Pointer[VersionMetadata] + + // Notifier for new processed block height + processedHeightChannel chan uint64 + // Signal channel to notify when processing is done + doneProcessingEvents chan struct{} + + // Stores latest processed block height + lastProcessedHeight counters.StrictMonotonousCounter +} + +// NewStopControl creates a new StopControl instance. +// +// Parameters: +// - log: The logger used for logging. +// +// Returns: +// - A pointer to the newly created StopControl instance. +func NewStopControl( + log zerolog.Logger, +) *StopControl { + sc := &StopControl{ + log: log.With(). + Str("component", "stop_control"). + Logger(), + lastProcessedHeight: counters.NewMonotonousCounter(0), + versionData: atomic.NewPointer[VersionMetadata](nil), + processedHeightChannel: make(chan uint64), + doneProcessingEvents: make(chan struct{}), + } + + sc.cm = component.NewComponentManagerBuilder(). + AddWorker(sc.processEvents). + Build() + sc.Component = sc.cm + + return sc +} + +// OnVersionUpdate is called when a version update occurs. +// +// It updates the incompatible block height and the expected node version +// based on the provided height and semver. +// +// Parameters: +// - height: The block height that is incompatible with the current node version. +// - version: The new semantic version object that is expected for compatibility. +func (sc *StopControl) OnVersionUpdate(height uint64, version *semver.Version) { + // If the version was updated, store new version information + if version != nil { + sc.log.Info(). + Uint64("height", height). + Str("semver", version.String()). + Msg("Received version update") + + sc.versionData.Store(&VersionMetadata{ + incompatibleBlockHeight: height, + updatedVersion: version.String(), + }) + return + } + + // If semver is 0, but notification was received, this means that the version update was deleted. + sc.versionData.Store(nil) +} + +// onProcessedBlock is called when a new block is processed block. +// when the last compatible block is processed, the StopControl will cause the node to crash +// +// Parameters: +// - ctx: The context used to signal an irrecoverable error. +func (sc *StopControl) onProcessedBlock(ctx irrecoverable.SignalerContext) { + versionData := sc.versionData.Load() + if versionData == nil { + return + } + + newHeight := sc.lastProcessedHeight.Value() + if newHeight >= versionData.incompatibleBlockHeight-1 { + ctx.Throw(fmt.Errorf("processed block at height %d is incompatible with the current node version, please upgrade to version %s starting from block height %d", + newHeight, versionData.updatedVersion, versionData.incompatibleBlockHeight)) + } +} + +// updateProcessedHeight updates the last processed height and triggers notifications. +// +// Parameters: +// - height: The height of the latest processed block. +func (sc *StopControl) updateProcessedHeight(height uint64) { + select { + case sc.processedHeightChannel <- height: // Successfully sent the height to the channel + case <-sc.doneProcessingEvents: // Process events are done, do not block + } +} + +// RegisterHeightRecorder registers an execution data height recorder with the StopControl. +// +// Parameters: +// - recorder: The execution data height recorder to register. +func (sc *StopControl) RegisterHeightRecorder(recorder execution_data.ProcessedHeightRecorder) { + recorder.SetHeightUpdatesConsumer(sc.updateProcessedHeight) +} + +// processEvents processes incoming events related to block heights and version updates. +// +// Parameters: +// - ctx: The context used to handle irrecoverable errors. +// - ready: A function to signal that the component is ready to start processing events. +func (sc *StopControl) processEvents(ctx irrecoverable.SignalerContext, ready component.ReadyFunc) { + ready() + + defer close(sc.doneProcessingEvents) // Ensure the signal channel is closed when done + + for { + select { + case <-ctx.Done(): + return + case height, ok := <-sc.processedHeightChannel: + if !ok { + return + } + if sc.lastProcessedHeight.Set(height) { + sc.onProcessedBlock(ctx) + } + } + } +} diff --git a/engine/common/stop/stop_control_test.go b/engine/common/stop/stop_control_test.go new file mode 100644 index 00000000000..75d5c6e8026 --- /dev/null +++ b/engine/common/stop/stop_control_test.go @@ -0,0 +1,138 @@ +package stop + +import ( + "context" + "fmt" + "sync" + "testing" + "time" + + "github.com/coreos/go-semver/semver" + "github.com/rs/zerolog" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + + "github.com/onflow/flow-go/module/irrecoverable" + "github.com/onflow/flow-go/utils/unittest" +) + +// RunWithStopControl is a helper function that creates a StopControl instance and runs the provided test function with it. +// +// Parameters: +// - t: The testing context. +// - f: A function that takes a MockSignalerContext and a StopControl, used to run the test logic. +func RunWithStopControl(t *testing.T, f func(ctx *irrecoverable.MockSignalerContext, sc *StopControl)) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + signalerContext := irrecoverable.NewMockSignalerContext(t, ctx) + + f(signalerContext, createStopControl(t, signalerContext)) +} + +// createStopControl creates and starts a new StopControl instance. +// +// Parameters: +// - t: The testing context. +// - signalerContext: The mock context used to simulate signaler behavior. +// +// Returns: +// - A pointer to the newly created and started StopControl instance. +func createStopControl(t *testing.T, signalerContext *irrecoverable.MockSignalerContext) *StopControl { + sc := NewStopControl(zerolog.Nop()) + assert.NotNil(t, sc) + + // Start the StopControl component. + sc.Start(signalerContext) + + return sc +} + +// TestNewStopControl verifies that a new StopControl instance is created correctly and its components are ready. +// +// This test ensures that the StopControl can be initialized and started properly, and that all components are ready +// within a specified time frame. +func TestNewStopControl(t *testing.T) { + RunWithStopControl(t, func(_ *irrecoverable.MockSignalerContext, sc *StopControl) { + unittest.RequireComponentsReadyBefore(t, 2*time.Second, sc) + }) +} + +// TestStopControl_OnVersionUpdate tests the OnVersionUpdate method of the StopControl. +// +// This test covers two scenarios: +// 1. When a valid version update is received, it checks that the version data is stored correctly. +// 2. When a nil version is provided, it checks that the version data is cleared. +func TestStopControl_OnVersionUpdate(t *testing.T) { + RunWithStopControl(t, func(_ *irrecoverable.MockSignalerContext, sc *StopControl) { + + // Case 1: Version is updated + height := uint64(100) + version := semver.New("1.0.0") + + sc.OnVersionUpdate(height, version) + + // Verify that the version data is correctly stored. + versionData := sc.versionData.Load() + assert.NotNil(t, versionData) + assert.Equal(t, height, versionData.incompatibleBlockHeight) + assert.Equal(t, "1.0.0", versionData.updatedVersion) + + // Case 2: Version update is deleted (nil version) + sc.OnVersionUpdate(0, nil) + + // Verify that the version data is cleared. + versionData = sc.versionData.Load() + assert.Nil(t, versionData) + }) +} + +// TestStopControl_OnProcessedBlock tests the onProcessedBlock method of the StopControl. +// +// This test covers multiple scenarios related to processing block heights: +// 1. Verifying that the processed height is updated correctly. +// 2. Ensuring that a lower processed height cannot overwrite a higher one. +// 3. Testing that the StopControl correctly triggers an irrecoverable error (via Throw) when the incompatible block height is reached. +func TestStopControl_OnProcessedBlock(t *testing.T) { + RunWithStopControl(t, func(ctx *irrecoverable.MockSignalerContext, sc *StopControl) { + // Initial block height + height := uint64(10) + + // Update processed height and verify it's stored correctly. + sc.updateProcessedHeight(height) + assert.Equal(t, height, sc.lastProcessedHeight.Value()) + + // Attempt to set a lower processed height, which should not be allowed. + sc.updateProcessedHeight(height - 1) + assert.Equal(t, height, sc.lastProcessedHeight.Value()) + + // Set version metadata with an incompatible height and verify the processed height behavior. + incompatibleHeight := uint64(13) + version := semver.New("1.0.0") + + sc.OnVersionUpdate(incompatibleHeight, version) + height = incompatibleHeight - 2 + sc.updateProcessedHeight(height) + assert.Equal(t, height, sc.lastProcessedHeight.Value()) + + // Prepare to trigger the Throw method when the incompatible block height is processed. + height = incompatibleHeight - 1 + + var wg sync.WaitGroup + wg.Add(1) + + // Expected error message when the incompatible block height is processed. + expectedError := fmt.Errorf("processed block at height %d is incompatible with the current node version, please upgrade to version %s starting from block height %d", height, version.String(), incompatibleHeight) + + // Set expectation that the Throw method will be called with the expected error. + ctx.On("Throw", expectedError).Run(func(args mock.Arguments) { wg.Done() }).Return().Once() + + // Update the processed height to the incompatible height and wait for Throw to be called. + sc.updateProcessedHeight(height) + unittest.RequireReturnsBefore(t, wg.Wait, 100*time.Millisecond, "expect for ctx.Throw before timeout") + + // Verify that the processed height and the Throw method call are correct. + assert.Equal(t, height, sc.lastProcessedHeight.Value()) + ctx.AssertCalled(t, "Throw", expectedError) + }) +} diff --git a/engine/common/version/version_control.go b/engine/common/version/version_control.go index 5bd45901114..0c44aa47c0a 100644 --- a/engine/common/version/version_control.go +++ b/engine/common/version/version_control.go @@ -32,10 +32,15 @@ type VersionControlConsumer func(height uint64, version *semver.Version) // NoHeight represents the maximum possible height for blocks. var NoHeight = uint64(0) +// defaultCompatibilityOverrides stores the list of version compatibility overrides. +// version beacon events who's Major.Minor.Patch version match an entry in this map will be ignored. +// +// IMPORTANT: only add versions to this list if you are certain that the cadence and fvm changes +// deployed during the HCU are backwards compatible for scripts. +var defaultCompatibilityOverrides = map[string]struct{}{} + // VersionControl manages the version control system for the node. // It consumes BlockFinalized events and updates the node's version control based on the latest version beacon. -// -// VersionControl implements the protocol.Consumer and component.Component interfaces. type VersionControl struct { // Noop implements the protocol.Consumer interface with no operations. psEvents.Noop @@ -67,6 +72,10 @@ type VersionControl struct { // startHeight and endHeight define the height boundaries for version compatibility. startHeight *atomic.Uint64 endHeight *atomic.Uint64 + + // compatibilityOverrides stores the list of version compatibility overrides. + // version beacon events who's Major.Minor.Patch version match an entry in this map will be ignored. + compatibilityOverrides map[string]struct{} } var _ protocol.Consumer = (*VersionControl)(nil) @@ -97,6 +106,7 @@ func NewVersionControl( finalizedHeightNotifier: engine.NewNotifier(), startHeight: atomic.NewUint64(NoHeight), endHeight: atomic.NewUint64(NoHeight), + compatibilityOverrides: defaultCompatibilityOverrides, } if vc.nodeVersion == nil { @@ -146,10 +156,7 @@ func (v *VersionControl) initBoundaries( for { vb, err := v.versionBeacons.Highest(processedHeight) if err != nil && !errors.Is(err, storage.ErrNotFound) { - ctx.Throw( - fmt.Errorf( - "failed to get highest version beacon for version control: %w", - err)) + ctx.Throw(fmt.Errorf("failed to get highest version beacon for version control: %w", err)) return err } @@ -175,17 +182,16 @@ func (v *VersionControl) initBoundaries( if err == nil { err = fmt.Errorf("boundary semantic version is nil") } - ctx.Throw( - fmt.Errorf( - "failed to parse semver during version control setup: %w", - err)) + ctx.Throw(fmt.Errorf("failed to parse semver during version control setup: %w", err)) return err } - - compResult := ver.Compare(*v.nodeVersion) processedHeight = vb.SealHeight - 1 - if compResult <= 0 { + if v.isOverridden(ver) { + continue + } + + if ver.Compare(*v.nodeVersion) <= 0 { v.startHeight.Store(boundary.BlockHeight) v.log.Info(). Uint64("startHeight", boundary.BlockHeight). @@ -295,10 +301,7 @@ func (v *VersionControl) blockFinalized( Uint64("height", height). Msg("Failed to get highest version beacon") - ctx.Throw( - fmt.Errorf( - "failed to get highest version beacon for version control: %w", - err)) + ctx.Throw(fmt.Errorf("failed to get highest version beacon for version control: %w", err)) return } @@ -330,13 +333,14 @@ func (v *VersionControl) blockFinalized( } // this should never happen as we already validated the version beacon // when indexing it - ctx.Throw( - fmt.Errorf( - "failed to parse semver: %w", - err)) + ctx.Throw(fmt.Errorf("failed to parse semver: %w", err)) return } + if v.isOverridden(ver) { + continue + } + if ver.Compare(*v.nodeVersion) > 0 { newEndHeight = boundary.BlockHeight - 1 @@ -385,3 +389,24 @@ func (v *VersionControl) EndHeight() uint64 { return endHeight } + +// isOverridden checks if the version is overridden by the compatibility overrides and can be ignored. +func (v *VersionControl) isOverridden(ver *semver.Version) bool { + normalizedVersion := &semver.Version{ + Major: ver.Major, + Minor: ver.Minor, + Patch: ver.Patch, + } + + _, ok := v.compatibilityOverrides[normalizedVersion.String()] + if !ok { + return false + } + + v.log.Info(). + Str("event_version", ver.String()). + Str("override_version", normalizedVersion.String()). + Msg("ignoring version beacon event matching compatibility override") + + return true +} diff --git a/engine/common/version/version_control_test.go b/engine/common/version/version_control_test.go index c6917076a79..ace0712aa30 100644 --- a/engine/common/version/version_control_test.go +++ b/engine/common/version/version_control_test.go @@ -29,6 +29,7 @@ type testCaseConfig struct { nodeVersion string versionEvents []*flow.SealedVersionBeacon + overrides map[string]struct{} expectedStart uint64 expectedEnd uint64 } @@ -184,6 +185,51 @@ func TestVersionControlInitialization(t *testing.T) { expectedStart: sealedRootBlockHeight + 12, expectedEnd: sealedRootBlockHeight + 13, }, + { + name: "start and end version set, start ignored due to override", + nodeVersion: "0.0.2", + versionEvents: []*flow.SealedVersionBeacon{ + VersionBeaconEvent(sealedRootBlockHeight+10, flow.VersionBoundary{BlockHeight: sealedRootBlockHeight + 12, Version: "0.0.1"}), + VersionBeaconEvent(latestBlockHeight-10, flow.VersionBoundary{BlockHeight: latestBlockHeight - 8, Version: "0.0.3"}), + }, + overrides: map[string]struct{}{"0.0.1": {}}, + expectedStart: sealedRootBlockHeight, + expectedEnd: latestBlockHeight - 9, + }, + { + name: "start and end version set, end ignored due to override", + nodeVersion: "0.0.2", + versionEvents: []*flow.SealedVersionBeacon{ + VersionBeaconEvent(sealedRootBlockHeight+10, flow.VersionBoundary{BlockHeight: sealedRootBlockHeight + 12, Version: "0.0.1"}), + VersionBeaconEvent(latestBlockHeight-10, flow.VersionBoundary{BlockHeight: latestBlockHeight - 8, Version: "0.0.3"}), + }, + overrides: map[string]struct{}{"0.0.3": {}}, + expectedStart: sealedRootBlockHeight + 12, + expectedEnd: latestBlockHeight, + }, + { + name: "start and end version set, middle envent ignored due to override", + nodeVersion: "0.0.2", + versionEvents: []*flow.SealedVersionBeacon{ + VersionBeaconEvent(sealedRootBlockHeight+10, flow.VersionBoundary{BlockHeight: sealedRootBlockHeight + 12, Version: "0.0.1"}), + VersionBeaconEvent(latestBlockHeight-3, flow.VersionBoundary{BlockHeight: latestBlockHeight - 1, Version: "0.0.3"}), + VersionBeaconEvent(latestBlockHeight-10, flow.VersionBoundary{BlockHeight: latestBlockHeight - 8, Version: "0.0.4"}), + }, + overrides: map[string]struct{}{"0.0.3": {}}, + expectedStart: sealedRootBlockHeight + 12, + expectedEnd: latestBlockHeight - 9, + }, + { + name: "pre-release version matches overrides", + nodeVersion: "0.0.2", + versionEvents: []*flow.SealedVersionBeacon{ + VersionBeaconEvent(sealedRootBlockHeight+10, flow.VersionBoundary{BlockHeight: sealedRootBlockHeight + 12, Version: "0.0.1-pre-release.0"}), + VersionBeaconEvent(latestBlockHeight-10, flow.VersionBoundary{BlockHeight: latestBlockHeight - 8, Version: "0.0.3"}), + }, + overrides: map[string]struct{}{"0.0.1": {}}, + expectedStart: sealedRootBlockHeight, + expectedEnd: latestBlockHeight - 9, + }, } for _, testCase := range testCases { @@ -217,6 +263,7 @@ func TestVersionControlInitialization(t *testing.T) { versionBeacons: versionBeacons, sealedRootBlockHeight: sealedRootBlockHeight, latestFinalizedBlockHeight: latestBlockHeight, + overrides: testCase.overrides, signalerContext: irrecoverable.NewMockSignalerContext(t, ctx), }) @@ -322,6 +369,72 @@ func generateChecks(testCase testCaseConfig, finalizedRootBlockHeight, latestBlo return checks } +// TestVersionBoundaryReceived tests the behavior of the VersionControl component when a new +// version beacon event is received. +func TestVersionBoundaryReceived(t *testing.T) { + signalCtx := irrecoverable.NewMockSignalerContext(t, context.Background()) + + contract := &versionBeaconContract{} + + // Create version event for initial height + latestHeight := uint64(10) + boundaryHeight := uint64(13) + + vc := createVersionControlComponent(t, versionComponentTestConfigs{ + nodeVersion: "0.0.1", + versionBeacons: contract, + sealedRootBlockHeight: 0, + latestFinalizedBlockHeight: latestHeight, + overrides: map[string]struct{}{"0.0.2": {}}, // skip event at 0.0.2 + signalerContext: signalCtx, + }) + + var assertUpdate func(height uint64, version *semver.Version) + var assertCallbackCalled, assertCallbackNotCalled func() + + // Add a consumer to verify version updates + vc.AddVersionUpdatesConsumer(func(height uint64, version *semver.Version) { + assertUpdate(height, version) + }) + assert.Len(t, vc.consumers, 1) + + // At this point, both start and end heights are unset + + // Add a new boundary, and finalize the block + latestHeight++ // 11 + contract.AddBoundary(latestHeight, flow.VersionBoundary{BlockHeight: boundaryHeight, Version: "0.0.2"}) + + // This event should be skipped due to the override + assertUpdate, assertCallbackNotCalled = generateConsumerIgnoredAssertions(t) + vc.blockFinalized(signalCtx, latestHeight) + assertCallbackNotCalled() + + // Next, add another new boundary and finalize the block + latestHeight++ // 12 + contract.AddBoundary(latestHeight, flow.VersionBoundary{BlockHeight: boundaryHeight, Version: "0.0.3"}) + + assertUpdate, assertCallbackCalled = generateConsumerAssertions(t, boundaryHeight, semver.New("0.0.3")) + vc.blockFinalized(signalCtx, latestHeight) + assertCallbackCalled() + + // Finally, finalize one more block to get past the boundary + latestHeight++ // 13 + vc.blockFinalized(signalCtx, latestHeight) + + // Check compatibility at key heights + compatible, err := vc.CompatibleAtBlock(10) + require.NoError(t, err) + assert.True(t, compatible) + + compatible, err = vc.CompatibleAtBlock(12) + require.NoError(t, err) + assert.True(t, compatible) + + compatible, err = vc.CompatibleAtBlock(13) + require.NoError(t, err) + assert.False(t, compatible) +} + // TestVersionBoundaryUpdated tests the behavior of the VersionControl component when the version is updated. func TestVersionBoundaryUpdated(t *testing.T) { signalCtx := irrecoverable.NewMockSignalerContext(t, context.Background()) @@ -487,6 +600,23 @@ func TestNotificationSkippedForCompatibleVersions(t *testing.T) { assert.True(t, compatible) } +// TestIsOverriden tests the isOverridden method of the VersionControl component correctly matches +// versions +func TestIsOverriden(t *testing.T) { + vc := &VersionControl{ + compatibilityOverrides: map[string]struct{}{"0.0.1": {}}, + } + + assert.True(t, vc.isOverridden(semver.New("0.0.1"))) + assert.True(t, vc.isOverridden(semver.New("0.0.1-pre-release"))) + + assert.False(t, vc.isOverridden(semver.New("0.0.2"))) + assert.False(t, vc.isOverridden(semver.New("0.0.2-pre-release"))) + + assert.False(t, vc.isOverridden(semver.New("1.0.1"))) + assert.False(t, vc.isOverridden(semver.New("0.1.1"))) +} + func generateConsumerAssertions( t *testing.T, boundaryHeight uint64, @@ -507,12 +637,29 @@ func generateConsumerAssertions( return assertUpdate, assertCalled } +func generateConsumerIgnoredAssertions( + t *testing.T, +) (func(uint64, *semver.Version), func()) { + called := false + + assertUpdate := func(uint64, *semver.Version) { + called = true + } + + assertNotCalled := func() { + assert.False(t, called) + } + + return assertUpdate, assertNotCalled +} + // versionComponentTestConfigs contains custom tweaks for version control creation type versionComponentTestConfigs struct { nodeVersion string versionBeacons storage.VersionBeacons sealedRootBlockHeight uint64 latestFinalizedBlockHeight uint64 + overrides map[string]struct{} signalerContext *irrecoverable.MockSignalerContext } @@ -530,6 +677,10 @@ func createVersionControlComponent( ) require.NoError(t, err) + if config.overrides != nil { + vc.compatibilityOverrides = config.overrides + } + // Start the VersionControl component. vc.Start(config.signalerContext) unittest.RequireComponentsReadyBefore(t, 2*time.Second, vc) diff --git a/engine/consensus/compliance/core.go b/engine/consensus/compliance/core.go index 8ed733c8fe9..5ab6d26c269 100644 --- a/engine/consensus/compliance/core.go +++ b/engine/consensus/compliance/core.go @@ -271,7 +271,7 @@ func (c *Core) processBlockAndDescendants(proposal flow.Slashable[*flow.Block]) }) // notify VoteAggregator about the invalid block - err = c.voteAggregator.InvalidBlock(model.ProposalFromFlow(header)) + err = c.voteAggregator.InvalidBlock(model.SignedProposalFromFlow(header)) if err != nil { if mempool.IsBelowPrunedThresholdError(err) { log.Warn().Msg("received invalid block, but is below pruned threshold") @@ -327,7 +327,7 @@ func (c *Core) processBlockProposal(proposal *flow.Block) error { ) defer span.End() - hotstuffProposal := model.ProposalFromFlow(header) + hotstuffProposal := model.SignedProposalFromFlow(header) err := c.validator.ValidateProposal(hotstuffProposal) if err != nil { if model.IsInvalidProposalError(err) { diff --git a/engine/consensus/compliance/core_test.go b/engine/consensus/compliance/core_test.go index 494d1d0e91d..41a57a5ffc7 100644 --- a/engine/consensus/compliance/core_test.go +++ b/engine/consensus/compliance/core_test.go @@ -9,6 +9,7 @@ import ( "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" + "github.com/onflow/flow-go/consensus/hotstuff/helper" hotstuff "github.com/onflow/flow-go/consensus/hotstuff/mocks" "github.com/onflow/flow-go/consensus/hotstuff/model" consensus "github.com/onflow/flow-go/engine/consensus/mock" @@ -285,7 +286,7 @@ func (cs *CoreSuite) TestOnBlockProposalValidParent() { // store the data for retrieval cs.headerDB[block.Header.ParentID] = cs.head - hotstuffProposal := model.ProposalFromFlow(block.Header) + hotstuffProposal := model.SignedProposalFromFlow(block.Header) cs.validator.On("ValidateProposal", hotstuffProposal).Return(nil) cs.voteAggregator.On("AddBlock", hotstuffProposal).Once() cs.hotstuff.On("SubmitProposal", hotstuffProposal) @@ -314,7 +315,7 @@ func (cs *CoreSuite) TestOnBlockProposalValidAncestor() { cs.headerDB[parent.ID()] = parent.Header cs.headerDB[ancestor.ID()] = ancestor.Header - hotstuffProposal := model.ProposalFromFlow(block.Header) + hotstuffProposal := model.SignedProposalFromFlow(block.Header) cs.validator.On("ValidateProposal", hotstuffProposal).Return(nil) cs.voteAggregator.On("AddBlock", hotstuffProposal).Once() cs.hotstuff.On("SubmitProposal", hotstuffProposal) @@ -363,7 +364,7 @@ func (cs *CoreSuite) TestOnBlockProposal_FailsHotStuffValidation() { parent := unittest.BlockWithParentFixture(ancestor.Header) block := unittest.BlockWithParentFixture(parent.Header) proposal := unittest.ProposalFromBlock(block) - hotstuffProposal := model.ProposalFromFlow(block.Header) + hotstuffProposal := model.SignedProposalFromFlow(block.Header) // store the data for retrieval cs.headerDB[parent.ID()] = parent.Header @@ -445,7 +446,7 @@ func (cs *CoreSuite) TestOnBlockProposal_FailsProtocolStateValidation() { parent := unittest.BlockWithParentFixture(ancestor.Header) block := unittest.BlockWithParentFixture(parent.Header) proposal := unittest.ProposalFromBlock(block) - hotstuffProposal := model.ProposalFromFlow(block.Header) + hotstuffProposal := model.SignedProposalFromFlow(block.Header) // store the data for retrieval cs.headerDB[parent.ID()] = parent.Header @@ -551,7 +552,7 @@ func (cs *CoreSuite) TestProcessBlockAndDescendants() { cs.childrenDB[parentID] = append(cs.childrenDB[parentID], pending3) for _, block := range []*flow.Block{parent, block1, block2, block3} { - hotstuffProposal := model.ProposalFromFlow(block.Header) + hotstuffProposal := model.SignedProposalFromFlow(block.Header) cs.validator.On("ValidateProposal", hotstuffProposal).Return(nil) cs.voteAggregator.On("AddBlock", hotstuffProposal).Once() cs.hotstuff.On("SubmitProposal", hotstuffProposal).Once() @@ -601,7 +602,7 @@ func (cs *CoreSuite) TestProposalBufferingOrder() { require.NoError(cs.T(), err, "proposal buffering should pass") // make sure no block is forwarded to hotstuff - cs.hotstuff.AssertNotCalled(cs.T(), "SubmitProposal", model.ProposalFromFlow(&proposal.Block.Header)) + cs.hotstuff.AssertNotCalled(cs.T(), "SubmitProposal", model.SignedProposalFromFlow(&proposal.Block.Header)) } // check that we submit each proposal in a valid order @@ -618,7 +619,7 @@ func (cs *CoreSuite) TestProposalBufferingOrder() { } cs.hotstuff.On("SubmitProposal", mock.Anything).Times(4).Run( func(args mock.Arguments) { - proposal := args.Get(0).(*model.Proposal) + proposal := args.Get(0).(*model.SignedProposal) header := proposal.Block if calls == 0 { // first header processed must be the common parent @@ -626,7 +627,7 @@ func (cs *CoreSuite) TestProposalBufferingOrder() { } // mark the proposal as processed delete(unprocessed, header.BlockID) - cs.headerDB[header.BlockID] = model.ProposalToFlow(proposal) + cs.headerDB[header.BlockID] = helper.SignedProposalToFlow(proposal) calls++ }, ) diff --git a/engine/consensus/compliance/engine_test.go b/engine/consensus/compliance/engine_test.go index a82ccc558c7..e5afb20ad23 100644 --- a/engine/consensus/compliance/engine_test.go +++ b/engine/consensus/compliance/engine_test.go @@ -70,7 +70,7 @@ func (cs *EngineSuite) TestSubmittingMultipleEntries() { for i := 0; i < blockCount; i++ { block := unittest.BlockWithParentFixture(cs.head) proposal := messages.NewBlockProposal(block) - hotstuffProposal := model.ProposalFromFlow(block.Header) + hotstuffProposal := model.SignedProposalFromFlow(block.Header) cs.hotstuff.On("SubmitProposal", hotstuffProposal).Return().Once() cs.voteAggregator.On("AddBlock", hotstuffProposal).Once() cs.validator.On("ValidateProposal", hotstuffProposal).Return(nil).Once() @@ -88,7 +88,7 @@ func (cs *EngineSuite) TestSubmittingMultipleEntries() { block := unittest.BlockWithParentFixture(cs.head) proposal := unittest.ProposalFromBlock(block) - hotstuffProposal := model.ProposalFromFlow(block.Header) + hotstuffProposal := model.SignedProposalFromFlow(block.Header) cs.hotstuff.On("SubmitProposal", hotstuffProposal).Return().Once() cs.voteAggregator.On("AddBlock", hotstuffProposal).Once() cs.validator.On("ValidateProposal", hotstuffProposal).Return(nil).Once() diff --git a/engine/consensus/message_hub/message_hub.go b/engine/consensus/message_hub/message_hub.go index 07fe8c3a387..38cc34a95ca 100644 --- a/engine/consensus/message_hub/message_hub.go +++ b/engine/consensus/message_hub/message_hub.go @@ -436,7 +436,7 @@ func (h *MessageHub) OnOwnProposal(proposal *flow.Header, targetPublicationTime return } - hotstuffProposal := model.ProposalFromFlow(proposal) + hotstuffProposal := model.SignedProposalFromFlow(proposal) // notify vote aggregator that new block proposal is available, in case we are next leader h.voteAggregator.AddBlock(hotstuffProposal) // non-blocking diff --git a/engine/consensus/message_hub/message_hub_test.go b/engine/consensus/message_hub/message_hub_test.go index 68bd1adc59a..e5cd47ca1c1 100644 --- a/engine/consensus/message_hub/message_hub_test.go +++ b/engine/consensus/message_hub/message_hub_test.go @@ -250,7 +250,7 @@ func (s *MessageHubSuite) TestOnOwnProposal() { expectedBroadcastMsg := messages.NewBlockProposal(block) submitted := make(chan struct{}) // closed when proposal is submitted to hotstuff - hotstuffProposal := model.ProposalFromFlow(block.Header) + hotstuffProposal := model.SignedProposalFromFlow(block.Header) s.voteAggregator.On("AddBlock", hotstuffProposal).Once() s.hotstuff.On("SubmitProposal", hotstuffProposal). Run(func(args mock.Arguments) { close(submitted) }). @@ -315,7 +315,7 @@ func (s *MessageHubSuite) TestProcessMultipleMessagesHappyPath() { s.payloads.On("ByBlockID", proposal.Header.ID()).Return(proposal.Payload, nil) // unset chain and height to make sure they are correctly reconstructed - hotstuffProposal := model.ProposalFromFlow(proposal.Header) + hotstuffProposal := model.SignedProposalFromFlow(proposal.Header) s.voteAggregator.On("AddBlock", hotstuffProposal).Once() s.hotstuff.On("SubmitProposal", hotstuffProposal) expectedBroadcastMsg := messages.NewBlockProposal(&proposal) diff --git a/engine/execution/checker/core.go b/engine/execution/checker/core.go index 78ca7475dd9..a6d3d6f6ad6 100644 --- a/engine/execution/checker/core.go +++ b/engine/execution/checker/core.go @@ -36,6 +36,7 @@ func NewCore( // checkMyCommitWithSealedCommit is the main check of the checker engine func checkMyCommitWithSealedCommit( + logger zerolog.Logger, executedBlock *flow.Header, myCommit flow.StateCommitment, sealedCommit flow.StateCommitment, @@ -50,6 +51,11 @@ func checkMyCommitWithSealedCommit( ) } + logger.Info(). + Uint64("height", executedBlock.Height). + Str("block_id", executedBlock.ID().String()). + Msg("execution result matches the sealed result") + // match return nil } @@ -65,7 +71,7 @@ func (c *Core) RunCheck() error { mycommitAtLastSealed, err := c.execState.StateCommitmentByBlockID(lastSealedBlock.ID()) if err == nil { // if last sealed block has been executed, then check if they match - return checkMyCommitWithSealedCommit(lastSealedBlock, mycommitAtLastSealed, seal.FinalState) + return checkMyCommitWithSealedCommit(c.log, lastSealedBlock, mycommitAtLastSealed, seal.FinalState) } // if last sealed block has not been executed, then check if recent executed block has @@ -102,7 +108,7 @@ func (c *Core) RunCheck() error { return fmt.Errorf("could not get my state commitment OnFinalizedBlock, blockID: %v", seal.BlockID) } - return checkMyCommitWithSealedCommit(sealedExecuted, mycommit, sealedCommit) + return checkMyCommitWithSealedCommit(c.log, sealedExecuted, mycommit, sealedCommit) } // findLastSealedBlock finds the last sealed block diff --git a/engine/execution/computation/computer/computer.go b/engine/execution/computation/computer/computer.go index c3e77b9be7f..e863a4d23d1 100644 --- a/engine/execution/computation/computer/computer.go +++ b/engine/execution/computation/computer/computer.go @@ -256,7 +256,6 @@ func (e *blockComputer) queueTransactionRequests( i == len(collection.Transactions)-1) txnIndex += 1 } - } systemCtx := fvm.NewContextFromParent( diff --git a/engine/execution/computation/computer/computer_test.go b/engine/execution/computation/computer/computer_test.go index 6db12a1e528..0d7899100ad 100644 --- a/engine/execution/computation/computer/computer_test.go +++ b/engine/execution/computation/computer/computer_test.go @@ -8,12 +8,12 @@ import ( "testing" "github.com/onflow/cadence" + "github.com/onflow/cadence/common" "github.com/onflow/cadence/encoding/ccf" + "github.com/onflow/cadence/interpreter" "github.com/onflow/cadence/runtime" - "github.com/onflow/cadence/runtime/common" - "github.com/onflow/cadence/runtime/interpreter" - "github.com/onflow/cadence/runtime/sema" - "github.com/onflow/cadence/runtime/stdlib" + "github.com/onflow/cadence/sema" + "github.com/onflow/cadence/stdlib" "github.com/ipfs/boxo/blockstore" "github.com/ipfs/go-datastore" @@ -705,6 +705,7 @@ func TestBlockExecutor_ExecuteBlock(t *testing.T) { }, ), ), + fvm.WithReadVersionFromNodeVersionBeacon(false), ) vm := fvm.NewVirtualMachine() @@ -816,7 +817,9 @@ func TestBlockExecutor_ExecuteBlock(t *testing.T) { runtime.Config{}, func(_ runtime.Config) runtime.Runtime { return rt - }))) + })), + fvm.WithReadVersionFromNodeVersionBeacon(false), + ) vm := fvm.NewVirtualMachine() @@ -929,7 +932,9 @@ func TestBlockExecutor_ExecuteBlock(t *testing.T) { runtime.Config{}, func(_ runtime.Config) runtime.Runtime { return rt - }))) + })), + fvm.WithReadVersionFromNodeVersionBeacon(false), + ) vm := fvm.NewVirtualMachine() diff --git a/engine/execution/computation/manager.go b/engine/execution/computation/manager.go index 1b3765d7b01..e13a2d03791 100644 --- a/engine/execution/computation/manager.go +++ b/engine/execution/computation/manager.go @@ -231,8 +231,7 @@ func DefaultFVMOptions(chainID flow.ChainID, cadenceTracing bool, extensiveTraci reusableRuntime.NewReusableCadenceRuntimePool( ReusableCadenceRuntimePoolSize, runtime.Config{ - TracingEnabled: cadenceTracing, - AttachmentsEnabled: true, + TracingEnabled: cadenceTracing, }, )), fvm.WithEVMEnabled(true), diff --git a/engine/execution/computation/manager_test.go b/engine/execution/computation/manager_test.go index 7aee62d7b40..7bfc6e0aab7 100644 --- a/engine/execution/computation/manager_test.go +++ b/engine/execution/computation/manager_test.go @@ -13,8 +13,8 @@ import ( "github.com/ipfs/go-datastore" dssync "github.com/ipfs/go-datastore/sync" "github.com/onflow/cadence" + "github.com/onflow/cadence/common" jsoncdc "github.com/onflow/cadence/encoding/json" - "github.com/onflow/cadence/runtime/common" "github.com/rs/zerolog" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" diff --git a/engine/execution/computation/metrics/collector.go b/engine/execution/computation/metrics/collector.go new file mode 100644 index 00000000000..8f3438d4658 --- /dev/null +++ b/engine/execution/computation/metrics/collector.go @@ -0,0 +1,130 @@ +package metrics + +import ( + "sync" + + "github.com/rs/zerolog" + + "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/module/component" + "github.com/onflow/flow-go/module/irrecoverable" +) + +type collector struct { + log zerolog.Logger + + collection chan metrics + + mu sync.Mutex + + lowestAvailableHeight uint64 + blocksAtHeight map[uint64]map[flow.Identifier]struct{} + metrics map[flow.Identifier][]TransactionExecutionMetrics +} + +func newCollector( + log zerolog.Logger, + lowestAvailableHeight uint64, +) *collector { + return &collector{ + log: log, + lowestAvailableHeight: lowestAvailableHeight, + + collection: make(chan metrics, 1000), + blocksAtHeight: make(map[uint64]map[flow.Identifier]struct{}), + metrics: make(map[flow.Identifier][]TransactionExecutionMetrics), + } +} + +// Collect should never block because it's called from the execution +func (c *collector) Collect( + blockId flow.Identifier, + blockHeight uint64, + t TransactionExecutionMetrics, +) { + select { + case c.collection <- metrics{ + TransactionExecutionMetrics: t, + blockHeight: blockHeight, + blockId: blockId, + }: + default: + c.log.Warn(). + Uint64("height", blockHeight). + Msg("dropping metrics because the collection channel is full") + } +} + +func (c *collector) metricsCollectorWorker( + ctx irrecoverable.SignalerContext, + ready component.ReadyFunc, +) { + ready() + + for { + select { + case <-ctx.Done(): + return + case m := <-c.collection: + c.collect(m.blockId, m.blockHeight, m.TransactionExecutionMetrics) + } + } +} + +func (c *collector) collect( + blockId flow.Identifier, + blockHeight uint64, + t TransactionExecutionMetrics, +) { + c.mu.Lock() + defer c.mu.Unlock() + + if blockHeight <= c.lowestAvailableHeight { + c.log.Warn(). + Uint64("height", blockHeight). + Uint64("lowestAvailableHeight", c.lowestAvailableHeight). + Msg("received metrics for a block that is older or equal than the most recent block") + return + } + + if _, ok := c.blocksAtHeight[blockHeight]; !ok { + c.blocksAtHeight[blockHeight] = make(map[flow.Identifier]struct{}) + } + c.blocksAtHeight[blockHeight][blockId] = struct{}{} + c.metrics[blockId] = append(c.metrics[blockId], t) +} + +// Pop returns the metrics for the given finalized block at the given height +// and clears all data up to the given height. +func (c *collector) Pop(height uint64, finalizedBlockId flow.Identifier) []TransactionExecutionMetrics { + c.mu.Lock() + defer c.mu.Unlock() + + if height <= c.lowestAvailableHeight { + c.log.Warn(). + Uint64("height", height). + Stringer("finalizedBlockId", finalizedBlockId). + Msg("requested metrics for a finalizedBlockId that is older or equal than the most recent finalizedBlockId") + return nil + } + + // only return metrics for finalized block + metrics := c.metrics[finalizedBlockId] + + c.advanceTo(height) + + return metrics +} + +// advanceTo moves the latest height to the given height +// all data at lower heights will be deleted +func (c *collector) advanceTo(height uint64) { + for c.lowestAvailableHeight < height { + blocks := c.blocksAtHeight[c.lowestAvailableHeight] + for block := range blocks { + delete(c.metrics, block) + } + delete(c.blocksAtHeight, c.lowestAvailableHeight) + c.lowestAvailableHeight++ + } +} diff --git a/engine/execution/computation/metrics/collector_test.go b/engine/execution/computation/metrics/collector_test.go new file mode 100644 index 00000000000..14882c5f1c0 --- /dev/null +++ b/engine/execution/computation/metrics/collector_test.go @@ -0,0 +1,98 @@ +package metrics + +import ( + "context" + "sync" + "testing" + "time" + + "github.com/rs/zerolog" + "github.com/stretchr/testify/require" + + "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/module/irrecoverable" +) + +func Test_CollectorPopOnEmpty(t *testing.T) { + t.Parallel() + + log := zerolog.New(zerolog.NewTestWriter(t)) + latestHeight := uint64(100) + + collector := newCollector(log, latestHeight) + + data := collector.Pop(latestHeight, flow.ZeroID) + require.Nil(t, data) +} + +func Test_CollectorCollection(t *testing.T) { + log := zerolog.New(zerolog.NewTestWriter(t)) + startHeight := uint64(100) + + collector := newCollector(log, startHeight) + + ctx := context.Background() + go func() { + ictx := irrecoverable.NewMockSignalerContext(t, ctx) + collector.metricsCollectorWorker(ictx, func() {}) + }() + + wg := sync.WaitGroup{} + + wg.Add(16 * 16 * 16) + for height := 0; height < 16; height++ { + // for each height we add multiple blocks. Only one block will be popped per height + for block := 0; block < 16; block++ { + // for each block we add multiple transactions + for transaction := 0; transaction < 16; transaction++ { + go func(h, b, t int) { + defer wg.Done() + + block := flow.Identifier{} + block[0] = byte(h) + block[1] = byte(b) + + collector.Collect( + block, + startHeight+1+uint64(h), + TransactionExecutionMetrics{ + ExecutionTime: time.Duration(b + t), + }, + ) + }(height, block, transaction) + } + // wait a bit for the collector to process the data + <-time.After(1 * time.Millisecond) + } + } + + wg.Wait() + // wait a bit for the collector to process the data + <-time.After(10 * time.Millisecond) + + // there should be no data at the start height + data := collector.Pop(startHeight, flow.ZeroID) + require.Nil(t, data) + + for height := 0; height < 16; height++ { + block := flow.Identifier{} + block[0] = byte(height) + // always pop the first block each height + block[1] = byte(0) + + data := collector.Pop(startHeight+1+uint64(height), block) + + require.Len(t, data, 16) + } + + block := flow.Identifier{} + block[0] = byte(15) + block[1] = byte(1) + // height 16 was already popped so there should be no more data for any blocks + data = collector.Pop(startHeight+16, block) + require.Nil(t, data) + + // there should be no data past the last collected height + data = collector.Pop(startHeight+17, flow.ZeroID) + require.Nil(t, data) +} diff --git a/engine/execution/computation/metrics/provider.go b/engine/execution/computation/metrics/provider.go new file mode 100644 index 00000000000..c23a426141d --- /dev/null +++ b/engine/execution/computation/metrics/provider.go @@ -0,0 +1,124 @@ +package metrics + +import ( + "sync" + + "github.com/rs/zerolog" +) + +// provider is responsible for providing the metrics for the rpc endpoint +// it has a circular buffer of metrics for the last N finalized and executed blocks. +type provider struct { + log zerolog.Logger + + mu sync.RWMutex + + bufferSize uint + bufferIndex uint + blockHeightAtBufferIndex uint64 + + buffer [][]TransactionExecutionMetrics +} + +func newProvider( + log zerolog.Logger, + bufferSize uint, + blockHeightAtBufferIndex uint64, +) *provider { + if bufferSize == 0 { + panic("buffer size must be greater than zero") + } + + return &provider{ + log: log, + bufferSize: bufferSize, + blockHeightAtBufferIndex: blockHeightAtBufferIndex, + bufferIndex: 0, + buffer: make([][]TransactionExecutionMetrics, bufferSize), + } +} + +// Push buffers the metrics for the given height. +// The call should ensure height are called in strictly increasing order, otherwise +// metrics for the skipped height will not buffered. +func (p *provider) Push( + height uint64, + data []TransactionExecutionMetrics, +) { + p.mu.Lock() + defer p.mu.Unlock() + + if height <= p.blockHeightAtBufferIndex { + p.log.Warn(). + Uint64("height", height). + Uint64("blockHeightAtBufferIndex", p.blockHeightAtBufferIndex). + Msg("received metrics for a block that is older or equal than the most recent block") + return + } + if height > p.blockHeightAtBufferIndex+1 { + p.log.Warn(). + Uint64("height", height). + Uint64("blockHeightAtBufferIndex", p.blockHeightAtBufferIndex). + Msg("received metrics for a block that is not the next block") + + // Fill in the gap with nil + for i := p.blockHeightAtBufferIndex; i < height-1; i++ { + p.pushData(nil) + } + } + + p.pushData(data) +} + +func (p *provider) pushData(data []TransactionExecutionMetrics) { + p.bufferIndex = (p.bufferIndex + 1) % p.bufferSize + p.blockHeightAtBufferIndex++ + p.buffer[p.bufferIndex] = data +} + +func (p *provider) GetTransactionExecutionMetricsAfter(height uint64) (GetTransactionExecutionMetricsAfterResponse, error) { + p.mu.RLock() + defer p.mu.RUnlock() + + data := make(map[uint64][]TransactionExecutionMetrics) + + if height+1 > p.blockHeightAtBufferIndex { + return data, nil + } + + // start index is the lowest block height that is in the buffer + // missing heights are handled below + startHeight := uint64(0) + // assign startHeight with the lowest buffered height + if p.blockHeightAtBufferIndex > uint64(p.bufferSize) { + startHeight = p.blockHeightAtBufferIndex - uint64(p.bufferSize) + } + + // if the starting index is lower than the height we only need to return the data for + // the blocks that are later than the given height + if height+1 > startHeight { + startHeight = height + 1 + } + + for h := startHeight; h <= p.blockHeightAtBufferIndex; h++ { + // 0 <= diff; because of the bufferSize check above + diff := uint(p.blockHeightAtBufferIndex - h) + + // 0 <= diff < bufferSize; because of the bufferSize check above + // we are about to do a modulo operation with p.bufferSize on p.bufferIndex - diff, but diff could + // be larger than p.bufferIndex, which would result in a negative intermediate value. + // To avoid this, we add p.bufferSize to diff, which will guarantee that (p.bufferSize + p.bufferIndex - diff) + // is always positive, but the modulo operation will still return the same index. + intermediateIndex := p.bufferIndex + p.bufferSize - diff + index := intermediateIndex % p.bufferSize + + d := p.buffer[index] + if len(d) == 0 { + continue + } + + data[h] = p.buffer[index] + } + + return data, nil +} diff --git a/engine/execution/computation/metrics/provider_test.go b/engine/execution/computation/metrics/provider_test.go new file mode 100644 index 00000000000..152c9b45326 --- /dev/null +++ b/engine/execution/computation/metrics/provider_test.go @@ -0,0 +1,109 @@ +package metrics + +import ( + "testing" + "time" + + "github.com/rs/zerolog" + "github.com/stretchr/testify/require" +) + +func Test_ProviderGetOnEmpty(t *testing.T) { + t.Parallel() + + height := uint64(100) + bufferSize := uint(10) + log := zerolog.New(zerolog.NewTestWriter(t)) + + provider := newProvider(log, bufferSize, height) + + for i := 0; uint(i) < bufferSize; i++ { + data, err := provider.GetTransactionExecutionMetricsAfter(height - uint64(i)) + require.NoError(t, err) + require.Len(t, data, 0) + } +} + +func Test_ProviderGetOutOfBounds(t *testing.T) { + t.Parallel() + + height := uint64(100) + bufferSize := uint(10) + log := zerolog.New(zerolog.NewTestWriter(t)) + + provider := newProvider(log, bufferSize, height) + + res, err := provider.GetTransactionExecutionMetricsAfter(height + 1) + require.NoError(t, err) + require.Len(t, res, 0) +} + +func Test_ProviderPushSequential(t *testing.T) { + t.Parallel() + + height := uint64(100) + bufferSize := uint(10) + log := zerolog.New(zerolog.NewTestWriter(t)) + + provider := newProvider(log, bufferSize, height) + + for i := 0; uint(i) < bufferSize; i++ { + data := []TransactionExecutionMetrics{ + { + // Execution time is our label + ExecutionTime: time.Duration(i), + }, + } + + provider.Push(height+uint64(i)+1, data) + } + + data, err := provider.GetTransactionExecutionMetricsAfter(height) + require.Nil(t, err) + for i := 0; uint(i) < bufferSize; i++ { + require.Equal(t, time.Duration(uint(i)), data[height+uint64(i)+1][0].ExecutionTime) + } +} + +func Test_ProviderPushOutOfSequence(t *testing.T) { + t.Parallel() + + height := uint64(100) + bufferSize := uint(10) + log := zerolog.New(zerolog.NewTestWriter(t)) + + provider := newProvider(log, bufferSize, height) + + for i := 0; uint(i) < bufferSize; i++ { + data := []TransactionExecutionMetrics{ + { + ExecutionTime: time.Duration(i), + }, + } + + provider.Push(height+uint64(i)+1, data) + } + + newHeight := height + uint64(bufferSize) + + // Push out of sequence + data := []TransactionExecutionMetrics{ + { + ExecutionTime: time.Duration(newHeight + 2), + }, + } + + // no-op + provider.Push(newHeight, data) + + // skip 1 + provider.Push(newHeight+2, data) + + res, err := provider.GetTransactionExecutionMetricsAfter(height) + require.NoError(t, err) + + require.Len(t, res, int(bufferSize)) + + require.Nil(t, res[newHeight+1]) + require.Equal(t, time.Duration(newHeight+2), res[newHeight+2][0].ExecutionTime) +} diff --git a/engine/execution/computation/metrics/transaction_execution_metrics.go b/engine/execution/computation/metrics/transaction_execution_metrics.go new file mode 100644 index 00000000000..48cd2747600 --- /dev/null +++ b/engine/execution/computation/metrics/transaction_execution_metrics.go @@ -0,0 +1,168 @@ +package metrics + +import ( + "time" + + "github.com/onflow/flow-go/engine" + + cadenceCommon "github.com/onflow/cadence/common" + "github.com/rs/zerolog" + + "github.com/onflow/flow-go/engine/execution/state" + "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/module/component" + "github.com/onflow/flow-go/module/irrecoverable" + "github.com/onflow/flow-go/state/protocol" + psEvents "github.com/onflow/flow-go/state/protocol/events" + "github.com/onflow/flow-go/storage" +) + +type TransactionExecutionMetricsProvider interface { + component.Component + protocol.Consumer + + // GetTransactionExecutionMetricsAfter returns the transaction metrics for all blocks higher than the given height + // It returns a map of block height to a list of transaction execution metrics + // Blocks that are out of scope (only a limited number blocks are kept in memory) are not returned + GetTransactionExecutionMetricsAfter(height uint64) (GetTransactionExecutionMetricsAfterResponse, error) + + // Collect the transaction metrics for the given block + // Collect does not block, it returns immediately + Collect( + blockId flow.Identifier, + blockHeight uint64, + t TransactionExecutionMetrics, + ) +} + +// GetTransactionExecutionMetricsAfterResponse is the response type for GetTransactionExecutionMetricsAfter +// It is a map of block height to a list of transaction execution metrics +type GetTransactionExecutionMetricsAfterResponse = map[uint64][]TransactionExecutionMetrics + +type TransactionExecutionMetrics struct { + TransactionID flow.Identifier + ExecutionTime time.Duration + ExecutionEffortWeights map[cadenceCommon.ComputationKind]uint +} + +type metrics struct { + TransactionExecutionMetrics + blockHeight uint64 + blockId flow.Identifier +} + +// transactionExecutionMetricsProvider is responsible for providing the metrics for the rpc endpoint. +// It has a circular buffer of metrics for the last N finalized and executed blocks. +// The metrics are not guaranteed to be available for all blocks. If the node is just starting up or catching up +// to the latest finalized block, some blocks may not have metrics available. +// The metrics are intended to be used for monitoring and analytics purposes. +type transactionExecutionMetricsProvider struct { + // collector is responsible for collecting the metrics + // the collector collects the metrics from the execution during block execution + // on a finalized and executed block, the metrics are moved to the provider, + // all non-finalized metrics for that height are discarded + *collector + + // provider is responsible for providing the metrics for the rpc endpoint + // it has a circular buffer of metrics for the last N finalized and executed blocks. + *provider + + component.Component + // transactionExecutionMetricsProvider needs to consume BlockFinalized events. + psEvents.Noop + + log zerolog.Logger + + executionState state.FinalizedExecutionState + headers storage.Headers + blockFinalizedNotifier engine.Notifier + + latestFinalizedAndExecutedHeight uint64 +} + +var _ TransactionExecutionMetricsProvider = (*transactionExecutionMetricsProvider)(nil) + +func NewTransactionExecutionMetricsProvider( + log zerolog.Logger, + executionState state.FinalizedExecutionState, + headers storage.Headers, + latestFinalizedAndExecutedHeight uint64, + bufferSize uint, +) TransactionExecutionMetricsProvider { + log = log.With().Str("component", "transaction_execution_metrics_provider").Logger() + + collector := newCollector(log, latestFinalizedAndExecutedHeight) + provider := newProvider(log, bufferSize, latestFinalizedAndExecutedHeight) + + p := &transactionExecutionMetricsProvider{ + collector: collector, + provider: provider, + log: log, + executionState: executionState, + headers: headers, + blockFinalizedNotifier: engine.NewNotifier(), + latestFinalizedAndExecutedHeight: latestFinalizedAndExecutedHeight, + } + + cm := component.NewComponentManagerBuilder() + cm.AddWorker(collector.metricsCollectorWorker) + cm.AddWorker(p.blockFinalizedWorker) + + p.Component = cm.Build() + + return p +} + +func (p *transactionExecutionMetricsProvider) BlockFinalized(*flow.Header) { + p.blockFinalizedNotifier.Notify() +} + +// move data from the collector to the provider +func (p *transactionExecutionMetricsProvider) onBlockExecutedAndFinalized(block flow.Identifier, height uint64) { + data := p.collector.Pop(height, block) + p.provider.Push(height, data) +} + +func (p *transactionExecutionMetricsProvider) blockFinalizedWorker( + ctx irrecoverable.SignalerContext, + ready component.ReadyFunc, +) { + ready() + + for { + select { + case <-ctx.Done(): + return + case <-p.blockFinalizedNotifier.Channel(): + p.onExecutedAndFinalized() + } + } +} + +func (p *transactionExecutionMetricsProvider) onExecutedAndFinalized() { + latestFinalizedAndExecutedHeight, err := p.executionState.GetHighestFinalizedExecuted() + + if err != nil { + p.log.Warn().Err(err).Msg("could not get highest finalized executed") + return + } + + // the latest finalized and executed block could be more than one block further than the last one handled + // step through all blocks between the last one handled and the latest finalized and executed + for height := p.latestFinalizedAndExecutedHeight + 1; height <= latestFinalizedAndExecutedHeight; height++ { + blockID, err := p.headers.BlockIDByHeight(height) + if err != nil { + p.log.Warn(). + Err(err). + Uint64("height", height). + Msg("could not get header by height") + return + } + + p.onBlockExecutedAndFinalized(blockID, height) + + if height == latestFinalizedAndExecutedHeight { + p.latestFinalizedAndExecutedHeight = height + } + } +} diff --git a/engine/execution/ingestion/core.go b/engine/execution/ingestion/core.go index 95d68b30f05..62889ffc479 100644 --- a/engine/execution/ingestion/core.go +++ b/engine/execution/ingestion/core.go @@ -398,6 +398,14 @@ func (e *Core) onBlockExecuted( return nil } +func nonSystemTransactionCount(result flow.ExecutionResult) uint64 { + count := uint64(0) + for _, chunk := range result.Chunks { + count += chunk.NumberOfTransactions + } + return count +} + func (e *Core) onCollection(col *flow.Collection) error { colID := col.ID() e.log.Info(). diff --git a/engine/execution/ingestion/engine.go b/engine/execution/ingestion/engine.go deleted file mode 100644 index f3dfcaf1dd2..00000000000 --- a/engine/execution/ingestion/engine.go +++ /dev/null @@ -1,958 +0,0 @@ -package ingestion - -import ( - "context" - "errors" - "fmt" - "sync" - "time" - - "github.com/rs/zerolog" - "github.com/rs/zerolog/log" - - "github.com/onflow/flow-go/engine" - "github.com/onflow/flow-go/engine/execution" - "github.com/onflow/flow-go/engine/execution/computation" - "github.com/onflow/flow-go/engine/execution/ingestion/stop" - "github.com/onflow/flow-go/engine/execution/ingestion/uploader" - "github.com/onflow/flow-go/engine/execution/provider" - "github.com/onflow/flow-go/engine/execution/state" - "github.com/onflow/flow-go/model/flow" - "github.com/onflow/flow-go/module" - "github.com/onflow/flow-go/module/executiondatasync/execution_data" - "github.com/onflow/flow-go/module/executiondatasync/pruner" - "github.com/onflow/flow-go/module/mempool/entity" - "github.com/onflow/flow-go/module/mempool/queue" - "github.com/onflow/flow-go/module/mempool/stdmap" - "github.com/onflow/flow-go/module/trace" - "github.com/onflow/flow-go/network" - psEvents "github.com/onflow/flow-go/state/protocol/events" - "github.com/onflow/flow-go/storage" - "github.com/onflow/flow-go/utils/logging" -) - -var _ execution_data.ProcessedHeightRecorder = (*Engine)(nil) - -// An Engine receives and saves incoming blocks. -type Engine struct { - psEvents.Noop // satisfy protocol events consumer interface - execution_data.ProcessedHeightRecorder - - unit *engine.Unit - log zerolog.Logger - collectionFetcher CollectionFetcher - headers storage.Headers // see comments on getHeaderByHeight for why we need it - blocks storage.Blocks - collections storage.Collections - computationManager computation.ComputationManager - providerEngine provider.ProviderEngine - mempool *Mempool - execState state.ExecutionState - metrics module.ExecutionMetrics - tracer module.Tracer - extensiveLogging bool - executionDataPruner *pruner.Pruner - uploader *uploader.Manager - stopControl *stop.StopControl - loader BlockLoader -} - -func New( - unit *engine.Unit, - logger zerolog.Logger, - net network.EngineRegistry, - collectionFetcher CollectionFetcher, - headers storage.Headers, - blocks storage.Blocks, - collections storage.Collections, - executionEngine computation.ComputationManager, - providerEngine provider.ProviderEngine, - execState state.ExecutionState, - metrics module.ExecutionMetrics, - tracer module.Tracer, - extLog bool, - pruner *pruner.Pruner, - uploader *uploader.Manager, - stopControl *stop.StopControl, - loader BlockLoader, -) (*Engine, error) { - log := logger.With().Str("engine", "ingestion").Logger() - - mempool := newMempool() - - eng := Engine{ - unit: unit, - log: log, - collectionFetcher: collectionFetcher, - headers: headers, - blocks: blocks, - collections: collections, - computationManager: executionEngine, - providerEngine: providerEngine, - mempool: mempool, - execState: execState, - metrics: metrics, - tracer: tracer, - extensiveLogging: extLog, - executionDataPruner: pruner, - uploader: uploader, - stopControl: stopControl, - loader: loader, - ProcessedHeightRecorder: execution_data.NewProcessedHeightRecorderManager(0), - } - - return &eng, nil -} - -// Ready returns a channel that will close when the engine has -// successfully started. -func (e *Engine) Ready() <-chan struct{} { - if e.stopControl.IsExecutionStopped() { - return e.unit.Ready() - } - - if err := e.uploader.RetryUploads(); err != nil { - e.log.Warn().Msg("failed to re-upload all ComputationResults") - } - - err := e.reloadUnexecutedBlocks() - if err != nil { - e.log.Fatal().Err(err).Msg("failed to load all unexecuted blocks") - } - - return e.unit.Ready() -} - -// Done returns a channel that will close when the engine has -// successfully stopped. -func (e *Engine) Done() <-chan struct{} { - return e.unit.Done() -} - -// ProcessLocal processes an event originating on the local node. -func (e *Engine) ProcessLocal(event interface{}) error { - return fmt.Errorf("ingestion error does not process local events") -} - -// on nodes startup, we need to load all the unexecuted blocks to the execution queues. -// blocks have to be loaded in the way that the parent has been loaded before loading its children -func (e *Engine) reloadUnexecutedBlocks() error { - unexecuted, err := e.loader.LoadUnexecuted(e.unit.Ctx()) - if err != nil { - return fmt.Errorf("could not load unexecuted blocks: %w", err) - } - // it's possible the BlockProcessable is called during the reloading, as the follower engine - // will receive blocks before ingestion engine is ready. - // The problem with that is, since the reloading hasn't finished yet, enqueuing the new block from - // the BlockProcessable callback will fail, because its parent block might have not been reloaded - // to the queues yet. - // So one solution here is to lock the execution queues during reloading, so that if BlockProcessable - // is called before reloading is finished, it will be blocked, which will avoid that edge case. - return e.mempool.Run(func( - blockByCollection *stdmap.BlockByCollectionBackdata, - executionQueues *stdmap.QueuesBackdata, - ) error { - for _, blockID := range unexecuted { - err := e.reloadBlock(blockByCollection, executionQueues, blockID) - if err != nil { - return fmt.Errorf("could not reload block: %v, %w", blockID, err) - } - - e.log.Debug().Hex("block_id", blockID[:]).Msg("reloaded block") - } - - e.log.Info().Int("count", len(unexecuted)).Msg("all unexecuted have been successfully reloaded") - - return nil - }) -} - -func (e *Engine) reloadBlock( - blockByCollection *stdmap.BlockByCollectionBackdata, - executionQueues *stdmap.QueuesBackdata, - blockID flow.Identifier, -) error { - block, err := e.blocks.ByID(blockID) - if err != nil { - return fmt.Errorf("could not get block by ID: %v %w", blockID, err) - } - - // enqueue the block and check if there is any missing collections - missingCollections, err := e.enqueueBlockAndCheckExecutable(blockByCollection, executionQueues, block, false) - - if err != nil { - return fmt.Errorf("could not enqueue block %x on reloading: %w", blockID, err) - } - - // forward the missing collections to requester engine for requesting them from collection nodes, - // adding the missing collections to mempool in order to trigger the block execution as soon as - // all missing collections are received. - err = e.fetchAndHandleCollection(blockID, block.Header.Height, missingCollections, func(collection *flow.Collection) error { - err := e.addCollectionToMempool(collection, blockByCollection) - - if err != nil { - return fmt.Errorf("could not add collection to mempool: %w", err) - } - return nil - }) - - if err != nil { - return fmt.Errorf("could not fetch or handle collection %w", err) - } - return nil -} - -// BlockProcessable handles the new verified blocks (blocks that -// have passed consensus validation) received from the consensus nodes -// NOTE: BlockProcessable might be called multiple times for the same block. -// NOTE: Ready calls reloadUnexecutedBlocks during initialization, which handles dropped protocol events. -func (e *Engine) BlockProcessable(b *flow.Header, _ *flow.QuorumCertificate) { - - // TODO: this should not be blocking: https://github.com/onflow/flow-go/issues/4400 - - // skip if stopControl tells to skip, so that we can avoid fetching collections - // for this block - if !e.stopControl.ShouldExecuteBlock(b.ID(), b.Height) { - return - } - - blockID := b.ID() - newBlock, err := e.blocks.ByID(blockID) - if err != nil { - e.log.Fatal().Err(err).Msgf("could not get incorporated block(%v): %v", blockID, err) - } - - e.log.Info().Hex("block_id", blockID[:]). - Uint64("height", b.Height). - Msg("handling new block") - - err = e.handleBlock(e.unit.Ctx(), newBlock) - if err != nil { - e.log.Error().Err(err).Hex("block_id", blockID[:]).Msg("failed to handle block") - } -} - -// Main handling - -// handle block will process the incoming block. -// the block has passed the consensus validation. -func (e *Engine) handleBlock(ctx context.Context, block *flow.Block) error { - - blockID := block.ID() - log := e.log.With().Hex("block_id", blockID[:]).Logger() - - span, _ := e.tracer.StartBlockSpan(ctx, blockID, trace.EXEHandleBlock) - defer span.End() - - executed, err := e.execState.IsBlockExecuted(block.Header.Height, blockID) - if err != nil { - return fmt.Errorf("could not check whether block is executed: %w", err) - } - - if executed { - log.Debug().Msg("block has been executed already") - return nil - } - - var missingCollections []*flow.CollectionGuarantee - // unexecuted block - // acquiring the lock so that there is only one process modifying the queue - err = e.mempool.Run(func( - blockByCollection *stdmap.BlockByCollectionBackdata, - executionQueues *stdmap.QueuesBackdata, - ) error { - missing, err := e.enqueueBlockAndCheckExecutable(blockByCollection, executionQueues, block, false) - if err != nil { - return err - } - missingCollections = missing - return nil - }) - - if err != nil { - return fmt.Errorf("could not enqueue block %v: %w", blockID, err) - } - - return e.addOrFetch(blockID, block.Header.Height, missingCollections) -} - -func (e *Engine) enqueueBlockAndCheckExecutable( - blockByCollection *stdmap.BlockByCollectionBackdata, - executionQueues *stdmap.QueuesBackdata, - block *flow.Block, - checkStateSync bool, -) ([]*flow.CollectionGuarantee, error) { - executableBlock := &entity.ExecutableBlock{ - Block: block, - CompleteCollections: make(map[flow.Identifier]*entity.CompleteCollection), - } - - blockID := executableBlock.ID() - - lg := e.log.With(). - Hex("block_id", blockID[:]). - Uint64("block_height", executableBlock.Block.Header.Height). - Logger() - - // adding the block to the queue, - queue, added, head := enqueue(executableBlock, executionQueues) - - // if it's not added, it means the block is not a new block, it already - // exists in the queue, then bail - if !added { - log.Debug().Hex("block_id", logging.Entity(executableBlock)). - Int("block_height", int(executableBlock.Height())). - Msg("block already exists in the execution queue") - return nil, nil - } - - firstUnexecutedHeight := queue.Head.Item.Height() - - // check if a block is executable. - // a block is executable if the following conditions are all true - // 1) the parent state commitment is ready - // 2) the collections for the block payload are ready - // 3) the child block is ready for querying the randomness - - // check if the block's parent has been executed. (we can't execute the block if the parent has - // not been executed yet) - // check if there is a statecommitment for the parent block - parentCommitment, err := e.execState.StateCommitmentByBlockID(block.Header.ParentID) - - // if we found the statecommitment for the parent block, then add it to the executable block. - if err == nil { - executableBlock.StartState = &parentCommitment - } else if errors.Is(err, storage.ErrNotFound) { - // the parent block is an unexecuted block. - // if the queue only has one block, and its parent doesn't - // exist in the queue, then we need to load the block from the storage. - _, ok := queue.Nodes[blockID] - if !ok { - lg.Error().Msgf("an unexecuted parent block is missing in the queue") - } - } else { - // if there is exception, then crash - lg.Fatal().Err(err).Msg("unexpected error while accessing storage, shutting down") - } - - // check if we have all the collections for the block, and request them if there is missing. - missingCollections, err := e.matchAndFindMissingCollections(executableBlock, blockByCollection) - if err != nil { - return nil, fmt.Errorf("cannot send collection requests: %w", err) - } - - complete := false - - // if newly enqueued block is inside any existing queue, we should skip now and wait - // for parent to finish execution - if head { - // execute the block if the block is ready to be executed - complete = e.executeBlockIfComplete(executableBlock) - } - - lg.Info(). - // if the execution is halt, but the queue keeps growing, we could check which block - // hasn't been executed. - Uint64("first_unexecuted_in_queue", firstUnexecutedHeight). - Bool("complete", complete). - Bool("head_of_queue", head). - Int("cols", len(executableBlock.Block.Payload.Guarantees)). - Int("missing_cols", len(missingCollections)). - Msg("block is enqueued") - - return missingCollections, nil -} - -// executeBlock will execute the block. -// When finish executing, it will check if the children becomes executable and execute them if yes. -func (e *Engine) executeBlock( - ctx context.Context, - executableBlock *entity.ExecutableBlock, -) { - - // don't execute the block if the stop control says no - if !e.stopControl.ShouldExecuteBlock(executableBlock.Block.Header.ID(), executableBlock.Block.Header.Height) { - return - } - - lg := e.log.With(). - Hex("block_id", logging.Entity(executableBlock)). - Uint64("height", executableBlock.Block.Header.Height). - Int("collections", len(executableBlock.CompleteCollections)). - Logger() - - lg.Info().Msg("executing block") - - startedAt := time.Now() - - span, ctx := e.tracer.StartSpanFromContext(ctx, trace.EXEExecuteBlock) - defer span.End() - - parentID := executableBlock.Block.Header.ParentID - parentErID, err := e.execState.GetExecutionResultID(ctx, parentID) - if err != nil { - lg.Err(err). - Str("parentID", parentID.String()). - Msg("could not get execution result ID for parent block") - return - } - - snapshot := e.execState.NewStorageSnapshot(*executableBlock.StartState, - executableBlock.Block.Header.ParentID, - executableBlock.Block.Header.Height-1, - ) - - computationResult, err := e.computationManager.ComputeBlock( - ctx, - parentErID, - executableBlock, - snapshot) - if err != nil { - lg.Err(err).Msg("error while computing block") - return - } - - wg := sync.WaitGroup{} - wg.Add(1) - defer wg.Wait() - - go func() { - defer wg.Done() - err := e.uploader.Upload(ctx, computationResult) - if err != nil { - lg.Err(err).Msg("error while uploading block") - // continue processing. uploads should not block execution - } - }() - - err = e.saveExecutionResults(ctx, computationResult) - if errors.Is(err, storage.ErrDataMismatch) { - lg.Fatal().Err(err).Msg("fatal: trying to store different results for the same block") - } - - if err != nil { - lg.Err(err).Msg("error while handing computation results") - return - } - - receipt := computationResult.ExecutionReceipt - broadcasted, err := e.providerEngine.BroadcastExecutionReceipt( - ctx, executableBlock.Block.Header.Height, receipt) - if err != nil { - lg.Err(err).Msg("critical: failed to broadcast the receipt") - } - - finalEndState := computationResult.CurrentEndState() - lg.Info(). - Hex("parent_block", executableBlock.Block.Header.ParentID[:]). - Int("collections", len(executableBlock.Block.Payload.Guarantees)). - Hex("start_state", executableBlock.StartState[:]). - Hex("final_state", finalEndState[:]). - Hex("receipt_id", logging.Entity(receipt)). - Hex("result_id", logging.Entity(receipt.ExecutionResult)). - Hex("execution_data_id", receipt.ExecutionResult.ExecutionDataID[:]). - Bool("state_changed", finalEndState != *executableBlock.StartState). - Uint64("num_txs", nonSystemTransactionCount(receipt.ExecutionResult)). - Bool("broadcasted", broadcasted). - Int64("timeSpentInMS", time.Since(startedAt).Milliseconds()). - Msg("block executed") - - e.stopControl.OnBlockExecuted(executableBlock.Block.Header) - - err = e.onBlockExecuted(executableBlock, finalEndState) - if err != nil { - lg.Err(err).Msg("failed in process block's children") - } - - if e.executionDataPruner != nil { - e.OnBlockProcessed(executableBlock.Height()) - } - - e.unit.Ctx() - -} - -func nonSystemTransactionCount(result flow.ExecutionResult) uint64 { - count := uint64(0) - for _, chunk := range result.Chunks { - count += chunk.NumberOfTransactions - } - return count -} - -// we've executed the block, now we need to check: -// 1. whether the state syncing can be turned off -// 2. whether its children can be executed -// the executionQueues stores blocks as a tree: -// -// 10 <- 11 <- 12 -// ^-- 13 -// 14 <- 15 <- 16 -// -// if block 10 is the one just executed, then we will remove it from the queue, and add -// its children back, meaning the tree will become: -// -// 11 <- 12 -// 13 -// 14 <- 15 <- 16 - -func (e *Engine) onBlockExecuted( - executed *entity.ExecutableBlock, - finalState flow.StateCommitment, -) error { - - e.metrics.ExecutionStorageStateCommitment(int64(len(finalState))) - e.metrics.ExecutionLastExecutedBlockHeight(executed.Block.Header.Height) - - missingCollections := make(map[*entity.ExecutableBlock][]*flow.CollectionGuarantee) - err := e.mempool.Run( - func( - blockByCollection *stdmap.BlockByCollectionBackdata, - executionQueues *stdmap.QueuesBackdata, - ) error { - // find the block that was just executed - executionQueue, exists := executionQueues.ByID(executed.ID()) - if !exists { - logQueueState(e.log, executionQueues, executed.ID()) - // when the block no longer exists in the queue, it means there was a race condition that - // two onBlockExecuted was called for the same block, and one process has already removed the - // block from the queue, so we will print an error here - return fmt.Errorf("block has been executed already, no longer exists in the queue") - } - - // dismount the executed block and all its children - _, newQueues := executionQueue.Dismount() - - // go through each children, add them back to the queue, and check - // if the children is executable - for _, queue := range newQueues { - queueID := queue.ID() - added := executionQueues.Add(queueID, queue) - if !added { - // blocks should be unique in execution queues, if we dismount all the children blocks, then - // add it back to the queues, then it should always be able to add. - // If not, then there is a bug that the queues have duplicated blocks - return fmt.Errorf("fatal error - child block already in execution queue") - } - - // the parent block has been executed, update the StartState of - // each child block. - child := queue.Head.Item.(*entity.ExecutableBlock) - child.StartState = &finalState - - missing, err := e.matchAndFindMissingCollections(child, blockByCollection) - if err != nil { - return fmt.Errorf("cannot send collection requests: %w", err) - } - if len(missing) > 0 { - missingCollections[child] = append(missingCollections[child], missing...) - } - - completed := e.executeBlockIfComplete(child) - if !completed { - e.log.Debug(). - Hex("executed_block", logging.Entity(executed)). - Hex("child_block", logging.Entity(child)). - Msg("child block is not ready to be executed yet") - } else { - e.log.Debug(). - Hex("executed_block", logging.Entity(executed)). - Hex("child_block", logging.Entity(child)). - Msg("child block is ready to be executed") - } - } - - // remove the executed block - executionQueues.Remove(executed.ID()) - - return nil - }) - - if err != nil { - e.log.Fatal().Err(err). - Hex("block", logging.Entity(executed)). - Uint64("height", executed.Block.Header.Height). - Msg("error while requeueing blocks after execution") - } - - for child, missing := range missingCollections { - err := e.addOrFetch(child.ID(), child.Block.Header.Height, missing) - if err != nil { - return fmt.Errorf("fail to add missing collections: %w", err) - } - } - - return nil -} - -// executeBlockIfComplete checks whether the block is ready to be executed. -// if yes, execute the block -// return a bool indicates whether the block was completed -func (e *Engine) executeBlockIfComplete(eb *entity.ExecutableBlock) bool { - - if eb.Executing { - return false - } - - // if don't have the delta, then check if everything is ready for executing - // the block - if eb.IsComplete() { - - if e.extensiveLogging { - e.logExecutableBlock(eb) - } - - // no external synchronisation is used because this method must be run in a thread-safe context - eb.Executing = true - - e.unit.Launch(func() { - e.executeBlock(e.unit.Ctx(), eb) - }) - return true - } - return false -} - -// OnCollection is a callback for handling the collections requested by the -// collection requester. -func (e *Engine) OnCollection(originID flow.Identifier, entity flow.Entity) { - // convert entity to strongly typed collection - collection, ok := entity.(*flow.Collection) - if !ok { - e.log.Error().Msgf("invalid entity type (%T)", entity) - return - } - - // no need to validate the origin ID, since the collection requester has - // checked the origin must be a collection node. - - err := e.handleCollection(originID, collection) - if err != nil { - e.log.Error().Err(err).Msg("could not handle collection") - } -} - -// a block can't be executed if its collection is missing. -// since a collection can belong to multiple blocks, we need to -// find all the blocks that are needing this collection, and then -// check if any of these block becomes executable and execute it if -// is. -func (e *Engine) handleCollection( - originID flow.Identifier, - collection *flow.Collection, -) error { - collID := collection.ID() - - span, _ := e.tracer.StartCollectionSpan(context.Background(), collID, trace.EXEHandleCollection) - defer span.End() - - lg := e.log.With().Hex("collection_id", collID[:]).Logger() - - lg.Info().Hex("sender", originID[:]).Int("len", collection.Len()).Msg("handle collection") - defer func(startTime time.Time) { - lg.Info().TimeDiff("duration", time.Now(), startTime).Msg("collection handled") - }(time.Now()) - - // TODO: bail if have seen this collection before. - err := e.collections.Store(collection) - if err != nil { - return fmt.Errorf("cannot store collection: %w", err) - } - - return e.mempool.BlockByCollection.Run( - func(backdata *stdmap.BlockByCollectionBackdata) error { - return e.addCollectionToMempool(collection, backdata) - }, - ) -} - -func (e *Engine) addCollectionToMempool( - collection *flow.Collection, - backdata *stdmap.BlockByCollectionBackdata, -) error { - collID := collection.ID() - blockByCollectionID, exists := backdata.ByID(collID) - - // if we don't find any block for this collection, then - // means we don't need this collection any more. - // or it was ejected from the mempool when it was full. - // either way, we will return - if !exists { - return nil - } - - for _, executableBlock := range blockByCollectionID.ExecutableBlocks { - blockID := executableBlock.ID() - - completeCollection, ok := executableBlock.CompleteCollections[collID] - if !ok { - return fmt.Errorf("cannot handle collection: internal inconsistency - collection pointing to block %v which does not contain said collection", - blockID) - } - - e.metrics.UpdateCollectionMaxHeight(executableBlock.Block.Header.Height) - - if completeCollection.IsCompleted() { - // already received transactions for this collection - continue - } - - // update the transactions of the collection - // Note: it's guaranteed the transactions are for this collection, because - // the collection id matches with the CollectionID from the collection guarantee - completeCollection.Transactions = collection.Transactions - - // check if the block becomes executable - _ = e.executeBlockIfComplete(executableBlock) - } - - // since we've received this collection, remove it from the index - // this also prevents from executing the same block twice, because the second - // time when the collection arrives, it will not be found in the blockByCollectionID - // index. - backdata.Remove(collID) - - return nil -} - -func newQueue(blockify queue.Blockify, queues *stdmap.QueuesBackdata) ( - *queue.Queue, - bool, -) { - q := queue.NewQueue(blockify) - qID := q.ID() - return q, queues.Add(qID, q) -} - -// enqueue adds a block to the queues, return the queue that includes the block and booleans -// * is block new one (it's not already enqueued, not a duplicate) -// * is head of the queue (new queue has been created) -// -// Queues are chained blocks. Since a block can't be executable until its parent has been -// executed, the chained structure allows us to only check the head of each queue to see if -// any block becomes executable. -// for instance we have one queue whose head is A: -// -// A <- B <- C -// ^- D <- E -// -// If we receive E <- F, then we will add it to the queue: -// -// A <- B <- C -// ^- D <- E <- F -// -// Even through there are 6 blocks, we only need to check if block A becomes executable. -// when the parent block isn't in the queue, we add it as a new queue. for instance, if -// we receive H <- G, then the queues will become: -// -// A <- B <- C -// ^- D <- E -// G -func enqueue(blockify queue.Blockify, queues *stdmap.QueuesBackdata) ( - *queue.Queue, - bool, - bool, -) { - for _, queue := range queues.All() { - if stored, isNew := queue.TryAdd(blockify); stored { - return queue, isNew, false - } - } - queue, isNew := newQueue(blockify, queues) - return queue, isNew, true -} - -// check if the block's collections have been received, -// if yes, add the collection to the executable block -// if no, fetch the collection. -// if a block has 3 collection, it would be 3 reqs to fetch them. -// mark the collection belongs to the block, -// mark the block contains this collection. -// It returns the missing collections to be fetched -// TODO: to rename -func (e *Engine) matchAndFindMissingCollections( - executableBlock *entity.ExecutableBlock, - collectionsBackdata *stdmap.BlockByCollectionBackdata, -) ([]*flow.CollectionGuarantee, error) { - missingCollections := make([]*flow.CollectionGuarantee, 0, len(executableBlock.Block.Payload.Guarantees)) - - for _, guarantee := range executableBlock.Block.Payload.Guarantees { - coll := &entity.CompleteCollection{ - Guarantee: guarantee, - } - executableBlock.CompleteCollections[guarantee.ID()] = coll - - // check if we have requested this collection before. - // blocksNeedingCollection stores all the blocks that contain this collection - - if blocksNeedingCollection, exists := collectionsBackdata.ByID(guarantee.ID()); exists { - // if we've requested this collection, it means other block might also contain this collection. - // in this case, add this block to the map so that when the collection is received, - // we could update the executable block - blocksNeedingCollection.ExecutableBlocks[executableBlock.ID()] = executableBlock - - // since the collection is still being requested, we don't have the transactions - // yet, so exit - continue - } - - // the storage doesn't have this collection, meaning this is our first time seeing this - // collection guarantee, create an entry to store in collectionsBackdata in order to - // update the executable blocks when the collection is received. - blocksNeedingCollection := &entity.BlocksByCollection{ - CollectionID: guarantee.ID(), - ExecutableBlocks: map[flow.Identifier]*entity.ExecutableBlock{executableBlock.ID(): executableBlock}, - } - - added := collectionsBackdata.Add(blocksNeedingCollection.ID(), blocksNeedingCollection) - if !added { - // sanity check, should not happen, unless mempool implementation has a bug - return nil, fmt.Errorf("collection already mapped to block") - } - - missingCollections = append(missingCollections, guarantee) - } - - return missingCollections, nil -} - -// save the execution result of a block -func (e *Engine) saveExecutionResults( - ctx context.Context, - result *execution.ComputationResult, -) error { - span, childCtx := e.tracer.StartSpanFromContext(ctx, trace.EXESaveExecutionResults) - defer span.End() - - e.log.Debug(). - Hex("block_id", logging.Entity(result.ExecutableBlock)). - Msg("received computation result") - - for _, event := range result.ExecutionResult.ServiceEvents { - e.log.Info(). - Uint64("block_height", result.ExecutableBlock.Height()). - Hex("block_id", logging.Entity(result.ExecutableBlock)). - Str("event_type", event.Type.String()). - Msg("service event emitted") - } - - err := e.execState.SaveExecutionResults(childCtx, result) - if err != nil { - return fmt.Errorf("cannot persist execution state: %w", err) - } - - finalEndState := result.CurrentEndState() - e.log.Debug(). - Hex("block_id", logging.Entity(result.ExecutableBlock)). - Hex("start_state", result.ExecutableBlock.StartState[:]). - Hex("final_state", finalEndState[:]). - Msg("saved computation results") - - return nil -} - -// logExecutableBlock logs all data about an executable block -// over time we should skip this -func (e *Engine) logExecutableBlock(eb *entity.ExecutableBlock) { - // log block - e.log.Debug(). - Hex("block_id", logging.Entity(eb)). - Hex("prev_block_id", logging.ID(eb.Block.Header.ParentID)). - Uint64("block_height", eb.Block.Header.Height). - Int("number_of_collections", len(eb.Collections())). - RawJSON("block_header", logging.AsJSON(eb.Block.Header)). - Msg("extensive log: block header") - - // logs transactions - for i, col := range eb.Collections() { - for j, tx := range col.Transactions { - e.log.Debug(). - Hex("block_id", logging.Entity(eb)). - Int("block_height", int(eb.Block.Header.Height)). - Hex("prev_block_id", logging.ID(eb.Block.Header.ParentID)). - Int("collection_index", i). - Int("tx_index", j). - Hex("collection_id", logging.ID(col.Guarantee.CollectionID)). - Hex("tx_hash", logging.Entity(tx)). - Hex("start_state_commitment", eb.StartState[:]). - RawJSON("transaction", logging.AsJSON(tx)). - Msg("extensive log: executed tx content") - } - } -} - -// addOrFetch checks if there are stored collections for the given guarantees, if there is, -// forward them to mempool to process the collection, otherwise fetch the collections. -// any error returned are exception -func (e *Engine) addOrFetch( - blockID flow.Identifier, - height uint64, - guarantees []*flow.CollectionGuarantee, -) error { - return e.fetchAndHandleCollection(blockID, height, guarantees, func(collection *flow.Collection) error { - err := e.mempool.BlockByCollection.Run( - func(backdata *stdmap.BlockByCollectionBackdata) error { - return e.addCollectionToMempool(collection, backdata) - }) - - if err != nil { - return fmt.Errorf("could not add collection to mempool: %w", err) - } - return nil - }) -} - -// addOrFetch checks if there are stored collections for the given guarantees, if there is, -// forward them to the handler to process the collection, otherwise fetch the collections. -// any error returned are exception -func (e *Engine) fetchAndHandleCollection( - blockID flow.Identifier, - height uint64, - guarantees []*flow.CollectionGuarantee, - handleCollection func(*flow.Collection) error, -) error { - fetched := false - for _, guarantee := range guarantees { - // if we've requested this collection, we will store it in the storage, - // so check the storage to see whether we've seen it. - collection, err := e.collections.ByID(guarantee.CollectionID) - - if err == nil { - // we found the collection from storage, forward this collection to handler - err = handleCollection(collection) - if err != nil { - return fmt.Errorf("could not handle collection: %w", err) - } - - continue - } - - // check if there was exception - if !errors.Is(err, storage.ErrNotFound) { - return fmt.Errorf("error while querying for collection: %w", err) - } - - err = e.collectionFetcher.FetchCollection(blockID, height, guarantee) - if err != nil { - return fmt.Errorf("could not fetch collection: %w", err) - } - fetched = true - } - - // make sure that the requests are dispatched immediately by the requester - if fetched { - e.collectionFetcher.Force() - e.metrics.ExecutionCollectionRequestSent() - } - - return nil -} - -func logQueueState(log zerolog.Logger, queues *stdmap.QueuesBackdata, blockID flow.Identifier) { - all := queues.All() - - log.With().Hex("queue_state__executed_block_id", blockID[:]).Int("count", len(all)).Logger() - for i, queue := range all { - log.Error().Msgf("%v-th queue state: %v", i, queue.String()) - } -} diff --git a/engine/execution/ingestion/engine_test.go b/engine/execution/ingestion/engine_test.go deleted file mode 100644 index b7d5a3665d6..00000000000 --- a/engine/execution/ingestion/engine_test.go +++ /dev/null @@ -1,827 +0,0 @@ -package ingestion - -import ( - "context" - "crypto/rand" - "fmt" - "sync" - "testing" - "time" - - "github.com/onflow/crypto" - "github.com/rs/zerolog" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" - - enginePkg "github.com/onflow/flow-go/engine" - "github.com/onflow/flow-go/engine/execution" - computation "github.com/onflow/flow-go/engine/execution/computation/mock" - "github.com/onflow/flow-go/engine/execution/ingestion/loader" - "github.com/onflow/flow-go/engine/execution/ingestion/mocks" - "github.com/onflow/flow-go/engine/execution/ingestion/stop" - "github.com/onflow/flow-go/engine/execution/ingestion/uploader" - uploadermock "github.com/onflow/flow-go/engine/execution/ingestion/uploader/mock" - provider "github.com/onflow/flow-go/engine/execution/provider/mock" - stateMock "github.com/onflow/flow-go/engine/execution/state/mock" - "github.com/onflow/flow-go/fvm/storage/snapshot" - "github.com/onflow/flow-go/model/flow" - "github.com/onflow/flow-go/module/mempool/entity" - "github.com/onflow/flow-go/module/metrics" - "github.com/onflow/flow-go/module/trace" - "github.com/onflow/flow-go/network/mocknetwork" - protocol "github.com/onflow/flow-go/state/protocol/mock" - storageerr "github.com/onflow/flow-go/storage" - storage "github.com/onflow/flow-go/storage/mock" - "github.com/onflow/flow-go/utils/unittest" -) - -type testingContext struct { - t *testing.T - engine *Engine - headers *storage.Headers - blocks *storage.Blocks - collections *mocks.MockCollectionStore - state *protocol.State - computationManager *computation.ComputationManager - providerEngine *provider.ProviderEngine - executionState *stateMock.ExecutionState - stopControl *stop.StopControl - uploadMgr *uploader.Manager - fetcher *mocks.MockFetcher - - mu *sync.Mutex -} - -func runWithEngine(t *testing.T, f func(testingContext)) { - - net := new(mocknetwork.EngineRegistry) - - // generates signing identity including staking key for signing - seed := make([]byte, crypto.KeyGenSeedMinLen) - n, err := rand.Read(seed) - require.Equal(t, n, crypto.KeyGenSeedMinLen) - require.NoError(t, err) - sk, err := crypto.GeneratePrivateKey(crypto.BLSBLS12381, seed) - require.NoError(t, err) - myIdentity := unittest.IdentityFixture() - myIdentity.Role = flow.RoleExecution - myIdentity.StakingPubKey = sk.PublicKey() - - headers := storage.NewHeaders(t) - blocks := storage.NewBlocks(t) - collections := mocks.NewMockCollectionStore() - - computationManager := computation.NewComputationManager(t) - providerEngine := provider.NewProviderEngine(t) - protocolState := protocol.NewState(t) - executionState := stateMock.NewExecutionState(t) - - var engine *Engine - - defer func() { - unittest.AssertClosesBefore(t, engine.Done(), 5*time.Second, "expect to stop before timeout") - computationManager.AssertExpectations(t) - protocolState.AssertExpectations(t) - executionState.AssertExpectations(t) - providerEngine.AssertExpectations(t) - }() - - log := unittest.Logger() - metrics := metrics.NewNoopCollector() - - tracer, err := trace.NewTracer(log, "test", "test", trace.SensitivityCaptureAll) - require.NoError(t, err) - - unit := enginePkg.NewUnit() - stopControl := stop.NewStopControl( - unit, - time.Second, - zerolog.Nop(), - executionState, - headers, - nil, - nil, - &flow.Header{Height: 1}, - false, - false, - ) - - uploadMgr := uploader.NewManager(trace.NewNoopTracer()) - - fetcher := mocks.NewMockFetcher() - loader := loader.NewUnexecutedLoader(log, protocolState, headers, executionState) - - engine, err = New( - unit, - log, - net, - fetcher, - headers, - blocks, - collections, - computationManager, - providerEngine, - executionState, - metrics, - tracer, - false, - nil, - uploadMgr, - stopControl, - loader, - ) - require.NoError(t, err) - - f(testingContext{ - t: t, - engine: engine, - headers: headers, - blocks: blocks, - collections: collections, - state: protocolState, - computationManager: computationManager, - providerEngine: providerEngine, - executionState: executionState, - uploadMgr: uploadMgr, - stopControl: stopControl, - fetcher: fetcher, - - mu: &sync.Mutex{}, - }) - - <-engine.Done() -} - -// TestExecuteOneBlock verifies after collection is received, -// block is executed, uploaded, and broadcasted -func TestExecuteOneBlock(t *testing.T) { - runWithEngine(t, func(ctx testingContext) { - // create a mocked storage that has similar behavior as the real execution state. - // the mocked storage allows us to prepare results for the prepared blocks, so that - // the mocked methods know what to return, and it also allows us to verify that the - // mocked API is called with correct data. - store := mocks.NewMockBlockStore(t) - - col := unittest.CollectionFixture(1) - // Root <- A - blockA := makeBlockWithCollection(store.RootBlock, &col) - result := store.CreateBlockAndMockResult(t, blockA) - - ctx.mockIsBlockExecuted(store) - ctx.mockStateCommitmentByBlockID(store) - ctx.mockGetExecutionResultID(store) - ctx.mockNewStorageSnapshot(result) - - // receive block - err := ctx.engine.handleBlock(context.Background(), blockA.Block) - require.NoError(t, err) - - wg := sync.WaitGroup{} - wg.Add(1) // wait for block A to be executed - - ctx.mockComputeBlock(store) - ctx.mockSaveExecutionResults(store, &wg) - - // verify upload will be called - uploader := uploadermock.NewUploader(ctx.t) - uploader.On("Upload", result).Return(nil).Once() - ctx.uploadMgr.AddUploader(uploader) - - // verify broadcast will be called - ctx.providerEngine.On("BroadcastExecutionReceipt", - mock.Anything, - blockA.Block.Header.Height, - result.ExecutionReceipt).Return(true, nil).Once() - - err = ctx.engine.handleCollection(unittest.IdentifierFixture(), &col) - require.NoError(t, err) - - unittest.AssertReturnsBefore(t, wg.Wait, 10*time.Second) - - // verify collection is fetched - require.True(t, ctx.fetcher.IsFetched(col.ID())) - - // verify block is executed - store.AssertExecuted(t, "A", blockA.ID()) - }) -} - -// verify block will be executed if collection is received first -func TestExecuteBlocks(t *testing.T) { - - runWithEngine(t, func(ctx testingContext) { - store := mocks.NewMockBlockStore(t) - - col1 := unittest.CollectionFixture(1) - col2 := unittest.CollectionFixture(1) - // Root <- A[C1] <- B[C2] - // prepare two blocks, so that receiving C2 before C1 won't trigger any block to be executed, - // which creates the case where C2 collection is received first, and block B will become - // executable as soon as its parent block A is executed. - blockA := makeBlockWithCollection(store.RootBlock, &col1) - blockB := makeBlockWithCollection(blockA.Block.Header, &col2) - resultA := store.CreateBlockAndMockResult(t, blockA) - resultB := store.CreateBlockAndMockResult(t, blockB) - - ctx.mockIsBlockExecuted(store) - ctx.mockStateCommitmentByBlockID(store) - ctx.mockGetExecutionResultID(store) - ctx.mockNewStorageSnapshot(resultA) - ctx.mockNewStorageSnapshot(resultB) - ctx.providerEngine.On("BroadcastExecutionReceipt", mock.Anything, mock.Anything, mock.Anything).Return(false, nil) - - // receive block - err := ctx.engine.handleBlock(context.Background(), blockA.Block) - require.NoError(t, err) - - err = ctx.engine.handleBlock(context.Background(), blockB.Block) - require.NoError(t, err) - - ctx.mockComputeBlock(store) - wg := sync.WaitGroup{} - wg.Add(2) // wait for 2 blocks to be executed - ctx.mockSaveExecutionResults(store, &wg) - - require.NoError(t, ctx.engine.handleCollection(unittest.IdentifierFixture(), &col2)) - require.NoError(t, ctx.engine.handleCollection(unittest.IdentifierFixture(), &col1)) - - unittest.AssertReturnsBefore(t, wg.Wait, 10*time.Second) - - // verify collection is fetched - require.True(t, ctx.fetcher.IsFetched(col1.ID())) - require.True(t, ctx.fetcher.IsFetched(col2.ID())) - - // verify block is executed - store.AssertExecuted(t, "A", blockA.ID()) - store.AssertExecuted(t, "B", blockB.ID()) - }) -} - -// verify block will be executed if collection is already in storage -func TestExecuteNextBlockIfCollectionIsReady(t *testing.T) { - runWithEngine(t, func(ctx testingContext) { - store := mocks.NewMockBlockStore(t) - - col1 := unittest.CollectionFixture(1) - col2 := unittest.CollectionFixture(1) - - // Root <- A[C1] <- B[C2] - blockA := makeBlockWithCollection(store.RootBlock, &col1) - blockB := makeBlockWithCollection(blockA.Block.Header, &col2) - resultA := store.CreateBlockAndMockResult(t, blockA) - resultB := store.CreateBlockAndMockResult(t, blockB) - - // C2 is available in storage - require.NoError(t, ctx.collections.Store(&col2)) - - ctx.mockIsBlockExecuted(store) - ctx.mockStateCommitmentByBlockID(store) - ctx.mockGetExecutionResultID(store) - ctx.mockNewStorageSnapshot(resultA) - ctx.mockNewStorageSnapshot(resultB) - - // receiving block A and B will not trigger any execution - // because A is missing collection C1, B is waiting for A to be executed - err := ctx.engine.handleBlock(context.Background(), blockA.Block) - require.NoError(t, err) - - err = ctx.engine.handleBlock(context.Background(), blockB.Block) - require.NoError(t, err) - - ctx.providerEngine.On("BroadcastExecutionReceipt", mock.Anything, mock.Anything, mock.Anything).Return(false, nil) - ctx.mockComputeBlock(store) - wg := sync.WaitGroup{} - wg.Add(2) // waiting for A and B to be executed - ctx.mockSaveExecutionResults(store, &wg) - - // receiving collection C1 will execute both A and B - err = ctx.engine.handleCollection(unittest.IdentifierFixture(), &col1) - require.NoError(t, err) - - unittest.AssertReturnsBefore(t, wg.Wait, 10*time.Second) - - // verify collection is fetched - require.True(t, ctx.fetcher.IsFetched(col1.ID())) - require.False(t, ctx.fetcher.IsFetched(col2.ID())) - - // verify block is executed - store.AssertExecuted(t, "A", blockA.ID()) - store.AssertExecuted(t, "B", blockB.ID()) - }) -} - -// verify block will only be executed once even if block or collection are received multiple times -func TestExecuteBlockOnlyOnce(t *testing.T) { - runWithEngine(t, func(ctx testingContext) { - store := mocks.NewMockBlockStore(t) - - col := unittest.CollectionFixture(1) - // Root <- A[C] - blockA := makeBlockWithCollection(store.RootBlock, &col) - resultA := store.CreateBlockAndMockResult(t, blockA) - - ctx.mockIsBlockExecuted(store) - ctx.mockStateCommitmentByBlockID(store) - ctx.mockGetExecutionResultID(store) - ctx.mockNewStorageSnapshot(resultA) - - // receive block - err := ctx.engine.handleBlock(context.Background(), blockA.Block) - require.NoError(t, err) - - // receive block again before collection is received - err = ctx.engine.handleBlock(context.Background(), blockA.Block) - require.NoError(t, err) - - ctx.mockComputeBlock(store) - wg := sync.WaitGroup{} - wg.Add(1) // wait for block A to be executed - ctx.mockSaveExecutionResults(store, &wg) - ctx.providerEngine.On("BroadcastExecutionReceipt", mock.Anything, mock.Anything, mock.Anything).Return(false, nil) - - err = ctx.engine.handleCollection(unittest.IdentifierFixture(), &col) - require.NoError(t, err) - - // receiving collection again before block is executed - err = ctx.engine.handleCollection(unittest.IdentifierFixture(), &col) - require.NoError(t, err) - - unittest.AssertReturnsBefore(t, wg.Wait, 10*time.Second) - - // receiving collection again after block is executed - err = ctx.engine.handleCollection(unittest.IdentifierFixture(), &col) - require.NoError(t, err) - - // verify collection is fetched - require.True(t, ctx.fetcher.IsFetched(col.ID())) - - // verify block is executed - store.AssertExecuted(t, "A", blockA.ID()) - }) -} - -// given two blocks depend on the same root block and contain same collections, -// receiving all collections will trigger the execution of both blocks concurrently -func TestExecuteForkConcurrently(t *testing.T) { - runWithEngine(t, func(ctx testingContext) { - store := mocks.NewMockBlockStore(t) - - // create A and B that have the same collections and same parent - // Root <- A[C1, C2] - // <- B[C1, C2] - col1 := unittest.CollectionFixture(1) - col2 := unittest.CollectionFixture(1) - - blockA := makeBlockWithCollection(store.RootBlock, &col1, &col2) - blockB := makeBlockWithCollection(store.RootBlock, &col1, &col2) - resultA := store.CreateBlockAndMockResult(t, blockA) - resultB := store.CreateBlockAndMockResult(t, blockB) - - ctx.mockIsBlockExecuted(store) - ctx.mockStateCommitmentByBlockID(store) - ctx.mockGetExecutionResultID(store) - ctx.mockNewStorageSnapshot(resultA) - ctx.mockNewStorageSnapshot(resultB) - - // receive blocks - err := ctx.engine.handleBlock(context.Background(), blockA.Block) - require.NoError(t, err) - - err = ctx.engine.handleBlock(context.Background(), blockB.Block) - require.NoError(t, err) - - err = ctx.engine.handleCollection(unittest.IdentifierFixture(), &col1) - require.NoError(t, err) - - ctx.providerEngine.On("BroadcastExecutionReceipt", mock.Anything, mock.Anything, mock.Anything).Return(false, nil) - ctx.mockComputeBlock(store) - wg := sync.WaitGroup{} - wg.Add(2) // wait for A and B to be executed - ctx.mockSaveExecutionResults(store, &wg) - - err = ctx.engine.handleCollection(unittest.IdentifierFixture(), &col2) - require.NoError(t, err) - - unittest.AssertReturnsBefore(t, wg.Wait, 10*time.Second) - - // verify block is executed - store.AssertExecuted(t, "A", blockA.ID()) - store.AssertExecuted(t, "B", blockB.ID()) - }) -} - -// verify block will be executed in order -func TestExecuteBlockInOrder(t *testing.T) { - runWithEngine(t, func(ctx testingContext) { - store := mocks.NewMockBlockStore(t) - // create A and B that have the same collections and same parent - // Root <- A[C1, C2] - // <- B[C2] <- C[C3] - // verify receiving C3, C1, then C2 will trigger all blocks to be executed - col1 := unittest.CollectionFixture(1) - col2 := unittest.CollectionFixture(1) - col3 := unittest.CollectionFixture(1) - - blockA := makeBlockWithCollection(store.RootBlock, &col1, &col2) - blockB := makeBlockWithCollection(store.RootBlock, &col2) - blockC := makeBlockWithCollection(store.RootBlock, &col3) - resultA := store.CreateBlockAndMockResult(t, blockA) - resultB := store.CreateBlockAndMockResult(t, blockB) - resultC := store.CreateBlockAndMockResult(t, blockC) - - ctx.mockIsBlockExecuted(store) - ctx.mockStateCommitmentByBlockID(store) - ctx.mockGetExecutionResultID(store) - ctx.mockNewStorageSnapshot(resultA) - ctx.mockNewStorageSnapshot(resultB) - ctx.mockNewStorageSnapshot(resultC) - - // receive blocks - err := ctx.engine.handleBlock(context.Background(), blockA.Block) - require.NoError(t, err) - - err = ctx.engine.handleBlock(context.Background(), blockB.Block) - require.NoError(t, err) - - err = ctx.engine.handleBlock(context.Background(), blockC.Block) - require.NoError(t, err) - - err = ctx.engine.handleCollection(unittest.IdentifierFixture(), &col3) - require.NoError(t, err) - - err = ctx.engine.handleCollection(unittest.IdentifierFixture(), &col1) - require.NoError(t, err) - - ctx.providerEngine.On("BroadcastExecutionReceipt", mock.Anything, mock.Anything, mock.Anything).Return(false, nil) - ctx.mockComputeBlock(store) - wg := sync.WaitGroup{} - wg.Add(3) // waiting for A, B, C to be executed - ctx.mockSaveExecutionResults(store, &wg) - - err = ctx.engine.handleCollection(unittest.IdentifierFixture(), &col2) - require.NoError(t, err) - - unittest.AssertReturnsBefore(t, wg.Wait, 10*time.Second) - - // verify block is executed - store.AssertExecuted(t, "A", blockA.ID()) - store.AssertExecuted(t, "B", blockB.ID()) - store.AssertExecuted(t, "C", blockC.ID()) - }) -} - -func logBlocks(blocks map[string]*entity.ExecutableBlock) { - log := unittest.Logger() - for name, b := range blocks { - log.Debug().Msgf("creating blocks for testing, block %v's ID:%v", name, b.ID()) - } -} - -// verify that when blocks above the stop height are finalized, they won't -// be executed -func TestStopAtHeightWhenFinalizedBeforeExecuted(t *testing.T) { - runWithEngine(t, func(ctx testingContext) { - store := mocks.NewMockBlockStore(t) - - // this collection is used as trigger of execution - executionTrigger := unittest.CollectionFixture(1) - blockA := makeBlockWithCollection(store.RootBlock, &executionTrigger) - blockB := makeBlockWithCollection(blockA.Block.Header) - blockC := makeBlockWithCollection(blockB.Block.Header) - blockD := makeBlockWithCollection(blockC.Block.Header) - - resultA := store.CreateBlockAndMockResult(t, blockA) - resultB := store.CreateBlockAndMockResult(t, blockB) - store.CreateBlockAndMockResult(t, blockC) - store.CreateBlockAndMockResult(t, blockD) - - stopHeight := store.RootBlock.Height + 3 - require.Equal(t, stopHeight, blockC.Block.Header.Height) // stop at C (C will not be executed) - err := ctx.stopControl.SetStopParameters(stop.StopParameters{ - StopBeforeHeight: stopHeight, - }) - require.NoError(t, err) - - ctx.mockIsBlockExecuted(store) - ctx.mockStateCommitmentByBlockID(store) - ctx.mockGetExecutionResultID(store) - ctx.mockNewStorageSnapshot(resultA) - ctx.mockNewStorageSnapshot(resultB) - - // receive blocks - err = ctx.engine.handleBlock(context.Background(), blockA.Block) - require.NoError(t, err) - - err = ctx.engine.handleBlock(context.Background(), blockB.Block) - require.NoError(t, err) - - err = ctx.engine.handleBlock(context.Background(), blockC.Block) - require.NoError(t, err) - - err = ctx.engine.handleBlock(context.Background(), blockD.Block) - require.NoError(t, err) - - ctx.providerEngine.On("BroadcastExecutionReceipt", mock.Anything, mock.Anything, mock.Anything).Return(false, nil) - ctx.mockComputeBlock(store) - wg := sync.WaitGroup{} - wg.Add(2) // only 2 blocks (A, B) will be executed - ctx.mockSaveExecutionResults(store, &wg) - - // all blocks finalized - ctx.stopControl.BlockFinalizedForTesting(blockA.Block.Header) - ctx.stopControl.BlockFinalizedForTesting(blockB.Block.Header) - ctx.stopControl.BlockFinalizedForTesting(blockC.Block.Header) - ctx.stopControl.BlockFinalizedForTesting(blockD.Block.Header) - - // receiving the colleciton to trigger all blocks to be executed - err = ctx.engine.handleCollection(unittest.IdentifierFixture(), &executionTrigger) - require.NoError(t, err) - - unittest.AssertReturnsBefore(t, wg.Wait, 10*time.Second) - - // since stop height is C, verify that only A and B are executed, C and D are not executed - store.AssertExecuted(t, "A", blockA.ID()) - store.AssertExecuted(t, "B", blockB.ID()) - - store.AssertNotExecuted(t, "C", blockC.ID()) - store.AssertNotExecuted(t, "D", blockD.ID()) - }) -} - -// verify that blocks above the stop height won't be executed, even if they are -// later they got finalized -func TestStopAtHeightWhenExecutedBeforeFinalized(t *testing.T) { - runWithEngine(t, func(ctx testingContext) { - store := mocks.NewMockBlockStore(t) - - blockA := makeBlockWithCollection(store.RootBlock) - blockB := makeBlockWithCollection(blockA.Block.Header) - blockC := makeBlockWithCollection(blockB.Block.Header) - blockD := makeBlockWithCollection(blockC.Block.Header) - - resultA := store.CreateBlockAndMockResult(t, blockA) - resultB := store.CreateBlockAndMockResult(t, blockB) - store.CreateBlockAndMockResult(t, blockC) - store.CreateBlockAndMockResult(t, blockD) - - stopHeight := store.RootBlock.Height + 3 - require.Equal(t, stopHeight, blockC.Block.Header.Height) // stop at C (C will not be executed) - err := ctx.stopControl.SetStopParameters(stop.StopParameters{ - StopBeforeHeight: stopHeight, - }) - require.NoError(t, err) - - ctx.mockIsBlockExecuted(store) - ctx.mockStateCommitmentByBlockID(store) - ctx.mockGetExecutionResultID(store) - ctx.mockNewStorageSnapshot(resultA) - ctx.mockNewStorageSnapshot(resultB) - - ctx.providerEngine.On("BroadcastExecutionReceipt", mock.Anything, mock.Anything, mock.Anything).Return(false, nil) - ctx.mockComputeBlock(store) - wg := sync.WaitGroup{} - wg.Add(2) // waiting for only A, B to be executed - ctx.mockSaveExecutionResults(store, &wg) - - // receive blocks - err = ctx.engine.handleBlock(context.Background(), blockA.Block) - require.NoError(t, err) - - err = ctx.engine.handleBlock(context.Background(), blockB.Block) - require.NoError(t, err) - - err = ctx.engine.handleBlock(context.Background(), blockC.Block) - require.NoError(t, err) - - err = ctx.engine.handleBlock(context.Background(), blockD.Block) - require.NoError(t, err) - - // all blocks finalized - ctx.stopControl.BlockFinalizedForTesting(blockA.Block.Header) - ctx.stopControl.BlockFinalizedForTesting(blockB.Block.Header) - ctx.stopControl.BlockFinalizedForTesting(blockC.Block.Header) - ctx.stopControl.BlockFinalizedForTesting(blockD.Block.Header) - - unittest.AssertReturnsBefore(t, wg.Wait, 10*time.Second) - - // since stop height is C, verify that only A and B are executed, C and D are not executed - store.AssertExecuted(t, "A", blockA.ID()) - store.AssertExecuted(t, "B", blockB.ID()) - - store.AssertNotExecuted(t, "C", blockC.ID()) - store.AssertNotExecuted(t, "D", blockD.ID()) - }) -} - -// verify that when blocks execution and finalization happen concurrently -func TestStopAtHeightWhenExecutionFinalization(t *testing.T) { - runWithEngine(t, func(ctx testingContext) { - store := mocks.NewMockBlockStore(t) - - // Root <- A <- B (stop height, won't execute) <- C - // verify when executing A and finalizing B happens concurrently, - // still won't allow B and C to be executed - blockA := makeBlockWithCollection(store.RootBlock) - blockB := makeBlockWithCollection(blockA.Block.Header) - blockC := makeBlockWithCollection(blockB.Block.Header) - - resultA := store.CreateBlockAndMockResult(t, blockA) - store.CreateBlockAndMockResult(t, blockB) - store.CreateBlockAndMockResult(t, blockC) - - err := ctx.stopControl.SetStopParameters(stop.StopParameters{ - StopBeforeHeight: blockB.Block.Header.Height, - }) - require.NoError(t, err) - - ctx.mockIsBlockExecuted(store) - ctx.mockStateCommitmentByBlockID(store) - ctx.mockGetExecutionResultID(store) - ctx.mockNewStorageSnapshot(resultA) - - ctx.providerEngine.On("BroadcastExecutionReceipt", mock.Anything, mock.Anything, mock.Anything).Return(false, nil) - ctx.mockComputeBlock(store) - wg := sync.WaitGroup{} - // waiting for: - // 1. A, B, C to be handled - // 2. A, B, C to be finalized - // 3. only A to be executed - wg.Add(3) - ctx.mockSaveExecutionResults(store, &wg) - - // receive blocks - go func(wg *sync.WaitGroup) { - err = ctx.engine.handleBlock(context.Background(), blockA.Block) - require.NoError(t, err) - - err = ctx.engine.handleBlock(context.Background(), blockB.Block) - require.NoError(t, err) - - err = ctx.engine.handleBlock(context.Background(), blockC.Block) - require.NoError(t, err) - wg.Done() - }(&wg) - - go func(wg *sync.WaitGroup) { - // all blocks finalized - ctx.stopControl.BlockFinalizedForTesting(blockA.Block.Header) - ctx.stopControl.BlockFinalizedForTesting(blockB.Block.Header) - ctx.stopControl.BlockFinalizedForTesting(blockC.Block.Header) - wg.Done() - }(&wg) - - unittest.AssertReturnsBefore(t, wg.Wait, 10*time.Second) - - // since stop height is C, verify that only A and B are executed, C and D are not executed - store.AssertExecuted(t, "A", blockA.ID()) - store.AssertNotExecuted(t, "B", blockB.ID()) - store.AssertNotExecuted(t, "C", blockC.ID()) - }) -} - -// TestExecutedBlockUploadedFailureDoesntBlock tests that block processing continues even the -// uploader fails with an error -func TestExecutedBlockUploadedFailureDoesntBlock(t *testing.T) { - runWithEngine(t, func(ctx testingContext) { - store := mocks.NewMockBlockStore(t) - - col := unittest.CollectionFixture(1) - // Root <- A - blockA := makeBlockWithCollection(store.RootBlock, &col) - result := store.CreateBlockAndMockResult(t, blockA) - - ctx.mockIsBlockExecuted(store) - ctx.mockStateCommitmentByBlockID(store) - ctx.mockGetExecutionResultID(store) - ctx.mockNewStorageSnapshot(result) - - // receive block - err := ctx.engine.handleBlock(context.Background(), blockA.Block) - require.NoError(t, err) - - ctx.mockComputeBlock(store) - wg := sync.WaitGroup{} - wg.Add(1) // wait for block A to be executed - ctx.mockSaveExecutionResults(store, &wg) - - // verify upload will fail - uploader1 := uploadermock.NewUploader(ctx.t) - uploader1.On("Upload", result).Return(fmt.Errorf("error uploading")).Once() - ctx.uploadMgr.AddUploader(uploader1) - - // verify broadcast will be called - ctx.providerEngine.On("BroadcastExecutionReceipt", mock.Anything, mock.Anything, mock.Anything).Return(false, nil) - - err = ctx.engine.handleCollection(unittest.IdentifierFixture(), &col) - require.NoError(t, err) - - unittest.AssertReturnsBefore(t, wg.Wait, 10*time.Second) - - // verify collection is fetched - require.True(t, ctx.fetcher.IsFetched(col.ID())) - - // verify block is executed - store.AssertExecuted(t, "A", blockA.ID()) - }) -} - -func makeCollection() (*flow.Collection, *flow.CollectionGuarantee) { - col := unittest.CollectionFixture(1) - gua := col.Guarantee() - return &col, &gua -} - -func makeBlockWithCollection(parent *flow.Header, cols ...*flow.Collection) *entity.ExecutableBlock { - block := unittest.BlockWithParentFixture(parent) - completeCollections := make(map[flow.Identifier]*entity.CompleteCollection, len(block.Payload.Guarantees)) - for _, col := range cols { - g := col.Guarantee() - block.Payload.Guarantees = append(block.Payload.Guarantees, &g) - - cc := &entity.CompleteCollection{ - Guarantee: &g, - Transactions: col.Transactions, - } - completeCollections[col.ID()] = cc - } - block.Header.PayloadHash = block.Payload.Hash() - - executableBlock := &entity.ExecutableBlock{ - Block: block, - CompleteCollections: completeCollections, - StartState: unittest.StateCommitmentPointerFixture(), - } - return executableBlock -} - -func (ctx *testingContext) mockIsBlockExecuted(store *mocks.MockBlockStore) { - ctx.executionState.On("IsBlockExecuted", mock.Anything, mock.Anything). - Return(func(height uint64, blockID flow.Identifier) (bool, error) { - _, err := store.GetExecuted(blockID) - if err != nil { - return false, nil - } - return true, nil - }) -} - -func (ctx *testingContext) mockStateCommitmentByBlockID(store *mocks.MockBlockStore) { - ctx.executionState.On("StateCommitmentByBlockID", mock.Anything). - Return(func(blockID flow.Identifier) (flow.StateCommitment, error) { - result, err := store.GetExecuted(blockID) - if err != nil { - return flow.StateCommitment{}, storageerr.ErrNotFound - } - return result.Result.CurrentEndState(), nil - }) -} - -func (ctx *testingContext) mockGetExecutionResultID(store *mocks.MockBlockStore) { - ctx.executionState.On("GetExecutionResultID", mock.Anything, mock.Anything). - Return(func(ctx context.Context, blockID flow.Identifier) (flow.Identifier, error) { - blockResult, err := store.GetExecuted(blockID) - if err != nil { - return flow.ZeroID, storageerr.ErrNotFound - } - - return blockResult.Result.ExecutionReceipt.ExecutionResult.ID(), nil - }) -} - -func (ctx *testingContext) mockNewStorageSnapshot(result *execution.ComputationResult) { - // the result is the mocked result for the block, in other words, if the ingestion executes this block, - // the mocked computationManager will produce this result. - // so when mocking the StorageSnapshot method, it must be called with the StartState, as well as its - // parent block, which is used for retrieving the storage state at the end of the parent block. - ctx.executionState.On("NewStorageSnapshot", - *result.ExecutableBlock.StartState, - result.ExecutableBlock.Block.Header.ParentID, - result.ExecutableBlock.Block.Header.Height-1).Return(nil) -} - -func (ctx *testingContext) mockComputeBlock(store *mocks.MockBlockStore) { - ctx.computationManager.On("ComputeBlock", mock.Anything, mock.Anything, mock.Anything, mock.Anything). - Return(func(ctx context.Context, - parentBlockExecutionResultID flow.Identifier, - block *entity.ExecutableBlock, - snapshot snapshot.StorageSnapshot) ( - *execution.ComputationResult, error) { - blockResult, ok := store.ResultByBlock[block.ID()] - if !ok { - return nil, fmt.Errorf("block %s not found", block.ID()) - } - return blockResult.Result, nil - }) -} - -func (ctx *testingContext) mockSaveExecutionResults(store *mocks.MockBlockStore, wg *sync.WaitGroup) { - ctx.executionState.On("SaveExecutionResults", mock.Anything, mock.Anything). - Return(func(ctx context.Context, result *execution.ComputationResult) error { - defer wg.Done() - err := store.MarkExecuted(result) - if err != nil { - return err - } - return nil - }) -} diff --git a/engine/execution/ingestion/machine.go b/engine/execution/ingestion/machine.go index a4d0a27ee5d..efb9f521b83 100644 --- a/engine/execution/ingestion/machine.go +++ b/engine/execution/ingestion/machine.go @@ -54,7 +54,7 @@ func NewMachine( broadcaster provider.ProviderEngine, uploader *uploader.Manager, stopControl *stop.StopControl, -) (*Machine, module.ReadyDoneAware, error) { +) (*Machine, *Core, error) { e := &Machine{ log: logger.With().Str("engine", "ingestion_machine").Logger(), diff --git a/engine/execution/ingestion/mempool.go b/engine/execution/ingestion/mempool.go deleted file mode 100644 index 58d2b11f923..00000000000 --- a/engine/execution/ingestion/mempool.go +++ /dev/null @@ -1,29 +0,0 @@ -package ingestion - -//revive:disable:unexported-return - -import ( - "github.com/onflow/flow-go/module/mempool/stdmap" -) - -type Mempool struct { - ExecutionQueue *stdmap.Queues - BlockByCollection *stdmap.BlockByCollections -} - -func (m *Mempool) Run(f func(blockByCollection *stdmap.BlockByCollectionBackdata, executionQueue *stdmap.QueuesBackdata) error) error { - return m.ExecutionQueue.Run(func(queueBackdata *stdmap.QueuesBackdata) error { - return m.BlockByCollection.Run(func(blockByCollectionBackdata *stdmap.BlockByCollectionBackdata) error { - return f(blockByCollectionBackdata, queueBackdata) - }) - }) -} - -func newMempool() *Mempool { - m := &Mempool{ - BlockByCollection: stdmap.NewBlockByCollections(), - ExecutionQueue: stdmap.NewQueues(), - } - - return m -} diff --git a/engine/execution/rpc/engine.go b/engine/execution/rpc/engine.go index 260495f1bf1..9d7a9f87fc4 100644 --- a/engine/execution/rpc/engine.go +++ b/engine/execution/rpc/engine.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "net" + "sort" "strings" "unicode/utf8" @@ -26,6 +27,7 @@ import ( "github.com/onflow/flow-go/engine/common/rpc" "github.com/onflow/flow-go/engine/common/rpc/convert" exeEng "github.com/onflow/flow-go/engine/execution" + "github.com/onflow/flow-go/engine/execution/computation/metrics" "github.com/onflow/flow-go/engine/execution/state" fvmerrors "github.com/onflow/flow-go/fvm/errors" "github.com/onflow/flow-go/model/flow" @@ -62,6 +64,7 @@ func New( exeResults storage.ExecutionResults, txResults storage.TransactionResults, commits storage.Commits, + transactionMetrics metrics.TransactionExecutionMetricsProvider, chainID flow.ChainID, signerIndicesDecoder hotstuff.BlockSignerDecoder, apiRatelimits map[string]int, // the api rate limit (max calls per second) for each of the gRPC API e.g. Ping->100, ExecuteScriptAtBlockID->300 @@ -105,6 +108,7 @@ func New( exeResults: exeResults, transactionResults: txResults, commits: commits, + transactionMetrics: transactionMetrics, log: log, maxBlockRange: DefaultMaxBlockRange, }, @@ -166,6 +170,7 @@ type handler struct { transactionResults storage.TransactionResults log zerolog.Logger commits storage.Commits + transactionMetrics metrics.TransactionExecutionMetricsProvider maxBlockRange int } @@ -802,6 +807,58 @@ func (h *handler) blockHeaderResponse(header *flow.Header) (*execution.BlockHead }, nil } +// GetTransactionExecutionMetricsAfter gets the execution metrics for a transaction after a given block. +func (h *handler) GetTransactionExecutionMetricsAfter( + _ context.Context, + req *execution.GetTransactionExecutionMetricsAfterRequest, +) (*execution.GetTransactionExecutionMetricsAfterResponse, error) { + height := req.GetBlockHeight() + + metrics, err := h.transactionMetrics.GetTransactionExecutionMetricsAfter(height) + if err != nil { + return nil, status.Errorf(codes.Internal, "failed to get metrics after block height %v: %v", height, err) + } + + response := &execution.GetTransactionExecutionMetricsAfterResponse{ + Results: make([]*execution.GetTransactionExecutionMetricsAfterResponse_Result, 0, len(metrics)), + } + + for blockHeight, blockMetrics := range metrics { + blockResponse := &execution.GetTransactionExecutionMetricsAfterResponse_Result{ + BlockHeight: blockHeight, + Transactions: make([]*execution.GetTransactionExecutionMetricsAfterResponse_Transaction, len(blockMetrics)), + } + + for i, transactionMetrics := range blockMetrics { + transactionMetricsResponse := &execution.GetTransactionExecutionMetricsAfterResponse_Transaction{ + TransactionId: transactionMetrics.TransactionID[:], + ExecutionTime: uint64(transactionMetrics.ExecutionTime.Nanoseconds()), + ExecutionEffortWeights: make([]*execution.GetTransactionExecutionMetricsAfterResponse_ExecutionEffortWeight, 0, len(transactionMetrics.ExecutionEffortWeights)), + } + + for kind, weight := range transactionMetrics.ExecutionEffortWeights { + transactionMetricsResponse.ExecutionEffortWeights = append( + transactionMetricsResponse.ExecutionEffortWeights, + &execution.GetTransactionExecutionMetricsAfterResponse_ExecutionEffortWeight{ + Kind: uint64(kind), + Weight: uint64(weight), + }, + ) + } + + blockResponse.Transactions[i] = transactionMetricsResponse + } + response.Results = append(response.Results, blockResponse) + } + + // sort the response by block height in descending order + sort.Slice(response.Results, func(i, j int) bool { + return response.Results[i].BlockHeight > response.Results[j].BlockHeight + }) + + return response, nil +} + // additional check that when there is no event in the block, double check if the execution // result has no events as well, otherwise return an error. // we check the execution result has no event by checking if each chunk's EventCollection is diff --git a/engine/execution/state/bootstrap/bootstrap_test.go b/engine/execution/state/bootstrap/bootstrap_test.go index 6fa5ef66470..5a9d394a5ac 100644 --- a/engine/execution/state/bootstrap/bootstrap_test.go +++ b/engine/execution/state/bootstrap/bootstrap_test.go @@ -53,7 +53,7 @@ func TestBootstrapLedger(t *testing.T) { } func TestBootstrapLedger_ZeroTokenSupply(t *testing.T) { - expectedStateCommitmentBytes, _ := hex.DecodeString("e8b4b48a5c4eb510e28ecc9623271d53ef9915c98d333939b448516fa25e5a8f") + expectedStateCommitmentBytes, _ := hex.DecodeString("6e70a1ff40e4312a547d588a4355a538610bc22844a1faa907b4ec333ff1eca9") expectedStateCommitment, err := flow.ToStateCommitment(expectedStateCommitmentBytes) require.NoError(t, err) diff --git a/engine/execution/testutil/fixtures.go b/engine/execution/testutil/fixtures.go index 51647bcf9c3..38ec08afbe4 100644 --- a/engine/execution/testutil/fixtures.go +++ b/engine/execution/testutil/fixtures.go @@ -10,7 +10,7 @@ import ( "github.com/onflow/cadence" "github.com/onflow/cadence/encoding/ccf" jsoncdc "github.com/onflow/cadence/encoding/json" - "github.com/onflow/cadence/runtime/stdlib" + "github.com/onflow/cadence/stdlib" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" diff --git a/engine/testutil/mock/nodes.go b/engine/testutil/mock/nodes.go index d12a409cf57..8a2ea4fed1a 100644 --- a/engine/testutil/mock/nodes.go +++ b/engine/testutil/mock/nodes.go @@ -22,7 +22,7 @@ import ( "github.com/onflow/flow-go/engine/consensus/matching" "github.com/onflow/flow-go/engine/consensus/sealing" "github.com/onflow/flow-go/engine/execution/computation" - "github.com/onflow/flow-go/engine/execution/ingestion" + executionIngest "github.com/onflow/flow-go/engine/execution/ingestion" executionprovider "github.com/onflow/flow-go/engine/execution/provider" "github.com/onflow/flow-go/engine/execution/state" "github.com/onflow/flow-go/engine/verification/assigner" @@ -192,7 +192,7 @@ func (cn ConsensusNode) Done() { type ExecutionNode struct { GenericNode FollowerState protocol.FollowerState - IngestionEngine *ingestion.Engine + IngestionEngine *executionIngest.Core ExecutionEngine *computation.Manager RequestEngine *requester.Engine ReceiptsEngine *executionprovider.Engine @@ -217,6 +217,7 @@ func (en ExecutionNode) Ready(ctx context.Context) { // new interface. irctx, _ := irrecoverable.WithSignaler(ctx) en.ReceiptsEngine.Start(irctx) + en.IngestionEngine.Start(irctx) en.FollowerCore.Start(irctx) en.FollowerEngine.Start(irctx) en.SyncEngine.Start(irctx) @@ -238,7 +239,6 @@ func (en ExecutionNode) Done(cancelFunc context.CancelFunc) { // to stop all (deprecated) ready-done-aware <-util.AllDone( - en.IngestionEngine, en.IngestionEngine, en.ReceiptsEngine, en.Ledger, diff --git a/engine/testutil/nodes.go b/engine/testutil/nodes.go index b6d1037b500..a602e628565 100644 --- a/engine/testutil/nodes.go +++ b/engine/testutil/nodes.go @@ -46,7 +46,6 @@ import ( "github.com/onflow/flow-go/engine/execution/computation/query" "github.com/onflow/flow-go/engine/execution/ingestion" exeFetcher "github.com/onflow/flow-go/engine/execution/ingestion/fetcher" - "github.com/onflow/flow-go/engine/execution/ingestion/loader" "github.com/onflow/flow-go/engine/execution/ingestion/stop" "github.com/onflow/flow-go/engine/execution/ingestion/uploader" executionprovider "github.com/onflow/flow-go/engine/execution/provider" @@ -616,7 +615,7 @@ func ExecutionNode(t *testing.T, hub *stub.Hub, identity bootstrap.NodeInfo, ide checkpointHeight := uint64(0) require.NoError(t, esbootstrap.ImportRegistersFromCheckpoint(node.Log, checkpointFile, checkpointHeight, matchTrie.RootHash(), pebbledb, 2)) - diskStore, err := storagepebble.NewRegisters(pebbledb) + diskStore, err := storagepebble.NewRegisters(pebbledb, storagepebble.PruningDisabled) require.NoError(t, err) reader := finalizedreader.NewFinalizedReader(headersStorage, checkpointHeight) @@ -728,31 +727,25 @@ func ExecutionNode(t *testing.T, hub *stub.Hub, identity bootstrap.NodeInfo, ide ) fetcher := exeFetcher.NewCollectionFetcher(node.Log, requestEngine, node.State, false) - loader := loader.NewUnexecutedLoader(node.Log, node.State, node.Headers, execState) rootHead, rootQC := getRoot(t, &node) - ingestionEngine, err := ingestion.New( - unit, + _, ingestionCore, err := ingestion.NewMachine( node.Log, - node.Net, + node.ProtocolEvents, + requestEngine, fetcher, node.Headers, node.Blocks, collectionsStorage, - computationEngine, - pusherEngine, execState, + node.State, node.Metrics, - node.Tracer, - false, - nil, + computationEngine, + pusherEngine, uploader, stopControl, - loader, ) require.NoError(t, err) - requestEngine.WithHandle(ingestionEngine.OnCollection) - - node.ProtocolEvents.AddConsumer(ingestionEngine) + node.ProtocolEvents.AddConsumer(stopControl) followerCore, finalizer := createFollowerCore(t, &node, followerState, followerDistributor, rootHead, rootQC) // mock out hotstuff validator @@ -815,7 +808,7 @@ func ExecutionNode(t *testing.T, hub *stub.Hub, identity bootstrap.NodeInfo, ide return testmock.ExecutionNode{ GenericNode: node, FollowerState: followerState, - IngestionEngine: ingestionEngine, + IngestionEngine: ingestionCore, FollowerCore: followerCore, FollowerEngine: followerEng, SyncEngine: syncEngine, diff --git a/follower/follower_builder.go b/follower/follower_builder.go index 2988ee2767e..6f854216603 100644 --- a/follower/follower_builder.go +++ b/follower/follower_builder.go @@ -1,16 +1,12 @@ package follower import ( - "context" "encoding/json" "errors" "fmt" "strings" - dht "github.com/libp2p/go-libp2p-kad-dht" - "github.com/libp2p/go-libp2p/core/host" "github.com/libp2p/go-libp2p/core/peer" - "github.com/libp2p/go-libp2p/core/routing" "github.com/onflow/crypto" "github.com/rs/zerolog" @@ -44,17 +40,12 @@ import ( cborcodec "github.com/onflow/flow-go/network/codec/cbor" "github.com/onflow/flow-go/network/converter" "github.com/onflow/flow-go/network/p2p" - p2pbuilder "github.com/onflow/flow-go/network/p2p/builder" - p2pbuilderconfig "github.com/onflow/flow-go/network/p2p/builder/config" "github.com/onflow/flow-go/network/p2p/cache" "github.com/onflow/flow-go/network/p2p/conduit" - p2pdht "github.com/onflow/flow-go/network/p2p/dht" "github.com/onflow/flow-go/network/p2p/keyutils" p2plogging "github.com/onflow/flow-go/network/p2p/logging" - "github.com/onflow/flow-go/network/p2p/subscription" "github.com/onflow/flow-go/network/p2p/translator" "github.com/onflow/flow-go/network/p2p/unicast/protocols" - "github.com/onflow/flow-go/network/p2p/utils" "github.com/onflow/flow-go/network/slashing" "github.com/onflow/flow-go/network/underlay" "github.com/onflow/flow-go/network/validator" @@ -84,19 +75,14 @@ import ( // For a node running as a standalone process, the config fields will be populated from the command line params, // while for a node running as a library, the config fields are expected to be initialized by the caller. type FollowerServiceConfig struct { - bootstrapNodeAddresses []string - bootstrapNodePublicKeys []string - bootstrapIdentities flow.IdentitySkeletonList // the identity list of bootstrap peers the node uses to discover other nodes - NetworkKey crypto.PrivateKey // the networking key passed in by the caller when being used as a library - baseOptions []cmd.Option + bootstrapIdentities flow.IdentitySkeletonList // the identity list of bootstrap peers the node uses to discover other nodes + NetworkKey crypto.PrivateKey // the networking key passed in by the caller when being used as a library + baseOptions []cmd.Option } // DefaultFollowerServiceConfig defines all the default values for the FollowerServiceConfig func DefaultFollowerServiceConfig() *FollowerServiceConfig { - return &FollowerServiceConfig{ - bootstrapNodeAddresses: []string{}, - bootstrapNodePublicKeys: []string{}, - } + return &FollowerServiceConfig{} } // FollowerServiceBuilder provides the common functionality needed to bootstrap a Flow staked and observer @@ -136,7 +122,7 @@ func (builder *FollowerServiceBuilder) deriveBootstrapPeerIdentities() error { return nil } - ids, err := BootstrapIdentities(builder.bootstrapNodeAddresses, builder.bootstrapNodePublicKeys) + ids, err := builder.DeriveBootstrapPeerIdentities() if err != nil { return fmt.Errorf("failed to derive bootstrap peer identities: %w", err) } @@ -475,7 +461,7 @@ func (builder *FollowerServiceBuilder) InitIDProviders() { if flowID, err := builder.IDTranslator.GetFlowID(pid); err != nil { // TODO: this is an instance of "log error and continue with best effort" anti-pattern - builder.Logger.Err(err).Str("peer", p2plogging.PeerId(pid)).Msg("failed to translate to Flow ID") + builder.Logger.Debug().Str("peer", p2plogging.PeerId(pid)).Msg("failed to translate to Flow ID") } else { result = append(result, flowID) } @@ -535,86 +521,15 @@ func (builder *FollowerServiceBuilder) validateParams() error { if len(builder.bootstrapIdentities) > 0 { return nil } - if len(builder.bootstrapNodeAddresses) == 0 { + if len(builder.BootstrapNodeAddresses) == 0 { return errors.New("no bootstrap node address provided") } - if len(builder.bootstrapNodeAddresses) != len(builder.bootstrapNodePublicKeys) { + if len(builder.BootstrapNodeAddresses) != len(builder.BootstrapNodePublicKeys) { return errors.New("number of bootstrap node addresses and public keys should match") } return nil } -// initPublicLibp2pNode creates a libp2p node for the follower service in public (unstaked) network. -// The LibP2P host is created with the following options: -// - DHT as client and seeded with the given bootstrap peers -// - The specified bind address as the listen address -// - The passed in private key as the libp2p key -// - No connection gater -// - No connection manager -// - No peer manager -// - Default libp2p pubsub options -// -// Args: -// - networkKey: the private key to use for the libp2p node -// -// Returns: -// - p2p.LibP2PNode: the libp2p node -// - error: if any error occurs. Any error returned from this function is irrecoverable. -func (builder *FollowerServiceBuilder) initPublicLibp2pNode(networkKey crypto.PrivateKey) (p2p.LibP2PNode, error) { - var pis []peer.AddrInfo - - for _, b := range builder.bootstrapIdentities { - pi, err := utils.PeerAddressInfo(*b) - if err != nil { - return nil, fmt.Errorf("could not extract peer address info from bootstrap identity %v: %w", b, err) - } - - pis = append(pis, pi) - } - - node, err := p2pbuilder.NewNodeBuilder( - builder.Logger, - &builder.FlowConfig.NetworkConfig.GossipSub, - &p2pbuilderconfig.MetricsConfig{ - HeroCacheFactory: builder.HeroCacheMetricsFactory(), - Metrics: builder.Metrics.Network, - }, - network.PublicNetwork, - builder.BaseConfig.BindAddr, - networkKey, - builder.SporkID, - builder.IdentityProvider, - &builder.FlowConfig.NetworkConfig.ResourceManager, - p2pbuilderconfig.PeerManagerDisableConfig(), // disable peer manager for follower - &p2p.DisallowListCacheConfig{ - MaxSize: builder.FlowConfig.NetworkConfig.DisallowListNotificationCacheSize, - Metrics: metrics.DisallowListCacheMetricsFactory(builder.HeroCacheMetricsFactory(), network.PublicNetwork), - }, - &p2pbuilderconfig.UnicastConfig{ - Unicast: builder.FlowConfig.NetworkConfig.Unicast, - }). - SetSubscriptionFilter( - subscription.NewRoleBasedFilter( - subscription.UnstakedRole, builder.IdentityProvider, - ), - ). - SetRoutingSystem(func(ctx context.Context, h host.Host) (routing.Routing, error) { - return p2pdht.NewDHT(ctx, h, protocols.FlowPublicDHTProtocolID(builder.SporkID), - builder.Logger, - builder.Metrics.Network, - p2pdht.AsClient(), - dht.BootstrapPeers(pis...), - ) - }).Build() - if err != nil { - return nil, fmt.Errorf("could not build public libp2p node: %w", err) - } - - builder.LibP2PNode = node - - return builder.LibP2PNode, nil -} - // initObserverLocal initializes the observer's ID, network key and network address // Currently, it reads a node-info.priv.json like any other node. // TODO: read the node ID from the special bootstrap files @@ -651,11 +566,12 @@ func (builder *FollowerServiceBuilder) enqueuePublicNetworkInit() { builder. Component("public libp2p node", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { var err error - publicLibp2pNode, err = builder.initPublicLibp2pNode(node.NetworkKey) + publicLibp2pNode, err = builder.BuildPublicLibp2pNode(builder.BaseConfig.BindAddr, builder.bootstrapIdentities) if err != nil { - return nil, fmt.Errorf("could not create public libp2p node: %w", err) + return nil, fmt.Errorf("could not build public libp2p node: %w", err) } + builder.LibP2PNode = publicLibp2pNode return publicLibp2pNode, nil }). Component("public network", func(node *cmd.NodeConfig) (module.ReadyDoneAware, error) { diff --git a/fvm/accounts_test.go b/fvm/accounts_test.go index d8f5e15fb60..3cbffead859 100644 --- a/fvm/accounts_test.go +++ b/fvm/accounts_test.go @@ -8,8 +8,8 @@ import ( "github.com/onflow/cadence" "github.com/onflow/cadence/encoding/ccf" jsoncdc "github.com/onflow/cadence/encoding/json" - "github.com/onflow/cadence/runtime/format" - "github.com/onflow/cadence/runtime/stdlib" + "github.com/onflow/cadence/format" + "github.com/onflow/cadence/stdlib" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/fvm/blueprints/contracts.go b/fvm/blueprints/contracts.go index 34554be5a7a..f8fc22b0425 100644 --- a/fvm/blueprints/contracts.go +++ b/fvm/blueprints/contracts.go @@ -4,8 +4,8 @@ import ( _ "embed" "github.com/onflow/cadence" + "github.com/onflow/cadence/common" jsoncdc "github.com/onflow/cadence/encoding/json" - "github.com/onflow/cadence/runtime/common" "github.com/onflow/flow-go/model/flow" ) diff --git a/fvm/blueprints/fees.go b/fvm/blueprints/fees.go index 891e7be7a01..e5c8328615e 100644 --- a/fvm/blueprints/fees.go +++ b/fvm/blueprints/fees.go @@ -6,8 +6,8 @@ import ( "fmt" "github.com/onflow/cadence" + "github.com/onflow/cadence/common" jsoncdc "github.com/onflow/cadence/encoding/json" - "github.com/onflow/cadence/runtime/common" "github.com/onflow/flow-core-contracts/lib/go/templates" "github.com/onflow/flow-go/model/flow" diff --git a/fvm/bootstrap.go b/fvm/bootstrap.go index 5c7e09d31ed..0f3526c8d83 100644 --- a/fvm/bootstrap.go +++ b/fvm/bootstrap.go @@ -351,6 +351,7 @@ func (b *bootstrapExecutor) Execute() error { b.deployViewResolver(service, &env) b.deployBurner(service, &env) + b.deployCrypto(service, &env) err = expectAccounts(1) if err != nil { @@ -523,6 +524,22 @@ func (b *bootstrapExecutor) deployBurner(deployTo flow.Address, env *templates.E panicOnMetaInvokeErrf("failed to deploy burner contract: %s", txError, err) } +func (b *bootstrapExecutor) deployCrypto(deployTo flow.Address, env *templates.Environment) { + contract := contracts.Crypto() + + txError, err := b.invokeMetaTransaction( + b.ctx, + Transaction( + blueprints.DeployContractTransaction( + deployTo, + contract, + "Crypto"), + 0), + ) + env.CryptoAddress = deployTo.String() + panicOnMetaInvokeErrf("failed to deploy crypto contract: %s", txError, err) +} + func (b *bootstrapExecutor) deployMetadataViews(fungibleToken, nonFungibleToken flow.Address, env *templates.Environment) { mvContract := contracts.MetadataViews(*env) diff --git a/fvm/context.go b/fvm/context.go index 4007f22286f..fd198633b54 100644 --- a/fvm/context.go +++ b/fvm/context.go @@ -392,3 +392,12 @@ func WithEVMTracer(tracer debug.EVMTracer) Option { return ctx } } + +// WithReadVersionFromNodeVersionBeacon sets whether the version from the node version beacon should be read +// this should only be disabled for testing +func WithReadVersionFromNodeVersionBeacon(enabled bool) Option { + return func(ctx Context) Context { + ctx.ReadVersionFromNodeVersionBeacon = enabled + return ctx + } +} diff --git a/fvm/crypto/hash_test.go b/fvm/crypto/hash_test.go index 29eeb7ec09c..6c8ba0354c8 100644 --- a/fvm/crypto/hash_test.go +++ b/fvm/crypto/hash_test.go @@ -2,10 +2,9 @@ package crypto_test import ( "crypto/rand" - "testing" - "crypto/sha256" "crypto/sha512" + "testing" "github.com/onflow/crypto/hash" "github.com/stretchr/testify/assert" diff --git a/fvm/environment/account_creator.go b/fvm/environment/account_creator.go index d31985a8e75..a961609be89 100644 --- a/fvm/environment/account_creator.go +++ b/fvm/environment/account_creator.go @@ -3,7 +3,7 @@ package environment import ( "fmt" - "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/common" "github.com/onflow/flow-go/fvm/errors" "github.com/onflow/flow-go/fvm/storage/state" diff --git a/fvm/environment/account_info.go b/fvm/environment/account_info.go index 1c83dc9608b..f068487bd1f 100644 --- a/fvm/environment/account_info.go +++ b/fvm/environment/account_info.go @@ -4,7 +4,7 @@ import ( "fmt" "github.com/onflow/cadence" - "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/common" "github.com/onflow/flow-go/fvm/storage/state" "github.com/onflow/flow-go/fvm/tracing" diff --git a/fvm/environment/account_key_reader.go b/fvm/environment/account_key_reader.go index c232d9c9734..612373a8aa3 100644 --- a/fvm/environment/account_key_reader.go +++ b/fvm/environment/account_key_reader.go @@ -3,8 +3,8 @@ package environment import ( "fmt" + "github.com/onflow/cadence/common" "github.com/onflow/cadence/runtime" - "github.com/onflow/cadence/runtime/common" "github.com/onflow/flow-go/fvm/crypto" "github.com/onflow/flow-go/fvm/errors" diff --git a/fvm/environment/account_key_reader_test.go b/fvm/environment/account_key_reader_test.go index 10fba41631b..4d6c6620d09 100644 --- a/fvm/environment/account_key_reader_test.go +++ b/fvm/environment/account_key_reader_test.go @@ -4,7 +4,7 @@ import ( "testing" "testing/quick" - "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/common" testMock "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" diff --git a/fvm/environment/account_key_updater.go b/fvm/environment/account_key_updater.go index d44dd3a27f9..802ac03fe00 100644 --- a/fvm/environment/account_key_updater.go +++ b/fvm/environment/account_key_updater.go @@ -4,9 +4,9 @@ import ( "encoding/hex" "fmt" + "github.com/onflow/cadence/common" "github.com/onflow/cadence/runtime" - "github.com/onflow/cadence/runtime/common" - "github.com/onflow/cadence/runtime/sema" + "github.com/onflow/cadence/sema" fgcrypto "github.com/onflow/crypto" fghash "github.com/onflow/crypto/hash" diff --git a/fvm/environment/account_key_updater_test.go b/fvm/environment/account_key_updater_test.go index 79e91952a82..484740f0484 100644 --- a/fvm/environment/account_key_updater_test.go +++ b/fvm/environment/account_key_updater_test.go @@ -8,7 +8,7 @@ import ( "github.com/fxamacker/cbor/v2" "github.com/onflow/atree" "github.com/onflow/cadence/runtime" - "github.com/onflow/cadence/runtime/sema" + "github.com/onflow/cadence/sema" "github.com/onflow/crypto" "github.com/onflow/crypto/hash" "github.com/stretchr/testify/assert" diff --git a/fvm/environment/account_local_id_generator.go b/fvm/environment/account_local_id_generator.go index 9a1ba6a35c4..938e80f1991 100644 --- a/fvm/environment/account_local_id_generator.go +++ b/fvm/environment/account_local_id_generator.go @@ -1,7 +1,7 @@ package environment import ( - "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/common" "github.com/onflow/flow-go/fvm/storage/state" "github.com/onflow/flow-go/fvm/tracing" diff --git a/fvm/environment/account_local_id_generator_test.go b/fvm/environment/account_local_id_generator_test.go index 0a2c229226e..38f19d7ed9f 100644 --- a/fvm/environment/account_local_id_generator_test.go +++ b/fvm/environment/account_local_id_generator_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/require" - "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/common" "github.com/onflow/flow-go/fvm/environment" envMock "github.com/onflow/flow-go/fvm/environment/mock" diff --git a/fvm/environment/blocks.go b/fvm/environment/blocks.go index 6430955a0fe..85ad7d8dbac 100644 --- a/fvm/environment/blocks.go +++ b/fvm/environment/blocks.go @@ -3,7 +3,7 @@ package environment import ( "fmt" - "github.com/onflow/cadence/runtime/stdlib" + "github.com/onflow/cadence/stdlib" "github.com/onflow/cadence/runtime" diff --git a/fvm/environment/contract_reader.go b/fvm/environment/contract_reader.go index 2f21c4a9f92..5c8aba5cc25 100644 --- a/fvm/environment/contract_reader.go +++ b/fvm/environment/contract_reader.go @@ -3,9 +3,10 @@ package environment import ( "fmt" + "github.com/onflow/cadence/ast" + "github.com/onflow/cadence/common" "github.com/onflow/cadence/runtime" - "github.com/onflow/cadence/runtime/ast" - "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/stdlib" "github.com/onflow/flow-go/fvm/errors" "github.com/onflow/flow-go/fvm/tracing" @@ -15,21 +16,23 @@ import ( // ContractReader provide read access to contracts. type ContractReader struct { - tracer tracing.TracerSpan - meter Meter - - accounts Accounts + tracer tracing.TracerSpan + meter Meter + accounts Accounts + cryptoContractAddress common.Address } func NewContractReader( tracer tracing.TracerSpan, meter Meter, accounts Accounts, + cryptoContractAddress common.Address, ) *ContractReader { return &ContractReader{ - tracer: tracer, - meter: meter, - accounts: accounts, + tracer: tracer, + meter: meter, + accounts: accounts, + cryptoContractAddress: cryptoContractAddress, } } @@ -69,12 +72,37 @@ func (reader *ContractReader) ResolveLocation( return nil, fmt.Errorf("resolve location failed: %w", err) } + return ResolveLocation( + identifiers, + location, + reader.accounts.GetContractNames, + reader.cryptoContractAddress, + ) +} + +func ResolveLocation( + identifiers []ast.Identifier, + location common.Location, + getContractNames func(flow.Address) ([]string, error), + cryptoContractAddress common.Address, +) ([]runtime.ResolvedLocation, error) { + addressLocation, isAddress := location.(common.AddressLocation) // if the location is not an address location, e.g. an identifier location - // (`import Crypto`), then return a single resolved location which declares - // all identifiers. + // then return a single resolved location which declares all identifiers. if !isAddress { + + // if the location is the Crypto contract, + // translate it to the address of the Crypto contract on the chain + + if location == stdlib.CryptoContractLocation { + location = common.AddressLocation{ + Address: cryptoContractAddress, + Name: string(stdlib.CryptoContractLocation), + } + } + return []runtime.ResolvedLocation{ { Location: location, @@ -87,9 +115,13 @@ func (reader *ContractReader) ResolveLocation( // and no specific identifiers where requested in the import statement, // then fetch all identifiers at this address if len(identifiers) == 0 { + if getContractNames == nil { + return nil, fmt.Errorf("no identifiers provided") + } + address := flow.ConvertAddress(addressLocation.Address) - contractNames, err := reader.accounts.GetContractNames(address) + contractNames, err := getContractNames(address) if err != nil { return nil, fmt.Errorf("resolving location failed: %w", err) } diff --git a/fvm/environment/contract_updater.go b/fvm/environment/contract_updater.go index 2e2714c8f71..7504c8a5143 100644 --- a/fvm/environment/contract_updater.go +++ b/fvm/environment/contract_updater.go @@ -6,7 +6,7 @@ import ( "sort" "github.com/onflow/cadence" - "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/common" "github.com/onflow/flow-go/fvm/blueprints" "github.com/onflow/flow-go/fvm/errors" diff --git a/fvm/environment/contract_updater_test.go b/fvm/environment/contract_updater_test.go index ca48b575c90..861b0bdb860 100644 --- a/fvm/environment/contract_updater_test.go +++ b/fvm/environment/contract_updater_test.go @@ -5,7 +5,7 @@ import ( "testing" "github.com/onflow/cadence" - "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/common" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" diff --git a/fvm/environment/derived_data_invalidator.go b/fvm/environment/derived_data_invalidator.go index 5aa4bf05808..754a1e37513 100644 --- a/fvm/environment/derived_data_invalidator.go +++ b/fvm/environment/derived_data_invalidator.go @@ -1,11 +1,10 @@ package environment import ( - "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/common" "github.com/onflow/flow-go/fvm/storage/derived" "github.com/onflow/flow-go/fvm/storage/snapshot" - "github.com/onflow/flow-go/model/flow" ) type ContractUpdate struct { @@ -26,51 +25,44 @@ func (u ContractUpdates) Any() bool { type DerivedDataInvalidator struct { ContractUpdates - MeterParamOverridesUpdated bool + ExecutionParametersUpdated bool } var _ derived.TransactionInvalidator = DerivedDataInvalidator{} func NewDerivedDataInvalidator( contractUpdates ContractUpdates, - serviceAddress flow.Address, executionSnapshot *snapshot.ExecutionSnapshot, + meterStateRead *snapshot.ExecutionSnapshot, ) DerivedDataInvalidator { return DerivedDataInvalidator{ ContractUpdates: contractUpdates, - MeterParamOverridesUpdated: meterParamOverridesUpdated( - serviceAddress, - executionSnapshot), + ExecutionParametersUpdated: executionParametersUpdated( + executionSnapshot, + meterStateRead), } } -func meterParamOverridesUpdated( - serviceAddress flow.Address, +// executionParametersUpdated returns true if the meter param overrides have been updated +// this is done by checking if the registers needed to compute the meter param overrides +// have been touched in the execution snapshot +func executionParametersUpdated( executionSnapshot *snapshot.ExecutionSnapshot, + meterStateRead *snapshot.ExecutionSnapshot, ) bool { - serviceAccount := string(serviceAddress.Bytes()) - storageDomain := common.PathDomainStorage.Identifier() - - for registerId := range executionSnapshot.WriteSet { - // The meter param override values are stored in the service account. - if registerId.Owner != serviceAccount { - continue + if len(executionSnapshot.WriteSet) > len(meterStateRead.ReadSet) { + for registerId := range meterStateRead.ReadSet { + _, ok := executionSnapshot.WriteSet[registerId] + if ok { + return true + } } - - // NOTE: This condition is empirically generated by running the - // MeterParamOverridesComputer to capture touched registers. - // - // The paramater settings are stored as regular fields in the service - // account. In general, each account's regular fields are stored in - // ordered map known only to cadence. Cadence encodes this map into - // bytes and split the bytes into slab chunks before storing the slabs - // into the ledger. Hence any changes to the stabs indicate changes - // the ordered map. - // - // The meter param overrides use storageDomain as input, so any - // changes to it must also invalidate the values. - if registerId.Key == storageDomain || registerId.IsSlabIndex() { - return true + } else { + for registerId := range executionSnapshot.WriteSet { + _, ok := meterStateRead.ReadSet[registerId] + if ok { + return true + } } } @@ -81,8 +73,8 @@ func (invalidator DerivedDataInvalidator) ProgramInvalidator() derived.ProgramIn return ProgramInvalidator{invalidator} } -func (invalidator DerivedDataInvalidator) MeterParamOverridesInvalidator() derived.MeterParamOverridesInvalidator { - return MeterParamOverridesInvalidator{invalidator} +func (invalidator DerivedDataInvalidator) ExecutionParametersInvalidator() derived.ExecutionParametersInvalidator { + return ExecutionParametersInvalidator{invalidator} } type ProgramInvalidator struct { @@ -90,16 +82,16 @@ type ProgramInvalidator struct { } func (invalidator ProgramInvalidator) ShouldInvalidateEntries() bool { - return invalidator.MeterParamOverridesUpdated || + return invalidator.ExecutionParametersUpdated || invalidator.ContractUpdates.Any() } func (invalidator ProgramInvalidator) ShouldInvalidateEntry( - location common.AddressLocation, + _ common.AddressLocation, program *derived.Program, - snapshot *snapshot.ExecutionSnapshot, + _ *snapshot.ExecutionSnapshot, ) bool { - if invalidator.MeterParamOverridesUpdated { + if invalidator.ExecutionParametersUpdated { // if meter parameters changed we need to invalidate all programs return true } @@ -132,18 +124,18 @@ func (invalidator ProgramInvalidator) ShouldInvalidateEntry( return false } -type MeterParamOverridesInvalidator struct { +type ExecutionParametersInvalidator struct { DerivedDataInvalidator } -func (invalidator MeterParamOverridesInvalidator) ShouldInvalidateEntries() bool { - return invalidator.MeterParamOverridesUpdated +func (invalidator ExecutionParametersInvalidator) ShouldInvalidateEntries() bool { + return invalidator.ExecutionParametersUpdated } -func (invalidator MeterParamOverridesInvalidator) ShouldInvalidateEntry( +func (invalidator ExecutionParametersInvalidator) ShouldInvalidateEntry( _ struct{}, - _ derived.MeterParamOverrides, + _ derived.StateExecutionParameters, _ *snapshot.ExecutionSnapshot, ) bool { - return invalidator.MeterParamOverridesUpdated + return invalidator.ExecutionParametersUpdated } diff --git a/fvm/environment/derived_data_invalidator_test.go b/fvm/environment/derived_data_invalidator_test.go index 026f0dd119a..147c03fb57a 100644 --- a/fvm/environment/derived_data_invalidator_test.go +++ b/fvm/environment/derived_data_invalidator_test.go @@ -3,7 +3,7 @@ package environment_test import ( "testing" - "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/common" "github.com/stretchr/testify/require" "github.com/onflow/flow-go/fvm" @@ -82,7 +82,7 @@ func TestDerivedDataProgramInvalidator(t *testing.T) { }) t.Run("meter parameters invalidator invalidates all entries", func(t *testing.T) { invalidator := environment.DerivedDataInvalidator{ - MeterParamOverridesUpdated: true, + ExecutionParametersUpdated: true, }.ProgramInvalidator() require.True(t, invalidator.ShouldInvalidateEntries()) @@ -207,23 +207,23 @@ func TestDerivedDataProgramInvalidator(t *testing.T) { func TestMeterParamOverridesInvalidator(t *testing.T) { invalidator := environment.DerivedDataInvalidator{}. - MeterParamOverridesInvalidator() + ExecutionParametersInvalidator() require.False(t, invalidator.ShouldInvalidateEntries()) require.False(t, invalidator.ShouldInvalidateEntry( struct{}{}, - derived.MeterParamOverrides{}, + derived.StateExecutionParameters{}, nil)) invalidator = environment.DerivedDataInvalidator{ ContractUpdates: environment.ContractUpdates{}, - MeterParamOverridesUpdated: true, - }.MeterParamOverridesInvalidator() + ExecutionParametersUpdated: true, + }.ExecutionParametersInvalidator() require.True(t, invalidator.ShouldInvalidateEntries()) require.True(t, invalidator.ShouldInvalidateEntry( struct{}{}, - derived.MeterParamOverrides{}, + derived.StateExecutionParameters{}, nil)) } @@ -265,7 +265,11 @@ func TestMeterParamOverridesUpdated(t *testing.T) { txnState, err := blockDatabase.NewTransaction(0, state.DefaultParameters()) require.NoError(t, err) - computer := fvm.NewMeterParamOverridesComputer(ctx, txnState) + computer := fvm.NewExecutionParametersComputer( + ctx.Logger, + ctx, + txnState, + ) overrides, err := computer.Compute(txnState, struct{}{}) require.NoError(t, err) @@ -283,6 +287,12 @@ func TestMeterParamOverridesUpdated(t *testing.T) { ctx.TxBody = &flow.TransactionBody{} + meterStateRead := &snapshot.ExecutionSnapshot{ + ReadSet: map[flow.RegisterID]struct{}{ + flow.NewRegisterID(ctx.Chain.ServiceAddress(), "meter"): {}, + }, + } + checkForUpdates := func(id flow.RegisterID, expected bool) { snapshot := &snapshot.ExecutionSnapshot{ WriteSet: map[flow.RegisterID]flow.RegisterValue{ @@ -292,9 +302,9 @@ func TestMeterParamOverridesUpdated(t *testing.T) { invalidator := environment.NewDerivedDataInvalidator( environment.ContractUpdates{}, - ctx.Chain.ServiceAddress(), - snapshot) - require.Equal(t, expected, invalidator.MeterParamOverridesUpdated) + snapshot, + meterStateRead) + require.Equal(t, expected, invalidator.ExecutionParametersUpdated) } executionSnapshot, err = txnState.FinalizeMainTransaction() @@ -304,17 +314,14 @@ func TestMeterParamOverridesUpdated(t *testing.T) { otherOwner := unittest.RandomAddressFixtureForChain(ctx.Chain.ChainID()) for _, registerId := range executionSnapshot.AllRegisterIDs() { - checkForUpdates(registerId, true) + checkForUpdates(registerId, false) checkForUpdates( flow.NewRegisterID(otherOwner, registerId.Key), false) } - stabIndexKey := flow.NewRegisterID(owner, "$12345678") - require.True(t, stabIndexKey.IsSlabIndex()) - - checkForUpdates(stabIndexKey, true) - checkForUpdates(flow.NewRegisterID(owner, "other keys"), false) - checkForUpdates(flow.NewRegisterID(otherOwner, stabIndexKey.Key), false) - checkForUpdates(flow.NewRegisterID(otherOwner, "other key"), false) + checkForUpdates(flow.NewRegisterID(owner, "meter2"), false) + checkForUpdates(flow.NewRegisterID(owner, "meter"), true) + checkForUpdates(flow.NewRegisterID(otherOwner, "meter2"), false) + checkForUpdates(flow.NewRegisterID(otherOwner, "meter"), false) } diff --git a/fvm/environment/env.go b/fvm/environment/env.go index 5b01728111c..e23e9c64deb 100644 --- a/fvm/environment/env.go +++ b/fvm/environment/env.go @@ -62,6 +62,12 @@ type Environment interface { error, ) + // GetCurrentVersionBoundary executes the getCurrentVersionBoundary function on the NodeVersionBeacon contract. + // the function will return the version boundary (version, block height) that is currently in effect. + // the version boundary currently in effect is the highest one not above the current block height. + // if there is no existing version boundary lower than the current block height, the function will return version 0 and block height 0. + GetCurrentVersionBoundary() (cadence.Value, error) + // AccountInfo GetAccount(address flow.Address) (*flow.Account, error) GetAccountKeys(address flow.Address) ([]flow.AccountPublicKey, error) diff --git a/fvm/environment/event_emitter_test.go b/fvm/environment/event_emitter_test.go index 56b52c4dbb0..388263a2bbc 100644 --- a/fvm/environment/event_emitter_test.go +++ b/fvm/environment/event_emitter_test.go @@ -7,9 +7,9 @@ import ( "github.com/stretchr/testify/require" "github.com/onflow/cadence" + "github.com/onflow/cadence/common" "github.com/onflow/cadence/encoding/ccf" - "github.com/onflow/cadence/runtime/common" - "github.com/onflow/cadence/runtime/stdlib" + "github.com/onflow/cadence/stdlib" "github.com/onflow/flow-go/fvm/environment" "github.com/onflow/flow-go/fvm/meter" diff --git a/fvm/environment/facade_env.go b/fvm/environment/facade_env.go index a9e558c5106..57b01d1a853 100644 --- a/fvm/environment/facade_env.go +++ b/fvm/environment/facade_env.go @@ -3,13 +3,15 @@ package environment import ( "context" - "github.com/onflow/cadence/runtime/ast" - "github.com/onflow/cadence/runtime/common" - "github.com/onflow/cadence/runtime/interpreter" + "github.com/onflow/cadence/ast" + "github.com/onflow/cadence/common" + "github.com/onflow/cadence/interpreter" + "github.com/onflow/cadence/sema" "github.com/onflow/flow-go/fvm/storage" "github.com/onflow/flow-go/fvm/storage/snapshot" "github.com/onflow/flow-go/fvm/storage/state" + "github.com/onflow/flow-go/fvm/systemcontracts" "github.com/onflow/flow-go/fvm/tracing" ) @@ -36,6 +38,7 @@ type facadeEnvironment struct { ValueStore *SystemContracts + MinimumCadenceRequiredVersion UUIDGenerator AccountLocalIDGenerator @@ -62,12 +65,15 @@ func newFacadeEnvironment( accounts := NewAccounts(txnState) logger := NewProgramLogger(tracer, params.ProgramLoggerParams) runtime := NewRuntime(params.RuntimeParams) + chain := params.Chain systemContracts := NewSystemContracts( - params.Chain, + chain, tracer, logger, runtime) + sc := systemcontracts.SystemContractsForChain(chain.ChainID()) + env := &facadeEnvironment{ Runtime: runtime, @@ -102,6 +108,9 @@ func newFacadeEnvironment( ), SystemContracts: systemContracts, + MinimumCadenceRequiredVersion: NewMinimumCadenceRequiredVersion( + txnState, + ), UUIDGenerator: NewUUIDGenerator( tracer, @@ -129,6 +138,7 @@ func newFacadeEnvironment( tracer, meter, accounts, + common.Address(sc.Crypto.Address), ), ContractUpdater: NoContractUpdater{}, Programs: NewPrograms( @@ -343,3 +353,37 @@ func (env *facadeEnvironment) RecoverProgram(program *ast.Program, location comm location, ) } + +func (env *facadeEnvironment) ValidateAccountCapabilitiesGet( + _ *interpreter.Interpreter, + _ interpreter.LocationRange, + _ interpreter.AddressValue, + _ interpreter.PathValue, + wantedBorrowType *sema.ReferenceType, + _ *sema.ReferenceType, +) (bool, error) { + _, hasEntitlements := wantedBorrowType.Authorization.(sema.EntitlementSetAccess) + if hasEntitlements { + // TODO: maybe abort + //return false, interpreter.GetCapabilityError{ + // LocationRange: locationRange, + //} + return false, nil + } + return true, nil +} + +func (env *facadeEnvironment) ValidateAccountCapabilitiesPublish( + _ *interpreter.Interpreter, + _ interpreter.LocationRange, + _ interpreter.AddressValue, + _ interpreter.PathValue, + capabilityBorrowType *interpreter.ReferenceStaticType, +) (bool, error) { + _, isEntitledCapability := capabilityBorrowType.Authorization.(interpreter.EntitlementSetAuthorization) + if isEntitledCapability { + // TODO: maybe abort + return false, nil + } + return true, nil +} diff --git a/fvm/environment/invoker.go b/fvm/environment/invoker.go index 8041acdb363..27f9c881b6b 100644 --- a/fvm/environment/invoker.go +++ b/fvm/environment/invoker.go @@ -2,7 +2,7 @@ package environment import ( "github.com/onflow/cadence" - "github.com/onflow/cadence/runtime/sema" + "github.com/onflow/cadence/sema" "github.com/onflow/flow-go/model/flow" ) diff --git a/fvm/environment/meter.go b/fvm/environment/meter.go index ad7c28c7eb3..614cd124a52 100644 --- a/fvm/environment/meter.go +++ b/fvm/environment/meter.go @@ -3,7 +3,8 @@ package environment import ( "context" - "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/common" + "github.com/onflow/cadence/runtime" "github.com/onflow/flow-go/fvm/errors" "github.com/onflow/flow-go/fvm/meter" @@ -70,18 +71,13 @@ var MainnetExecutionEffortWeights = meter.ExecutionEffortWeights{ } type Meter interface { - MeterComputation(common.ComputationKind, uint) error - ComputationUsed() (uint64, error) + runtime.MeterInterface + ComputationIntensities() meter.MeteredComputationIntensities ComputationAvailable(common.ComputationKind, uint) bool - MeterMemory(usage common.MemoryUsage) error - MemoryUsed() (uint64, error) - MeterEmittedEvent(byteSize uint64) error TotalEmittedEventBytes() uint64 - - InteractionUsed() (uint64, error) } type meterImpl struct { @@ -112,6 +108,10 @@ func (meter *meterImpl) ComputationAvailable( return meter.txnState.ComputationAvailable(kind, intensity) } +func (meter *meterImpl) ComputationRemaining(kind common.ComputationKind) uint { + return meter.txnState.ComputationRemaining(kind) +} + func (meter *meterImpl) ComputationUsed() (uint64, error) { return meter.txnState.TotalComputationUsed(), nil } diff --git a/fvm/environment/minimum_required_version.go b/fvm/environment/minimum_required_version.go new file mode 100644 index 00000000000..3095c33cda9 --- /dev/null +++ b/fvm/environment/minimum_required_version.go @@ -0,0 +1,99 @@ +package environment + +import ( + "github.com/coreos/go-semver/semver" + + "github.com/onflow/flow-go/fvm/storage/state" +) + +// MinimumCadenceRequiredVersion returns the minimum required cadence version for the current environment +// in semver format. +type MinimumCadenceRequiredVersion interface { + MinimumRequiredVersion() (string, error) +} + +type minimumCadenceRequiredVersion struct { + txnPreparer state.NestedTransactionPreparer +} + +func NewMinimumCadenceRequiredVersion( + txnPreparer state.NestedTransactionPreparer, +) MinimumCadenceRequiredVersion { + return minimumCadenceRequiredVersion{ + txnPreparer: txnPreparer, + } +} + +// MinimumRequiredVersion The returned cadence version can be used by cadence runtime for supporting feature flag. +// The feature flag in cadence allows ENs to produce consistent results even if running with +// different cadence versions at the same height, which is useful for rolling out cadence +// upgrade without all ENs restarting all together. +// For instance, we would like to grade cadence from v1 to v3, where v3 has a new cadence feature. +// We first make a cadence v2 that has feature flag only turned on when the MinimumRequiredVersion() +// method returns v2 or above. +// So cadence v2 with the feature flag turned off will produce the same result as v1 which doesn't have the feature. +// And cadence v2 with the feature flag turned on will also produce the same result as v3 which has the feature. +// The feature flag allows us to roll out cadence v2 to all ENs which was running v1. +// And we use the MinimumRequiredVersion to control when the feature flag should be switched from off to on. +// And the switching should happen at the same height for all ENs. +// +// The height-based switch over can be done by using VersionBeacon, however, the VersionBeacon only +// defines the flow-go version, not cadence version. +// So we first read the current minimum required flow-go version from the VersionBeacon control, +// and map it to the cadence version to be used by cadence to decide feature flag status. +// +// For instance, let’s say all ENs are running flow-go v0.37.0 with cadence v1. +// We first create a version mapping entry for flow-go v0.37.1 to cadence v2, and roll out v0.37.1 to all ENs. +// v0.37.1 ENs will produce the same result as v0.37.0 ENs, because the current version beacon still returns v0.37.0, +// which maps zero cadence version, and cadence will keep the feature flag off. +// +// After all ENs have upgraded to v0.37.1, we send out a version beacon to switch to v0.37.1 at a future height, +// let’s say height 1000. +// Then what happens is that: +// 1. ENs running v0.37.0 will crash after height 999, until upgrade to higher version +// 2. ENs running v0.37.1 will execute with cadence v2 with feature flag off up until height 999, and from height 1000, +// the feature flag will be on, which means all v0.37.1 ENs will again produce consistent results for blocks above 1000. +// +// After height 1000 have been sealed, we can roll out v0.37.2 to all ENs with cadence v3, and it will produce the consistent +// result as v0.37.1. +func (c minimumCadenceRequiredVersion) MinimumRequiredVersion() (string, error) { + executionParameters := c.txnPreparer.ExecutionParameters() + + // map the minimum required flow-go version to a minimum required cadence version + cadenceVersion := mapToCadenceVersion(executionParameters.ExecutionVersion, minimumFvmToMinimumCadenceVersionMapping) + + return cadenceVersion.String(), nil +} + +func mapToCadenceVersion(flowGoVersion semver.Version, versionMapping FlowGoToCadenceVersionMapping) semver.Version { + if versionGreaterThanOrEqualTo(flowGoVersion, versionMapping.FlowGoVersion) { + return versionMapping.CadenceVersion + } else { + return semver.Version{} + } +} + +func versionGreaterThanOrEqualTo(version semver.Version, other semver.Version) bool { + return version.Compare(other) >= 0 +} + +type FlowGoToCadenceVersionMapping struct { + FlowGoVersion semver.Version + CadenceVersion semver.Version +} + +// This could also be a map, but ist not needed because we only expect one entry at a give time +// we won't be fixing 2 separate issues at 2 separate version with one deploy. +var minimumFvmToMinimumCadenceVersionMapping = FlowGoToCadenceVersionMapping{ + // Leaving this example in, so it's easier to understand + // + // FlowGoVersion: *semver.New("0.37.0"), + // CadenceVersion: *semver.New("1.0.0"), + // +} + +func SetFVMToCadenceVersionMappingForTestingOnly(mapping FlowGoToCadenceVersionMapping) { + minimumFvmToMinimumCadenceVersionMapping = mapping +} + +var _ MinimumCadenceRequiredVersion = (*minimumCadenceRequiredVersion)(nil) diff --git a/fvm/environment/minimum_required_version_test.go b/fvm/environment/minimum_required_version_test.go new file mode 100644 index 00000000000..a72e10567df --- /dev/null +++ b/fvm/environment/minimum_required_version_test.go @@ -0,0 +1,63 @@ +package environment + +import ( + "testing" + + "github.com/coreos/go-semver/semver" + "github.com/stretchr/testify/require" +) + +func Test_MapToCadenceVersion(t *testing.T) { + flowV0 := semver.Version{} + cadenceV0 := semver.Version{} + flowV1 := semver.Version{ + Major: 0, + Minor: 37, + Patch: 0, + } + cadenceV1 := semver.Version{ + Major: 1, + Minor: 0, + Patch: 0, + } + + mapping := FlowGoToCadenceVersionMapping{ + FlowGoVersion: flowV1, + CadenceVersion: cadenceV1, + } + + t.Run("no mapping, v0", func(t *testing.T) { + version := mapToCadenceVersion(flowV0, FlowGoToCadenceVersionMapping{}) + + require.Equal(t, cadenceV0, version) + }) + + t.Run("v0", func(t *testing.T) { + version := mapToCadenceVersion(flowV0, mapping) + + require.Equal(t, semver.Version{}, version) + }) + t.Run("v1 - delta", func(t *testing.T) { + + v := flowV1 + v.Patch -= 1 + + version := mapToCadenceVersion(v, mapping) + + require.Equal(t, cadenceV0, version) + }) + t.Run("v1", func(t *testing.T) { + version := mapToCadenceVersion(flowV1, mapping) + + require.Equal(t, cadenceV1, version) + }) + t.Run("v1 + delta", func(t *testing.T) { + + v := flowV1 + v.BumpPatch() + + version := mapToCadenceVersion(v, mapping) + + require.Equal(t, cadenceV1, version) + }) +} diff --git a/fvm/environment/mock/account_creator.go b/fvm/environment/mock/account_creator.go index 94a05f2a01c..360cf8481c3 100644 --- a/fvm/environment/mock/account_creator.go +++ b/fvm/environment/mock/account_creator.go @@ -3,7 +3,7 @@ package mock import ( - common "github.com/onflow/cadence/runtime/common" + common "github.com/onflow/cadence/common" mock "github.com/stretchr/testify/mock" ) diff --git a/fvm/environment/mock/account_info.go b/fvm/environment/mock/account_info.go index 44a802f1a28..4d424a91216 100644 --- a/fvm/environment/mock/account_info.go +++ b/fvm/environment/mock/account_info.go @@ -3,7 +3,7 @@ package mock import ( - common "github.com/onflow/cadence/runtime/common" + common "github.com/onflow/cadence/common" flow "github.com/onflow/flow-go/model/flow" diff --git a/fvm/environment/mock/account_key_reader.go b/fvm/environment/mock/account_key_reader.go index c1b84ddb1f7..e7adc517d78 100644 --- a/fvm/environment/mock/account_key_reader.go +++ b/fvm/environment/mock/account_key_reader.go @@ -3,11 +3,11 @@ package mock import ( - common "github.com/onflow/cadence/runtime/common" + common "github.com/onflow/cadence/common" mock "github.com/stretchr/testify/mock" - stdlib "github.com/onflow/cadence/runtime/stdlib" + stdlib "github.com/onflow/cadence/stdlib" ) // AccountKeyReader is an autogenerated mock type for the AccountKeyReader type diff --git a/fvm/environment/mock/account_key_updater.go b/fvm/environment/mock/account_key_updater.go index cd6bf7fbd9d..f3b5eb5f00a 100644 --- a/fvm/environment/mock/account_key_updater.go +++ b/fvm/environment/mock/account_key_updater.go @@ -3,13 +3,13 @@ package mock import ( - common "github.com/onflow/cadence/runtime/common" + common "github.com/onflow/cadence/common" mock "github.com/stretchr/testify/mock" - sema "github.com/onflow/cadence/runtime/sema" + sema "github.com/onflow/cadence/sema" - stdlib "github.com/onflow/cadence/runtime/stdlib" + stdlib "github.com/onflow/cadence/stdlib" ) // AccountKeyUpdater is an autogenerated mock type for the AccountKeyUpdater type diff --git a/fvm/environment/mock/account_local_id_generator.go b/fvm/environment/mock/account_local_id_generator.go index 8ff52c83663..51437d5e3e7 100644 --- a/fvm/environment/mock/account_local_id_generator.go +++ b/fvm/environment/mock/account_local_id_generator.go @@ -3,7 +3,7 @@ package mock import ( - common "github.com/onflow/cadence/runtime/common" + common "github.com/onflow/cadence/common" mock "github.com/stretchr/testify/mock" ) diff --git a/fvm/environment/mock/block_info.go b/fvm/environment/mock/block_info.go index df2b75cffc2..fc14603a92d 100644 --- a/fvm/environment/mock/block_info.go +++ b/fvm/environment/mock/block_info.go @@ -3,7 +3,7 @@ package mock import ( - stdlib "github.com/onflow/cadence/runtime/stdlib" + stdlib "github.com/onflow/cadence/stdlib" mock "github.com/stretchr/testify/mock" ) diff --git a/fvm/environment/mock/contract_updater.go b/fvm/environment/mock/contract_updater.go index 15c40a8c253..47c6ace5e04 100644 --- a/fvm/environment/mock/contract_updater.go +++ b/fvm/environment/mock/contract_updater.go @@ -3,7 +3,7 @@ package mock import ( - common "github.com/onflow/cadence/runtime/common" + common "github.com/onflow/cadence/common" environment "github.com/onflow/flow-go/fvm/environment" mock "github.com/stretchr/testify/mock" diff --git a/fvm/environment/mock/crypto_library.go b/fvm/environment/mock/crypto_library.go index e408bfa9fdc..abd1d931c3b 100644 --- a/fvm/environment/mock/crypto_library.go +++ b/fvm/environment/mock/crypto_library.go @@ -3,10 +3,10 @@ package mock import ( - sema "github.com/onflow/cadence/runtime/sema" + sema "github.com/onflow/cadence/sema" mock "github.com/stretchr/testify/mock" - stdlib "github.com/onflow/cadence/runtime/stdlib" + stdlib "github.com/onflow/cadence/stdlib" ) // CryptoLibrary is an autogenerated mock type for the CryptoLibrary type diff --git a/fvm/environment/mock/environment.go b/fvm/environment/mock/environment.go index 967095141bc..39aaf310aea 100644 --- a/fvm/environment/mock/environment.go +++ b/fvm/environment/mock/environment.go @@ -4,19 +4,19 @@ package mock import ( atree "github.com/onflow/atree" - ast "github.com/onflow/cadence/runtime/ast" + ast "github.com/onflow/cadence/ast" attribute "go.opentelemetry.io/otel/attribute" cadence "github.com/onflow/cadence" - common "github.com/onflow/cadence/runtime/common" + common "github.com/onflow/cadence/common" environment "github.com/onflow/flow-go/fvm/environment" flow "github.com/onflow/flow-go/model/flow" - interpreter "github.com/onflow/cadence/runtime/interpreter" + interpreter "github.com/onflow/cadence/interpreter" meter "github.com/onflow/flow-go/fvm/meter" @@ -26,9 +26,9 @@ import ( runtime "github.com/onflow/flow-go/fvm/runtime" - sema "github.com/onflow/cadence/runtime/sema" + sema "github.com/onflow/cadence/sema" - stdlib "github.com/onflow/cadence/runtime/stdlib" + stdlib "github.com/onflow/cadence/stdlib" time "time" @@ -907,6 +907,36 @@ func (_m *Environment) GetCurrentBlockHeight() (uint64, error) { return r0, r1 } +// GetCurrentVersionBoundary provides a mock function with given fields: +func (_m *Environment) GetCurrentVersionBoundary() (cadence.Value, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for GetCurrentVersionBoundary") + } + + var r0 cadence.Value + var r1 error + if rf, ok := ret.Get(0).(func() (cadence.Value, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() cadence.Value); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(cadence.Value) + } + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // GetInterpreterSharedState provides a mock function with given fields: func (_m *Environment) GetInterpreterSharedState() *interpreter.SharedState { ret := _m.Called() @@ -1335,6 +1365,34 @@ func (_m *Environment) MeterMemory(usage common.MemoryUsage) error { return r0 } +// MinimumRequiredVersion provides a mock function with given fields: +func (_m *Environment) MinimumRequiredVersion() (string, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for MinimumRequiredVersion") + } + + var r0 string + var r1 error + if rf, ok := ret.Get(0).(func() (string, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // ProgramLog provides a mock function with given fields: _a0 func (_m *Environment) ProgramLog(_a0 string) error { ret := _m.Called(_a0) @@ -1724,6 +1782,62 @@ func (_m *Environment) UpdateAccountContractCode(location common.AddressLocation return r0 } +// ValidateAccountCapabilitiesGet provides a mock function with given fields: inter, locationRange, address, path, wantedBorrowType, capabilityBorrowType +func (_m *Environment) ValidateAccountCapabilitiesGet(inter *interpreter.Interpreter, locationRange interpreter.LocationRange, address interpreter.AddressValue, path interpreter.PathValue, wantedBorrowType *sema.ReferenceType, capabilityBorrowType *sema.ReferenceType) (bool, error) { + ret := _m.Called(inter, locationRange, address, path, wantedBorrowType, capabilityBorrowType) + + if len(ret) == 0 { + panic("no return value specified for ValidateAccountCapabilitiesGet") + } + + var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(*interpreter.Interpreter, interpreter.LocationRange, interpreter.AddressValue, interpreter.PathValue, *sema.ReferenceType, *sema.ReferenceType) (bool, error)); ok { + return rf(inter, locationRange, address, path, wantedBorrowType, capabilityBorrowType) + } + if rf, ok := ret.Get(0).(func(*interpreter.Interpreter, interpreter.LocationRange, interpreter.AddressValue, interpreter.PathValue, *sema.ReferenceType, *sema.ReferenceType) bool); ok { + r0 = rf(inter, locationRange, address, path, wantedBorrowType, capabilityBorrowType) + } else { + r0 = ret.Get(0).(bool) + } + + if rf, ok := ret.Get(1).(func(*interpreter.Interpreter, interpreter.LocationRange, interpreter.AddressValue, interpreter.PathValue, *sema.ReferenceType, *sema.ReferenceType) error); ok { + r1 = rf(inter, locationRange, address, path, wantedBorrowType, capabilityBorrowType) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ValidateAccountCapabilitiesPublish provides a mock function with given fields: inter, locationRange, address, path, capabilityBorrowType +func (_m *Environment) ValidateAccountCapabilitiesPublish(inter *interpreter.Interpreter, locationRange interpreter.LocationRange, address interpreter.AddressValue, path interpreter.PathValue, capabilityBorrowType *interpreter.ReferenceStaticType) (bool, error) { + ret := _m.Called(inter, locationRange, address, path, capabilityBorrowType) + + if len(ret) == 0 { + panic("no return value specified for ValidateAccountCapabilitiesPublish") + } + + var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(*interpreter.Interpreter, interpreter.LocationRange, interpreter.AddressValue, interpreter.PathValue, *interpreter.ReferenceStaticType) (bool, error)); ok { + return rf(inter, locationRange, address, path, capabilityBorrowType) + } + if rf, ok := ret.Get(0).(func(*interpreter.Interpreter, interpreter.LocationRange, interpreter.AddressValue, interpreter.PathValue, *interpreter.ReferenceStaticType) bool); ok { + r0 = rf(inter, locationRange, address, path, capabilityBorrowType) + } else { + r0 = ret.Get(0).(bool) + } + + if rf, ok := ret.Get(1).(func(*interpreter.Interpreter, interpreter.LocationRange, interpreter.AddressValue, interpreter.PathValue, *interpreter.ReferenceStaticType) error); ok { + r1 = rf(inter, locationRange, address, path, capabilityBorrowType) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // ValidatePublicKey provides a mock function with given fields: key func (_m *Environment) ValidatePublicKey(key *stdlib.PublicKey) error { ret := _m.Called(key) diff --git a/fvm/environment/mock/meter.go b/fvm/environment/mock/meter.go index a156b02f0ef..d73a61c5ff7 100644 --- a/fvm/environment/mock/meter.go +++ b/fvm/environment/mock/meter.go @@ -3,7 +3,7 @@ package mock import ( - common "github.com/onflow/cadence/runtime/common" + common "github.com/onflow/cadence/common" meter "github.com/onflow/flow-go/fvm/meter" @@ -137,9 +137,9 @@ func (_m *Meter) MemoryUsed() (uint64, error) { return r0, r1 } -// MeterComputation provides a mock function with given fields: _a0, _a1 -func (_m *Meter) MeterComputation(_a0 common.ComputationKind, _a1 uint) error { - ret := _m.Called(_a0, _a1) +// MeterComputation provides a mock function with given fields: operationType, intensity +func (_m *Meter) MeterComputation(operationType common.ComputationKind, intensity uint) error { + ret := _m.Called(operationType, intensity) if len(ret) == 0 { panic("no return value specified for MeterComputation") @@ -147,7 +147,7 @@ func (_m *Meter) MeterComputation(_a0 common.ComputationKind, _a1 uint) error { var r0 error if rf, ok := ret.Get(0).(func(common.ComputationKind, uint) error); ok { - r0 = rf(_a0, _a1) + r0 = rf(operationType, intensity) } else { r0 = ret.Error(0) } diff --git a/fvm/environment/mock/minimum_cadence_required_version.go b/fvm/environment/mock/minimum_cadence_required_version.go new file mode 100644 index 00000000000..d3bff30e08b --- /dev/null +++ b/fvm/environment/mock/minimum_cadence_required_version.go @@ -0,0 +1,52 @@ +// Code generated by mockery v2.43.2. DO NOT EDIT. + +package mock + +import mock "github.com/stretchr/testify/mock" + +// MinimumCadenceRequiredVersion is an autogenerated mock type for the MinimumCadenceRequiredVersion type +type MinimumCadenceRequiredVersion struct { + mock.Mock +} + +// MinimumRequiredVersion provides a mock function with given fields: +func (_m *MinimumCadenceRequiredVersion) MinimumRequiredVersion() (string, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for MinimumRequiredVersion") + } + + var r0 string + var r1 error + if rf, ok := ret.Get(0).(func() (string, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// NewMinimumCadenceRequiredVersion creates a new instance of MinimumCadenceRequiredVersion. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewMinimumCadenceRequiredVersion(t interface { + mock.TestingT + Cleanup(func()) +}) *MinimumCadenceRequiredVersion { + mock := &MinimumCadenceRequiredVersion{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/fvm/environment/mock/transaction_info.go b/fvm/environment/mock/transaction_info.go index 30a913dd35b..b45074a8474 100644 --- a/fvm/environment/mock/transaction_info.go +++ b/fvm/environment/mock/transaction_info.go @@ -3,7 +3,7 @@ package mock import ( - common "github.com/onflow/cadence/runtime/common" + common "github.com/onflow/cadence/common" flow "github.com/onflow/flow-go/model/flow" diff --git a/fvm/environment/program_logger.go b/fvm/environment/program_logger.go index b5308ca2abd..4fbfceb793b 100644 --- a/fvm/environment/program_logger.go +++ b/fvm/environment/program_logger.go @@ -3,7 +3,7 @@ package environment import ( "time" - "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/common" "github.com/rs/zerolog" "go.opentelemetry.io/otel/attribute" otelTrace "go.opentelemetry.io/otel/trace" diff --git a/fvm/environment/program_recovery.go b/fvm/environment/program_recovery.go index a4a41f733d7..4a953b9613e 100644 --- a/fvm/environment/program_recovery.go +++ b/fvm/environment/program_recovery.go @@ -3,9 +3,9 @@ package environment import ( "fmt" - "github.com/onflow/cadence/runtime/ast" - "github.com/onflow/cadence/runtime/common" - "github.com/onflow/cadence/runtime/sema" + "github.com/onflow/cadence/ast" + "github.com/onflow/cadence/common" + "github.com/onflow/cadence/sema" "github.com/onflow/flow-go/fvm/systemcontracts" "github.com/onflow/flow-go/model/flow" @@ -201,6 +201,11 @@ func RecoveredNonFungibleTokenCode(nonFungibleTokenAddress common.Address, contr %[2]s.recoveryPanic("Collection.deposit") } + access(all) + view fun getIDs(): [UInt64] { + return self.ownedNFTs.keys + } + access(all) view fun getSupportedNFTTypes(): {Type: Bool} { %[2]s.recoveryPanic("Collection.getSupportedNFTTypes") diff --git a/fvm/environment/programs.go b/fvm/environment/programs.go index 8ca57cdbd10..563d6ebb0aa 100644 --- a/fvm/environment/programs.go +++ b/fvm/environment/programs.go @@ -7,9 +7,9 @@ import ( "golang.org/x/xerrors" "github.com/onflow/cadence" + "github.com/onflow/cadence/common" jsoncdc "github.com/onflow/cadence/encoding/json" - "github.com/onflow/cadence/runtime/common" - "github.com/onflow/cadence/runtime/interpreter" + "github.com/onflow/cadence/interpreter" "github.com/onflow/flow-go/fvm/errors" "github.com/onflow/flow-go/fvm/storage" diff --git a/fvm/environment/programs_test.go b/fvm/environment/programs_test.go index 9c8d50402cf..b0368db49e0 100644 --- a/fvm/environment/programs_test.go +++ b/fvm/environment/programs_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/common" "github.com/stretchr/testify/require" "github.com/onflow/flow-go/fvm" @@ -156,7 +156,9 @@ func Test_Programs(t *testing.T) { fvm.WithAuthorizationChecksEnabled(false), fvm.WithSequenceNumberCheckAndIncrementEnabled(false), fvm.WithCadenceLogging(true), - fvm.WithDerivedBlockData(derivedBlockData)) + fvm.WithDerivedBlockData(derivedBlockData), + // disable reading version from node version beacon otherwise it loads an extra contract + fvm.WithReadVersionFromNodeVersionBeacon(false)) var contractASnapshot *snapshot.ExecutionSnapshot var contractBSnapshot *snapshot.ExecutionSnapshot @@ -613,7 +615,9 @@ func Test_ProgramsDoubleCounting(t *testing.T) { fvm.WithSequenceNumberCheckAndIncrementEnabled(false), fvm.WithCadenceLogging(true), fvm.WithDerivedBlockData(derivedBlockData), - fvm.WithMetricsReporter(metrics)) + fvm.WithMetricsReporter(metrics), + // disable reading version from node version beacon otherwise it loads an extra contract + fvm.WithReadVersionFromNodeVersionBeacon(false)) t.Run("deploy contracts and ensure cache is empty", func(t *testing.T) { // deploy contract A diff --git a/fvm/environment/system_contracts.go b/fvm/environment/system_contracts.go index cd7c9db9339..e8e9598aa3c 100644 --- a/fvm/environment/system_contracts.go +++ b/fvm/environment/system_contracts.go @@ -2,8 +2,8 @@ package environment import ( "github.com/onflow/cadence" - "github.com/onflow/cadence/runtime/common" - "github.com/onflow/cadence/runtime/sema" + "github.com/onflow/cadence/common" + "github.com/onflow/cadence/sema" "go.opentelemetry.io/otel/attribute" "github.com/onflow/flow-go/fvm/systemcontracts" @@ -312,3 +312,21 @@ func (sys *SystemContracts) AccountsStorageCapacity( }, ) } + +var getCurrentVersionBoundarySpec = ContractFunctionSpec{ + AddressFromChain: ServiceAddress, + LocationName: systemcontracts.ContractNameNodeVersionBeacon, + FunctionName: systemcontracts.ContractVersionBeacon_getCurrentVersionBoundary, + ArgumentTypes: []sema.Type{}, +} + +// GetCurrentVersionBoundary executes the getCurrentVersionBoundary function on the NodeVersionBeacon contract. +// the function will return the version boundary (version, block height) that is currently in effect. +// the version boundary currently in effect is the highest one not above the current block height. +// if there is no existing version boundary lower than the current block height, the function will return version 0 and block height 0. +func (sys *SystemContracts) GetCurrentVersionBoundary() (cadence.Value, error) { + return sys.Invoke( + getCurrentVersionBoundarySpec, + []cadence.Value{}, + ) +} diff --git a/fvm/environment/system_contracts_test.go b/fvm/environment/system_contracts_test.go index ca9ae5a23a5..e5870107956 100644 --- a/fvm/environment/system_contracts_test.go +++ b/fvm/environment/system_contracts_test.go @@ -4,9 +4,9 @@ import ( "testing" "github.com/onflow/cadence" + "github.com/onflow/cadence/common" "github.com/onflow/cadence/runtime" - "github.com/onflow/cadence/runtime/common" - "github.com/onflow/cadence/runtime/sema" + "github.com/onflow/cadence/sema" "github.com/stretchr/testify/require" "github.com/onflow/flow-go/fvm/environment" diff --git a/fvm/environment/transaction_info.go b/fvm/environment/transaction_info.go index f7d76e165ee..6abfb367962 100644 --- a/fvm/environment/transaction_info.go +++ b/fvm/environment/transaction_info.go @@ -1,7 +1,7 @@ package environment import ( - "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/common" "github.com/onflow/flow-go/fvm/errors" "github.com/onflow/flow-go/fvm/storage/state" diff --git a/fvm/environment/value_store.go b/fvm/environment/value_store.go index 27fa80c6385..441f23f9f96 100644 --- a/fvm/environment/value_store.go +++ b/fvm/environment/value_store.go @@ -1,6 +1,7 @@ package environment import ( + "bytes" "fmt" "github.com/onflow/atree" @@ -140,7 +141,6 @@ func (store *valueStore) GetValue( return v, nil } -// TODO disable SetValue for scripts, right now the view changes are discarded func (store *valueStore) SetValue( owner []byte, keyBytes []byte, @@ -153,7 +153,16 @@ func (store *valueStore) SetValue( return errors.NewInvalidInternalStateAccessError(id, "modify") } - err := store.meter.MeterComputation( + oldValue, err := store.accounts.GetValue(id) + if err != nil { + return fmt.Errorf("get value failed: %w", err) + } + // no-op write + if bytes.Equal(oldValue, value) { + return nil + } + + err = store.meter.MeterComputation( ComputationKindSetValue, uint(len(value))) if err != nil { diff --git a/fvm/errors/errors.go b/fvm/errors/errors.go index 47997d6feec..bdb865f2cbd 100644 --- a/fvm/errors/errors.go +++ b/fvm/errors/errors.go @@ -5,8 +5,8 @@ import ( "fmt" "github.com/hashicorp/go-multierror" + "github.com/onflow/cadence/errors" "github.com/onflow/cadence/runtime" - "github.com/onflow/cadence/runtime/errors" ) type Unwrappable interface { diff --git a/fvm/errors/errors_test.go b/fvm/errors/errors_test.go index b634995b617..923b86b77ff 100644 --- a/fvm/errors/errors_test.go +++ b/fvm/errors/errors_test.go @@ -5,9 +5,9 @@ import ( "testing" "github.com/hashicorp/go-multierror" + cadenceErr "github.com/onflow/cadence/errors" "github.com/onflow/cadence/runtime" - cadenceErr "github.com/onflow/cadence/runtime/errors" - "github.com/onflow/cadence/runtime/sema" + "github.com/onflow/cadence/sema" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/fvm/evm/backends/wrappedEnv.go b/fvm/evm/backends/wrappedEnv.go index 5eea87bd128..1c7100a83aa 100644 --- a/fvm/evm/backends/wrappedEnv.go +++ b/fvm/evm/backends/wrappedEnv.go @@ -3,8 +3,8 @@ package backends import ( "github.com/onflow/atree" "github.com/onflow/cadence" + "github.com/onflow/cadence/common" "github.com/onflow/cadence/runtime" - "github.com/onflow/cadence/runtime/common" "github.com/rs/zerolog" otelTrace "go.opentelemetry.io/otel/trace" diff --git a/fvm/evm/debug/tracer.go b/fvm/evm/debug/tracer.go index 0a047aed3df..52b4a7e5236 100644 --- a/fvm/evm/debug/tracer.go +++ b/fvm/evm/debug/tracer.go @@ -76,16 +76,25 @@ func (t *CallTracer) GetResultByTxHash(txID gethCommon.Hash) json.RawMessage { } func (t *CallTracer) Collect(txID gethCommon.Hash) { + l := t.logger.With(). + Str("tx-id", txID.String()). + Str("block-id", t.blockID.String()). + Logger() + + // collect the trace result + res, found := t.resultsByTxID[txID] + if !found { + l.Error().Msg("trace result not found") + return + } + // remove the result + delete(t.resultsByTxID, txID) + // upload is concurrent and it doesn't produce any errors, as the // client doesn't expect it, we don't want to break execution flow, // in case there are errors we retry, and if we fail after retries // we log them and continue. go func() { - l := t.logger.With(). - Str("tx-id", txID.String()). - Str("block-id", t.blockID.String()). - Logger() - defer func() { if r := recover(); r != nil { err, ok := r.(error) @@ -98,20 +107,12 @@ func (t *CallTracer) Collect(txID gethCommon.Hash) { Msg("failed to collect EVM traces") } }() - - res, found := t.resultsByTxID[txID] - if !found { - l.Error().Msg("trace result not found") - return - } if err := t.uploader.Upload(TraceID(txID, t.blockID), res); err != nil { l.Error().Err(err). Str("traces", string(res)). Msg("failed to upload trace results, no more retries") return } - // remove the result - delete(t.resultsByTxID, txID) l.Debug().Msg("evm traces uploaded successfully") }() diff --git a/fvm/evm/debug/tracer_test.go b/fvm/evm/debug/tracer_test.go index 89e62944e83..32e694b4ae7 100644 --- a/fvm/evm/debug/tracer_test.go +++ b/fvm/evm/debug/tracer_test.go @@ -1,4 +1,4 @@ -package debug +package debug_test import ( "encoding/json" @@ -11,6 +11,7 @@ import ( "github.com/rs/zerolog" "github.com/stretchr/testify/require" + "github.com/onflow/flow-go/fvm/evm/debug" "github.com/onflow/flow-go/fvm/evm/testutils" "github.com/onflow/flow-go/model/flow" ) @@ -23,13 +24,13 @@ func Test_CallTracer(t *testing.T) { mockUpload := &testutils.MockUploader{ UploadFunc: func(id string, message json.RawMessage) error { - require.Equal(t, TraceID(txID, blockID), id) + require.Equal(t, debug.TraceID(txID, blockID), id) require.Equal(t, res, message) return nil }, } - tracer, err := NewEVMCallTracer(mockUpload, zerolog.Nop()) + tracer, err := debug.NewEVMCallTracer(mockUpload, zerolog.Nop()) require.NoError(t, err) tracer.WithBlockID(blockID) @@ -61,13 +62,13 @@ func Test_CallTracer(t *testing.T) { mockUpload := &testutils.MockUploader{ UploadFunc: func(id string, message json.RawMessage) error { - require.Equal(t, TraceID(txID, blockID), id) + require.Equal(t, debug.TraceID(txID, blockID), id) require.Equal(t, res, message) return nil }, } - tracer, err := NewEVMCallTracer(mockUpload, zerolog.Nop()) + tracer, err := debug.NewEVMCallTracer(mockUpload, zerolog.Nop()) require.NoError(t, err) tracer.WithBlockID(blockID) @@ -107,7 +108,7 @@ func Test_CallTracer(t *testing.T) { }, } - tracer, err := NewEVMCallTracer(mockUpload, zerolog.Nop()) + tracer, err := debug.NewEVMCallTracer(mockUpload, zerolog.Nop()) require.NoError(t, err) tracer.WithBlockID(blockID) @@ -117,7 +118,7 @@ func Test_CallTracer(t *testing.T) { }) t.Run("nop tracer", func(t *testing.T) { - tracer := nopTracer{} + tracer := debug.NopTracer require.Nil(t, tracer.TxTracer()) }) } diff --git a/fvm/evm/debug/uploader_test.go b/fvm/evm/debug/uploader_test.go index c8052a95bc6..b531ad74d3b 100644 --- a/fvm/evm/debug/uploader_test.go +++ b/fvm/evm/debug/uploader_test.go @@ -1,4 +1,4 @@ -package debug +package debug_test import ( "context" @@ -16,6 +16,7 @@ import ( "github.com/rs/zerolog" "github.com/stretchr/testify/require" + "github.com/onflow/flow-go/fvm/evm/debug" "github.com/onflow/flow-go/model/flow" testutils "github.com/onflow/flow-go/utils/unittest" ) @@ -27,7 +28,7 @@ func Test_Uploader(t *testing.T) { testutils.SkipUnless(t, testutils.TEST_REQUIRES_GCP_ACCESS, "requires GCP Bucket setup") t.Run("successfuly upload traces", func(t *testing.T) { - uploader, err := NewGCPUploader(bucket) + uploader, err := debug.NewGCPUploader(bucket) require.NoError(t, err) const testID = "test_p" @@ -56,10 +57,10 @@ func Test_TracerUploaderIntegration(t *testing.T) { testutils.SkipUnless(t, testutils.TEST_REQUIRES_GCP_ACCESS, "requires GCP Bucket setup") t.Run("successfuly uploads traces", func(t *testing.T) { - uploader, err := NewGCPUploader(bucket) + uploader, err := debug.NewGCPUploader(bucket) require.NoError(t, err) - tracer, err := NewEVMCallTracer(uploader, zerolog.Nop()) + tracer, err := debug.NewEVMCallTracer(uploader, zerolog.Nop()) require.NoError(t, err) tr := tracer.TxTracer() diff --git a/fvm/evm/emulator/config.go b/fvm/evm/emulator/config.go index a4fc0cf398c..4c84f1466e2 100644 --- a/fvm/evm/emulator/config.go +++ b/fvm/evm/emulator/config.go @@ -48,36 +48,50 @@ func (c *Config) ChainRules() gethParams.Rules { c.BlockContext.Time) } -// DefaultChainConfig is the default chain config used by the emulator -// considers majority of EVM upgrades (e.g. Cancun update) are already applied +// PreviewNetChainConfig is the chain config used by the previewnet +var PreviewNetChainConfig = MakeChainConfig(types.FlowEVMPreviewNetChainID) + +// PreviewNetChainConfig is the chain config used by the testnet +var TestNetChainConfig = MakeChainConfig(types.FlowEVMTestNetChainID) + +// MainNetChainConfig is the chain config used by the mainnet +var MainNetChainConfig = MakeChainConfig(types.FlowEVMMainNetChainID) + +// DefaultChainConfig is the chain config used by the emulator +var DefaultChainConfig = PreviewNetChainConfig + +// MakeChainConfig constructs a chain config +// it considers majority of EVM upgrades (e.g. Cancun update) are already applied // This has been done through setting the height of these changes // to zero nad setting the time for some other changes to zero // For the future changes of EVM, we need to update the EVM go mod version // and set a proper height for the specific release based on the Flow EVM heights // so it could gets activated at a desired time. -var DefaultChainConfig = &gethParams.ChainConfig{ - ChainID: types.FlowEVMPreviewNetChainID, - - // Fork scheduling based on block heights - HomesteadBlock: bigZero, - DAOForkBlock: bigZero, - DAOForkSupport: false, - EIP150Block: bigZero, - EIP155Block: bigZero, - EIP158Block: bigZero, - ByzantiumBlock: bigZero, // already on Byzantium - ConstantinopleBlock: bigZero, // already on Constantinople - PetersburgBlock: bigZero, // already on Petersburg - IstanbulBlock: bigZero, // already on Istanbul - BerlinBlock: bigZero, // already on Berlin - LondonBlock: bigZero, // already on London - MuirGlacierBlock: bigZero, // already on MuirGlacier - - // Fork scheduling based on timestamps - ShanghaiTime: &zero, // already on Shanghai - CancunTime: &zero, // already on Cancun - PragueTime: nil, // not on Prague - VerkleTime: nil, // not on Verkle +func MakeChainConfig(chainID *big.Int) *gethParams.ChainConfig { + return &gethParams.ChainConfig{ + ChainID: chainID, + + // Fork scheduling based on block heights + HomesteadBlock: bigZero, + DAOForkBlock: bigZero, + DAOForkSupport: false, + EIP150Block: bigZero, + EIP155Block: bigZero, + EIP158Block: bigZero, + ByzantiumBlock: bigZero, // already on Byzantium + ConstantinopleBlock: bigZero, // already on Constantinople + PetersburgBlock: bigZero, // already on Petersburg + IstanbulBlock: bigZero, // already on Istanbul + BerlinBlock: bigZero, // already on Berlin + LondonBlock: bigZero, // already on London + MuirGlacierBlock: bigZero, // already on MuirGlacier + + // Fork scheduling based on timestamps + ShanghaiTime: &zero, // already on Shanghai + CancunTime: &zero, // already on Cancun + PragueTime: nil, // not on Prague + VerkleTime: nil, // not on Verkle + } } // Default config supports the dynamic fee structure (EIP-1559) @@ -123,7 +137,14 @@ type Option func(*Config) *Config // WithChainID sets the evm chain ID func WithChainID(chainID *big.Int) Option { return func(c *Config) *Config { - c.ChainConfig.ChainID = chainID + switch chainID.Uint64() { + case types.FlowEVMPreviewNetChainIDInUInt64: + c.ChainConfig = PreviewNetChainConfig + case types.FlowEVMTestNetChainIDInUInt64: + c.ChainConfig = TestNetChainConfig + case types.FlowEVMMainNetChainIDInUInt64: + c.ChainConfig = MainNetChainConfig + } return c } } diff --git a/fvm/evm/emulator/emulator.go b/fvm/evm/emulator/emulator.go index 132bf32600a..53b247a1fa4 100644 --- a/fvm/evm/emulator/emulator.go +++ b/fvm/evm/emulator/emulator.go @@ -6,6 +6,7 @@ import ( "github.com/holiman/uint256" "github.com/onflow/atree" + "github.com/onflow/crypto/hash" gethCommon "github.com/onflow/go-ethereum/common" gethCore "github.com/onflow/go-ethereum/core" gethTracing "github.com/onflow/go-ethereum/core/tracing" @@ -178,7 +179,8 @@ func (bl *BlockView) RunTransaction( } // all commit errors (StateDB errors) has to be returned - if err := proc.commit(true); err != nil { + res.StateChangeCommitment, err = proc.commit(true) + if err != nil { return nil, err } @@ -224,7 +226,8 @@ func (bl *BlockView) BatchRunTransactions(txs []*gethTypes.Transaction) ([]*type } // all commit errors (StateDB errors) has to be returned - if err := proc.commit(false); err != nil { + res.StateChangeCommitment, err = proc.commit(false) + if err != nil { return nil, err } @@ -280,6 +283,11 @@ func (bl *BlockView) DryRunTransaction( // we need to skip nonce check for dry run msg.SkipAccountChecks = true + // call tracer + if proc.evm.Config.Tracer != nil && proc.evm.Config.Tracer.OnTxStart != nil { + proc.evm.Config.Tracer.OnTxStart(proc.evm.GetVMContext(), tx, msg.From) + } + // return without committing the state txResult, err = proc.run(msg, tx.Hash(), tx.Type()) if txResult.Successful() { @@ -303,6 +311,13 @@ func (bl *BlockView) DryRunTransaction( txResult.GasConsumed += txResult.GasRefund } + // call tracer on tx end + if proc.evm.Config.Tracer != nil && + proc.evm.Config.Tracer.OnTxEnd != nil && + txResult != nil { + proc.evm.Config.Tracer.OnTxEnd(txResult.Receipt(), txResult.ValidationError) + } + return txResult, err } @@ -332,18 +347,18 @@ type procedure struct { } // commit commits the changes to the state (with optional finalization) -func (proc *procedure) commit(finalize bool) error { - err := proc.state.Commit(finalize) +func (proc *procedure) commit(finalize bool) (hash.Hash, error) { + stateUpdateCommitment, err := proc.state.Commit(finalize) if err != nil { // if known types (state errors) don't do anything and return if types.IsAFatalError(err) || types.IsAStateError(err) || types.IsABackendError(err) { - return err + return stateUpdateCommitment, err } // else is a new fatal error - return types.NewFatalError(err) + return stateUpdateCommitment, types.NewFatalError(err) } - return nil + return stateUpdateCommitment, nil } func (proc *procedure) mintTo( @@ -386,7 +401,8 @@ func (proc *procedure) mintTo( } // commit and finalize the state and return any stateDB error - return res, proc.commit(true) + res.StateChangeCommitment, err = proc.commit(true) + return res, err } func (proc *procedure) withdrawFrom( @@ -432,7 +448,8 @@ func (proc *procedure) withdrawFrom( proc.state.SubBalance(bridge, value, gethTracing.BalanceIncreaseWithdrawal) // commit and finalize the state and return any stateDB error - return res, proc.commit(true) + res.StateChangeCommitment, err = proc.commit(true) + return res, err } // deployAt deploys a contract at the given target address @@ -574,7 +591,8 @@ func (proc *procedure) deployAt( res.CumulativeGasUsed = proc.config.BlockTotalGasUsedSoFar + res.GasConsumed proc.state.SetCode(addr, ret) - return res, proc.commit(true) + res.StateChangeCommitment, err = proc.commit(true) + return res, err } func (proc *procedure) runDirect( @@ -590,7 +608,8 @@ func (proc *procedure) runDirect( return nil, err } // commit and finalize the state and return any stateDB error - return res, proc.commit(true) + res.StateChangeCommitment, err = proc.commit(true) + return res, err } // run runs a geth core.message and returns the diff --git a/fvm/evm/emulator/emulator_test.go b/fvm/evm/emulator/emulator_test.go index 4e268a28902..79d20378be5 100644 --- a/fvm/evm/emulator/emulator_test.go +++ b/fvm/evm/emulator/emulator_test.go @@ -1088,13 +1088,9 @@ func TestCallingExtraPrecompiles(t *testing.T) { output := []byte{3, 4} addr := testutils.RandomAddress(t) capturedCall := &types.PrecompiledCalls{ - Address: addr, - RequiredGasCalls: []types.RequiredGasCall{{ - Input: input, - Output: uint64(10), - }}, + Address: addr, + RequiredGasCalls: []uint64{10}, RunCalls: []types.RunCall{{ - Input: input, Output: output, ErrorMsg: "", }}, @@ -1103,7 +1099,8 @@ func TestCallingExtraPrecompiles(t *testing.T) { AddressFunc: func() types.Address { return addr }, - RequiredGasFunc: func(input []byte) uint64 { + RequiredGasFunc: func(inp []byte) uint64 { + require.Equal(t, input, inp) return uint64(10) }, RunFunc: func(inp []byte) ([]byte, error) { @@ -1224,9 +1221,9 @@ func TestTransactionTracing(t *testing.T) { require.NoError(t, res.ValidationError) require.NoError(t, res.VMError) txID = res.TxHash - tracer.Collect(txID) trace = tracer.GetResultByTxHash(txID) require.NotEmpty(t, trace) + tracer.Collect(txID) testAccount.SetNonce(testAccount.Nonce() + 1) require.Eventuallyf(t, func() bool { @@ -1260,17 +1257,17 @@ func TestTransactionTracing(t *testing.T) { testAccount.Address(), types.Address{0x01, 0x02}, testContract.ByteCode, - 1_000_000, + 2_000_000, big.NewInt(0), testAccount.Nonce(), ), ) requireSuccessfulExecution(t, err, res) txID = res.TxHash - tracer.WithBlockID(blockID) - tracer.Collect(txID) trace = tracer.GetResultByTxHash(txID) require.NotEmpty(t, trace) + tracer.WithBlockID(blockID) + tracer.Collect(txID) testAccount.SetNonce(testAccount.Nonce() + 1) require.Eventuallyf(t, func() bool { @@ -1311,10 +1308,10 @@ func TestTransactionTracing(t *testing.T) { res, err := blk.RunTransaction(tx) requireSuccessfulExecution(t, err, res) txID = res.TxHash - tracer.WithBlockID(blockID) - tracer.Collect(txID) trace = tracer.GetResultByTxHash(txID) require.NotEmpty(t, trace) + tracer.WithBlockID(blockID) + tracer.Collect(txID) testAccount.SetNonce(testAccount.Nonce() + 1) require.Eventuallyf(t, func() bool { @@ -1390,9 +1387,9 @@ func TestTransactionTracing(t *testing.T) { requireSuccessfulExecution(t, err, res) txID = res.TxHash tracer.WithBlockID(blockID) - tracer.Collect(txID) trace = tracer.GetResultByTxHash(txID) require.NotEmpty(t, trace) + tracer.Collect(txID) testAccount.SetNonce(testAccount.Nonce() + 1) require.Eventuallyf(t, func() bool { @@ -1482,9 +1479,9 @@ func TestTransactionTracing(t *testing.T) { require.NoError(t, err) require.Len(t, results, 1) txID = results[0].TxHash + trace = tracer.GetResultByTxHash(txID) tracer.WithBlockID(blockID) tracer.Collect(txID) - trace = tracer.GetResultByTxHash(txID) require.Eventuallyf(t, func() bool { <-uploaded diff --git a/fvm/evm/emulator/state/account.go b/fvm/evm/emulator/state/account.go index 0b1077e1084..f2f90d3f253 100644 --- a/fvm/evm/emulator/state/account.go +++ b/fvm/evm/emulator/state/account.go @@ -42,10 +42,16 @@ func NewAccount( } } +// HasCode returns true if account has code func (a *Account) HasCode() bool { return a.CodeHash != gethTypes.EmptyCodeHash } +// HasStoredValues returns true if account has stored values +func (a *Account) HasStoredValues() bool { + return len(a.CollectionID) != 0 +} + // Encode encodes the account func (a *Account) Encode() ([]byte, error) { return rlp.EncodeToBytes(a) diff --git a/fvm/evm/emulator/state/base.go b/fvm/evm/emulator/state/base.go index 6adc37bae8e..9f11ce6e3f0 100644 --- a/fvm/evm/emulator/state/base.go +++ b/fvm/evm/emulator/state/base.go @@ -21,6 +21,8 @@ const ( CodesStorageIDKey = "CodesStorageIDKey" ) +var EmptyHash = gethCommon.Hash{} + // BaseView implements a types.BaseView // it acts as the base layer of state queries for the stateDB // it stores accounts, codes and storage slots. @@ -348,6 +350,34 @@ func (v *BaseView) DeleteAccount(addr gethCommon.Address) error { return nil } +// PurgeAllSlotsOfAnAccount purges all the slots related to an account +func (v *BaseView) PurgeAllSlotsOfAnAccount(addr gethCommon.Address) error { + acc, err := v.getAccount(addr) + if err != nil { + return err + } + if acc == nil { // if account doesn't exist return + return nil + } + col, err := v.collectionProvider.CollectionByID(acc.CollectionID) + if err != nil { + return err + } + // delete all slots related to this account (eip-6780) + keys, err := col.Destroy() + if err != nil { + return err + } + delete(v.slots, addr) + for _, key := range keys { + delete(v.cachedSlots, types.SlotAddress{ + Address: addr, + Key: gethCommon.BytesToHash(key), + }) + } + return nil +} + // Commit commits the changes to the underlying storage layers func (v *BaseView) Commit() error { // commit collection changes @@ -389,6 +419,65 @@ func (v *BaseView) NumberOfAccounts() uint64 { return v.accounts.Size() } +// AccountIterator returns an account iterator +// +// Warning! this is an expensive operation and should only be used +// for testing and exporting state operations, while no changes +// are applied to accounts. Note that the iteration order is not guaranteed. +func (v *BaseView) AccountIterator() (*AccountIterator, error) { + itr, err := v.accounts.ReadOnlyIterator() + if err != nil { + return nil, err + } + return &AccountIterator{colIterator: itr}, nil +} + +// CodeIterator returns a code iterator +// +// Warning! this is an expensive operation and should only be used +// for testing and exporting state operations, while no changes +// are applied to codes. Note that the iteration order is not guaranteed. +func (v *BaseView) CodeIterator() (*CodeIterator, error) { + itr, err := v.codes.ReadOnlyIterator() + if err != nil { + return nil, err + } + return &CodeIterator{colIterator: itr}, nil +} + +// AccountStorageIterator returns an account storage iterator +// for the given address +// +// Warning! this is an expensive operation and should only be used +// for testing and exporting state operations, while no changes +// are applied to accounts. Note that the iteration order is not guaranteed. +func (v *BaseView) AccountStorageIterator( + addr gethCommon.Address, +) (*AccountStorageIterator, error) { + acc, err := v.getAccount(addr) + if err != nil { + return nil, err + } + if acc == nil || !acc.HasStoredValues() { + return nil, fmt.Errorf("account %s has no stored value", addr.String()) + } + col, found := v.slots[addr] + if !found { + col, err = v.collectionProvider.CollectionByID(acc.CollectionID) + if err != nil { + return nil, fmt.Errorf("failed to load storage collection for account %s: %w", addr.String(), err) + } + } + itr, err := col.ReadOnlyIterator() + if err != nil { + return nil, err + } + return &AccountStorageIterator{ + address: addr, + colIterator: itr, + }, nil +} + func (v *BaseView) fetchOrCreateCollection(path string) (collection *Collection, created bool, error error) { collectionID, err := v.ledger.GetValue(v.rootAddress[:], []byte(path)) if err != nil { @@ -592,8 +681,7 @@ func (v *BaseView) storeSlot(sk types.SlotAddress, data gethCommon.Hash) error { return err } - emptyValue := gethCommon.Hash{} - if data == emptyValue { + if data == EmptyHash { delete(v.cachedSlots, sk) return col.Remove(sk.Key.Bytes()) } @@ -631,3 +719,83 @@ func (v *BaseView) getSlotCollection(acc *Account) (*Collection, error) { } return col, nil } + +// AccountIterator iterates over accounts +type AccountIterator struct { + colIterator *CollectionIterator +} + +// Next returns the next account +// if no more accounts next would return nil (no error) +func (ai *AccountIterator) Next() (*Account, error) { + _, value, err := ai.colIterator.Next() + if err != nil { + return nil, fmt.Errorf("account iteration failed: %w", err) + } + return DecodeAccount(value) +} + +// CodeIterator iterates over codes stored in EVM +// code storage only stores unique codes +type CodeIterator struct { + colIterator *CollectionIterator +} + +// Next returns the next code +// if no more codes, it return nil (no error) +func (ci *CodeIterator) Next() ( + *CodeInContext, + error, +) { + ch, encodedCC, err := ci.colIterator.Next() + if err != nil { + return nil, fmt.Errorf("code iteration failed: %w", err) + } + // no more keys + if ch == nil { + return nil, nil + } + if len(encodedCC) == 0 { + return nil, + fmt.Errorf("encoded code container is empty (code hash: %x)", ch) + } + + codeCont, err := CodeContainerFromEncoded(encodedCC) + if err != nil { + return nil, fmt.Errorf("code container decoding failed (code hash: %x)", ch) + + } + return &CodeInContext{ + Hash: gethCommon.BytesToHash(ch), + Code: codeCont.Code(), + RefCounts: codeCont.RefCount(), + }, nil +} + +// AccountStorageIterator iterates over slots of an account +type AccountStorageIterator struct { + address gethCommon.Address + colIterator *CollectionIterator +} + +// Next returns the next slot in the storage +// if no more keys, it returns nil (no error) +func (asi *AccountStorageIterator) Next() ( + *types.SlotEntry, + error, +) { + k, v, err := asi.colIterator.Next() + if err != nil { + return nil, fmt.Errorf("account storage iteration failed: %w", err) + } + // no more keys + if k == nil { + return nil, nil + } + return &types.SlotEntry{ + Address: asi.address, + Key: gethCommon.BytesToHash(k), + Value: gethCommon.BytesToHash(v), + }, nil + +} diff --git a/fvm/evm/emulator/state/base_test.go b/fvm/evm/emulator/state/base_test.go index 6832188a8c2..f1decb3090a 100644 --- a/fvm/evm/emulator/state/base_test.go +++ b/fvm/evm/emulator/state/base_test.go @@ -302,6 +302,170 @@ func TestBaseView(t *testing.T) { require.Equal(t, uint64(1), view.NumberOfAccounts()) }) + t.Run("test account iterator", func(t *testing.T) { + ledger := testutils.GetSimpleValueStore() + rootAddr := flow.Address{1, 2, 3, 4, 5, 6, 7, 8} + view, err := state.NewBaseView(ledger, rootAddr) + require.NoError(t, err) + + accountCounts := 10 + nonces := make(map[gethCommon.Address]uint64) + balances := make(map[gethCommon.Address]*uint256.Int) + codeHashes := make(map[gethCommon.Address]gethCommon.Hash) + for i := 0; i < accountCounts; i++ { + addr := testutils.RandomCommonAddress(t) + balance := testutils.RandomUint256Int(1000) + nonce := testutils.RandomBigInt(1000).Uint64() + code := testutils.RandomData(t) + codeHash := testutils.RandomCommonHash(t) + + err = view.CreateAccount(addr, balance, nonce, code, codeHash) + require.NoError(t, err) + + nonces[addr] = nonce + balances[addr] = balance + codeHashes[addr] = codeHash + } + err = view.Commit() + require.NoError(t, err) + + ai, err := view.AccountIterator() + require.NoError(t, err) + + counter := 0 + for { + acc, err := ai.Next() + require.NoError(t, err) + if acc == nil { + break + } + require.Equal(t, nonces[acc.Address], acc.Nonce) + delete(nonces, acc.Address) + require.Equal(t, balances[acc.Address].Uint64(), acc.Balance.Uint64()) + delete(balances, acc.Address) + require.Equal(t, codeHashes[acc.Address], acc.CodeHash) + delete(codeHashes, acc.Address) + counter += 1 + } + + require.Equal(t, accountCounts, counter) + }) + + t.Run("test code iterator", func(t *testing.T) { + ledger := testutils.GetSimpleValueStore() + rootAddr := flow.Address{1, 2, 3, 4, 5, 6, 7, 8} + view, err := state.NewBaseView(ledger, rootAddr) + require.NoError(t, err) + + codeCounts := 10 + codeByCodeHash := make(map[gethCommon.Hash][]byte) + refCountByCodeHash := make(map[gethCommon.Hash]uint64) + for i := 0; i < codeCounts; i++ { + + code := testutils.RandomData(t) + codeHash := testutils.RandomCommonHash(t) + refCount := 0 + // we add each code couple of times through different accounts + for j := 1; j <= i+1; j++ { + addr := testutils.RandomCommonAddress(t) + balance := testutils.RandomUint256Int(1000) + nonce := testutils.RandomBigInt(1000).Uint64() + err = view.CreateAccount(addr, balance, nonce, code, codeHash) + require.NoError(t, err) + refCount += 1 + } + codeByCodeHash[codeHash] = code + refCountByCodeHash[codeHash] = uint64(refCount) + } + err = view.Commit() + require.NoError(t, err) + + ci, err := view.CodeIterator() + require.NoError(t, err) + + counter := 0 + for { + cic, err := ci.Next() + require.NoError(t, err) + if cic == nil { + break + } + require.Equal(t, codeByCodeHash[cic.Hash], cic.Code) + delete(codeByCodeHash, cic.Hash) + require.Equal(t, refCountByCodeHash[cic.Hash], cic.RefCounts) + delete(refCountByCodeHash, cic.Hash) + counter += 1 + } + + require.Equal(t, codeCounts, counter) + }) + + t.Run("test account storage iterator", func(t *testing.T) { + ledger := testutils.GetSimpleValueStore() + rootAddr := flow.Address{1, 2, 3, 4, 5, 6, 7, 8} + view, err := state.NewBaseView(ledger, rootAddr) + require.NoError(t, err) + + addr := testutils.RandomCommonAddress(t) + code := []byte("code") + balance := testutils.RandomUint256Int(1000) + nonce := testutils.RandomBigInt(1000).Uint64() + codeHash := gethCrypto.Keccak256Hash(code) + err = view.CreateAccount(addr, balance, nonce, code, codeHash) + require.NoError(t, err) + + slotCounts := 10 + values := make(map[gethCommon.Hash]gethCommon.Hash) + + for i := 0; i < slotCounts; i++ { + key := testutils.RandomCommonHash(t) + value := testutils.RandomCommonHash(t) + + err = view.UpdateSlot( + types.SlotAddress{ + Address: addr, + Key: key, + }, value) + require.NoError(t, err) + values[key] = value + } + err = view.Commit() + require.NoError(t, err) + + asi, err := view.AccountStorageIterator(addr) + require.NoError(t, err) + + counter := 0 + for { + slot, err := asi.Next() + require.NoError(t, err) + if slot == nil { + break + } + require.Equal(t, addr, slot.Address) + require.Equal(t, values[slot.Key], slot.Value) + delete(values, slot.Key) + counter += 1 + } + + require.Equal(t, slotCounts, counter) + + // test non existing address + addr2 := testutils.RandomCommonAddress(t) + _, err = view.AccountStorageIterator(addr2) + require.Error(t, err) + + // test address without storage + err = view.CreateAccount(addr2, balance, nonce, code, codeHash) + require.NoError(t, err) + + err = view.Commit() + require.NoError(t, err) + + _, err = view.AccountStorageIterator(addr2) + require.Error(t, err) + }) + } func checkAccount(t *testing.T, diff --git a/fvm/evm/emulator/state/code.go b/fvm/evm/emulator/state/code.go index bb893a30d39..d641e43943d 100644 --- a/fvm/evm/emulator/state/code.go +++ b/fvm/evm/emulator/state/code.go @@ -3,6 +3,9 @@ package state import ( "encoding/binary" "fmt" + + gethCommon "github.com/onflow/go-ethereum/common" + "github.com/onflow/go-ethereum/rlp" ) // CodeContainer contains codes and keeps @@ -77,3 +80,24 @@ func (cc *CodeContainer) Encode() []byte { copy(encoded[8:], cc.code) return encoded } + +// CodeInContext captures a code in its context +type CodeInContext struct { + Hash gethCommon.Hash + Code []byte + RefCounts uint64 +} + +// Encoded returns the encoded content of the code in context +func (cic *CodeInContext) Encode() ([]byte, error) { + return rlp.EncodeToBytes(cic) +} + +// CodeInContextFromEncoded constructs a code in context from the encoded data +func CodeInContextFromEncoded(encoded []byte) (*CodeInContext, error) { + if len(encoded) == 0 { + return nil, nil + } + cic := &CodeInContext{} + return cic, rlp.DecodeBytes(encoded, cic) +} diff --git a/fvm/evm/emulator/state/collection.go b/fvm/evm/emulator/state/collection.go index 00b8b7934b0..dea445c6524 100644 --- a/fvm/evm/emulator/state/collection.go +++ b/fvm/evm/emulator/state/collection.go @@ -203,6 +203,39 @@ func (c *Collection) Size() uint64 { return c.omap.Count() } +// ReadOnlyIterator returns a collection iterator that +// can be used to iterate over key value pairs in the collection +// +// Warning! iteration is a fairly expensive operation and +// should only be used for testing or exporting data purposes +// Also, Collection should not be mutated while iterating over key values +func (c *Collection) ReadOnlyIterator() (*CollectionIterator, error) { + iterator, err := c.omap.ReadOnlyIterator() + if err != nil { + return nil, err + } + return &CollectionIterator{iter: iterator}, nil +} + +// CollectionIterator allows iteration over the collection key value pairs +type CollectionIterator struct { + iter atree.MapIterator +} + +// Next returns the next key value pairs, when no more element it returns +// nil as key and value (no error) +func (ci *CollectionIterator) Next() (key []byte, value []byte, err error) { + k, v, err := ci.iter.Next() + if err != nil { + return nil, nil, fmt.Errorf("collection iteration failed: %w", err) + } + // no more keys + if k == nil { + return nil, nil, nil + } + return k.(ByteStringValue).Bytes(), v.(ByteStringValue).Bytes(), nil +} + type ByteStringValue struct { data []byte size uint32 diff --git a/fvm/evm/emulator/state/exporter.go b/fvm/evm/emulator/state/exporter.go new file mode 100644 index 00000000000..49f3a0fdbd8 --- /dev/null +++ b/fvm/evm/emulator/state/exporter.go @@ -0,0 +1,163 @@ +package state + +import ( + "io" + "os" + "path/filepath" + + "github.com/onflow/atree" + gethCommon "github.com/onflow/go-ethereum/common" + + "github.com/onflow/flow-go/model/flow" +) + +const ( + ExportedAccountsFileName = "accounts.bin" + ExportedCodesFileName = "codes.bin" + ExportedSlotsFileName = "slots.bin" +) + +type Exporter struct { + ledger atree.Ledger + root flow.Address + baseView *BaseView +} + +// NewExporter constructs a new Exporter +func NewExporter(ledger atree.Ledger, root flow.Address) (*Exporter, error) { + bv, err := NewBaseView(ledger, root) + if err != nil { + return nil, err + } + return &Exporter{ + ledger: ledger, + root: root, + baseView: bv, + }, nil +} + +func (e *Exporter) Export(path string) error { + af, err := os.OpenFile(filepath.Join(path, ExportedAccountsFileName), os.O_RDWR, 0644) + if err != nil { + return err + } + defer af.Close() + + addrWithStorage, err := e.exportAccounts(af) + if err != nil { + return err + } + + cf, err := os.OpenFile(filepath.Join(path, ExportedCodesFileName), os.O_RDWR, 0644) + if err != nil { + return err + } + defer cf.Close() + + err = e.exportCodes(cf) + if err != nil { + return err + } + + sf, err := os.OpenFile(filepath.Join(path, ExportedSlotsFileName), os.O_RDWR, 0644) + if err != nil { + return err + } + defer cf.Close() + + err = e.exportSlots(addrWithStorage, sf) + if err != nil { + return err + } + return nil +} + +// exports accounts and returns a list of addresses with non-empty storage +func (e *Exporter) exportAccounts(writer io.Writer) ([]gethCommon.Address, error) { + itr, err := e.baseView.AccountIterator() + if err != nil { + return nil, err + } + // make a list of accounts with storage + addrWithSlots := make([]gethCommon.Address, 0) + for { + // TODO: we can optimize by returning the encoded value + acc, err := itr.Next() + if err != nil { + return nil, err + } + if acc == nil { + break + } + if acc.HasStoredValues() { + addrWithSlots = append(addrWithSlots, acc.Address) + } + encoded, err := acc.Encode() + if err != nil { + return nil, err + } + // write every account on a new line + _, err = writer.Write(append(encoded, byte('\n'))) + if err != nil { + return nil, err + } + } + return addrWithSlots, nil +} + +// exportCodes exports codes +func (e *Exporter) exportCodes(writer io.Writer) error { + itr, err := e.baseView.CodeIterator() + if err != nil { + return err + } + for { + cic, err := itr.Next() + if err != nil { + return err + } + if cic == nil { + break + } + encoded, err := cic.Encode() + if err != nil { + return err + } + // write every codes on a new line + _, err = writer.Write(append(encoded, byte('\n'))) + if err != nil { + return err + } + } + return nil +} + +// exportSlots exports slots (key value pairs stored under accounts) +func (e *Exporter) exportSlots(addresses []gethCommon.Address, writer io.Writer) error { + for _, addr := range addresses { + itr, err := e.baseView.AccountStorageIterator(addr) + if err != nil { + return err + } + for { + slot, err := itr.Next() + if err != nil { + return err + } + if slot == nil { + break + } + encoded, err := slot.Encode() + if err != nil { + return err + } + // write every codes on a new line + _, err = writer.Write(append(encoded, byte('\n'))) + if err != nil { + return err + } + } + } + + return nil +} diff --git a/fvm/evm/emulator/state/stateDB.go b/fvm/evm/emulator/state/stateDB.go index 7dad40cc0d6..6076d193773 100644 --- a/fvm/evm/emulator/state/stateDB.go +++ b/fvm/evm/emulator/state/stateDB.go @@ -8,6 +8,7 @@ import ( "github.com/holiman/uint256" "github.com/onflow/atree" + "github.com/onflow/crypto/hash" gethCommon "github.com/onflow/go-ethereum/common" gethStateless "github.com/onflow/go-ethereum/core/stateless" gethTracing "github.com/onflow/go-ethereum/core/tracing" @@ -347,10 +348,10 @@ func (db *StateDB) Preimages() map[gethCommon.Hash][]byte { } // Commit commits state changes back to the underlying -func (db *StateDB) Commit(finalize bool) error { - // return error if any has been acumulated +func (db *StateDB) Commit(finalize bool) (hash.Hash, error) { + // return error if any has been accumulated if db.cachedError != nil { - return wrapError(db.cachedError) + return nil, wrapError(db.cachedError) } var err error @@ -378,6 +379,7 @@ func (db *StateDB) Commit(finalize bool) error { return bytes.Compare(sortedAddresses[i][:], sortedAddresses[j][:]) < 0 }) + updateCommitter := NewUpdateCommitter() // update accounts for _, addr := range sortedAddresses { deleted := false @@ -385,36 +387,53 @@ func (db *StateDB) Commit(finalize bool) error { if db.HasSelfDestructed(addr) { err = db.baseView.DeleteAccount(addr) if err != nil { - return wrapError(err) + return nil, wrapError(err) + } + err = updateCommitter.DeleteAccount(addr) + if err != nil { + return nil, wrapError(err) } deleted = true } if deleted { continue } + + bal := db.GetBalance(addr) + nonce := db.GetNonce(addr) + code := db.GetCode(addr) + codeHash := db.GetCodeHash(addr) // create new accounts if db.IsCreated(addr) { err = db.baseView.CreateAccount( addr, - db.GetBalance(addr), - db.GetNonce(addr), - db.GetCode(addr), - db.GetCodeHash(addr), + bal, + nonce, + code, + codeHash, ) if err != nil { - return wrapError(err) + return nil, wrapError(err) + } + err = updateCommitter.CreateAccount(addr, bal, nonce, codeHash) + if err != nil { + return nil, wrapError(err) } continue } err = db.baseView.UpdateAccount( addr, - db.GetBalance(addr), - db.GetNonce(addr), - db.GetCode(addr), - db.GetCodeHash(addr), + bal, + nonce, + code, + codeHash, ) if err != nil { - return wrapError(err) + return nil, wrapError(err) + } + err = updateCommitter.UpdateAccount(addr, bal, nonce, codeHash) + if err != nil { + return nil, wrapError(err) } } @@ -437,20 +456,29 @@ func (db *StateDB) Commit(finalize bool) error { if db.HasSelfDestructed(sk.Address) { continue } + val := db.GetState(sk.Address, sk.Key) err = db.baseView.UpdateSlot( sk, - db.GetState(sk.Address, sk.Key), + val, ) if err != nil { - return wrapError(err) + return nil, wrapError(err) + } + err = updateCommitter.UpdateSlot(sk.Address, sk.Key, val) + if err != nil { + return nil, wrapError(err) } } // don't purge views yet, people might call the logs etc + updateCommit := updateCommitter.Commitment() if finalize { - return db.Finalize() + err := db.Finalize() + if err != nil { + return nil, err + } } - return nil + return updateCommit, nil } // Finalize flushes all the changes diff --git a/fvm/evm/emulator/state/stateDB_test.go b/fvm/evm/emulator/state/stateDB_test.go index ef76234ee8a..7a6594e8691 100644 --- a/fvm/evm/emulator/state/stateDB_test.go +++ b/fvm/evm/emulator/state/stateDB_test.go @@ -88,8 +88,9 @@ func TestStateDB(t *testing.T) { ret = db.GetCommittedState(addr1, key1) require.Equal(t, gethCommon.Hash{}, ret) - err = db.Commit(true) + commit, err := db.Commit(true) require.NoError(t, err) + require.NotEmpty(t, commit) ret = db.GetCommittedState(addr1, key1) require.Equal(t, value1, ret) @@ -272,10 +273,10 @@ func TestStateDB(t *testing.T) { require.NoError(t, err) db.CreateAccount(testutils.RandomCommonAddress(t)) - - err = db.Commit(true) + commit, err := db.Commit(true) // ret := db.Error() require.Error(t, err) + require.Empty(t, commit) // check wrapping require.True(t, types.IsAStateError(err)) }) @@ -297,9 +298,10 @@ func TestStateDB(t *testing.T) { db.CreateAccount(testutils.RandomCommonAddress(t)) - err = db.Commit(true) + commit, err := db.Commit(true) // ret := db.Error() require.Error(t, err) + require.Empty(t, commit) // check wrapping require.True(t, types.IsAFatalError(err)) }) @@ -321,8 +323,9 @@ func TestStateDB(t *testing.T) { // accounts without slots db.CreateAccount(addr1) require.NoError(t, db.Error()) - err = db.Commit(true) + commit, err := db.Commit(true) require.NoError(t, err) + require.NotEmpty(t, commit) root = db.GetStorageRoot(addr1) require.NoError(t, db.Error()) @@ -330,8 +333,9 @@ func TestStateDB(t *testing.T) { db.AddBalance(addr1, uint256.NewInt(100), tracing.BalanceChangeTouchAccount) require.NoError(t, db.Error()) - err = db.Commit(true) + commit, err = db.Commit(true) require.NoError(t, err) + require.NotEmpty(t, commit) root = db.GetStorageRoot(addr1) require.NoError(t, db.Error()) @@ -344,8 +348,9 @@ func TestStateDB(t *testing.T) { require.NoError(t, db.Error()) db.SetState(addr1, key, value) require.NoError(t, db.Error()) - err = db.Commit(true) + commit, err = db.Commit(true) require.NoError(t, err) + require.NotEmpty(t, commit) root = db.GetStorageRoot(addr1) require.NoError(t, db.Error()) @@ -367,8 +372,9 @@ func TestStateDB(t *testing.T) { db.SetCode(addr1, code1) db.AddBalance(addr1, balance1, tracing.BalanceChangeTransfer) require.NoError(t, db.Error()) - err = db.Commit(true) + commit, err := db.Commit(true) require.NoError(t, err) + require.NotEmpty(t, commit) // renew db db, err = state.NewStateDB(ledger, rootAddr) require.NoError(t, err) @@ -388,8 +394,9 @@ func TestStateDB(t *testing.T) { db.AddBalance(addr2, balance2, tracing.BalanceChangeTransfer) require.NoError(t, db.Error()) // commit and renew db - err = db.Commit(true) + commit, err = db.Commit(true) require.NoError(t, err) + require.NotEmpty(t, commit) db, err = state.NewStateDB(ledger, rootAddr) require.NoError(t, err) // call self destruct should not work @@ -400,8 +407,9 @@ func TestStateDB(t *testing.T) { require.Empty(t, db.GetCode(addr2)) require.NoError(t, db.Error()) // commit and renew db - err = db.Commit(true) + commit, err = db.Commit(true) require.NoError(t, err) + require.NotEmpty(t, commit) db, err = state.NewStateDB(ledger, rootAddr) require.NoError(t, err) // set code and call contract creation @@ -411,8 +419,9 @@ func TestStateDB(t *testing.T) { // now calling selfdestruct should do the job db.Selfdestruct6780(addr2) require.NoError(t, db.Error()) - err = db.Commit(true) + commit, err = db.Commit(true) require.NoError(t, err) + require.NotEmpty(t, commit) db, err = state.NewStateDB(ledger, rootAddr) require.NoError(t, err) // now query @@ -438,8 +447,9 @@ func TestStateDB(t *testing.T) { db.Selfdestruct6780(addr3) require.NoError(t, db.Error()) // commit changes - err = db.Commit(true) + commit, err = db.Commit(true) require.NoError(t, err) + require.NotEmpty(t, commit) // renew db db, err = state.NewStateDB(ledger, rootAddr) require.NoError(t, err) diff --git a/fvm/evm/emulator/state/state_growth_test.go b/fvm/evm/emulator/state/state_growth_test.go index b2622b42a03..2ba0b12c2c3 100644 --- a/fvm/evm/emulator/state/state_growth_test.go +++ b/fvm/evm/emulator/state/state_growth_test.go @@ -64,7 +64,7 @@ func (s *storageTest) run(runner func(state types.StateDB)) error { runner(state) - err = state.Commit(true) + _, err = state.Commit(true) if err != nil { return err } diff --git a/fvm/evm/emulator/state/updateCommitter.go b/fvm/evm/emulator/state/updateCommitter.go new file mode 100644 index 00000000000..e2c3f331c6e --- /dev/null +++ b/fvm/evm/emulator/state/updateCommitter.go @@ -0,0 +1,133 @@ +package state + +import ( + "encoding/binary" + + "github.com/holiman/uint256" + "github.com/onflow/crypto/hash" + gethCommon "github.com/onflow/go-ethereum/common" +) + +type OpCode byte + +const ( + UnknownOpCode OpCode = 0 + + AccountCreationOpCode OpCode = 1 + AccountUpdateOpCode OpCode = 2 + AccountDeletionOpCode OpCode = 3 + SlotUpdateOpCode OpCode = 4 +) + +const ( + opcodeByteSize = 1 + addressByteSize = gethCommon.AddressLength + nonceByteSize = 8 + balanceByteSize = 32 + hashByteSize = gethCommon.HashLength + accountDeletionBufferSize = opcodeByteSize + addressByteSize + accountCreationBufferSize = opcodeByteSize + + addressByteSize + + nonceByteSize + + balanceByteSize + + hashByteSize + accountUpdateBufferSize = accountCreationBufferSize + slotUpdateBufferSize = opcodeByteSize + + addressByteSize + + hashByteSize + + hashByteSize +) + +// UpdateCommitter captures operations (delta) through +// a set of calls (order matters) and constructs a commitment over the state changes. +type UpdateCommitter struct { + hasher hash.Hasher +} + +// NewUpdateCommitter constructs a new UpdateCommitter +func NewUpdateCommitter() *UpdateCommitter { + return &UpdateCommitter{ + hasher: hash.NewSHA3_256(), + } +} + +// CreateAccount captures a create account operation +func (dc *UpdateCommitter) CreateAccount( + addr gethCommon.Address, + balance *uint256.Int, + nonce uint64, + codeHash gethCommon.Hash, +) error { + buffer := make([]byte, accountCreationBufferSize) + var index int + buffer[0] = byte(AccountCreationOpCode) + index += opcodeByteSize + copy(buffer[index:index+addressByteSize], addr.Bytes()) + index += addressByteSize + encodedBalance := balance.Bytes32() + copy(buffer[index:index+balanceByteSize], encodedBalance[:]) + index += balanceByteSize + binary.BigEndian.PutUint64(buffer[index:index+nonceByteSize], nonce) + index += nonceByteSize + copy(buffer[index:index+hashByteSize], codeHash.Bytes()) + _, err := dc.hasher.Write(buffer) + return err +} + +// UpdateAccount captures an update account operation +func (dc *UpdateCommitter) UpdateAccount( + addr gethCommon.Address, + balance *uint256.Int, + nonce uint64, + codeHash gethCommon.Hash, +) error { + buffer := make([]byte, accountUpdateBufferSize) + var index int + buffer[0] = byte(AccountUpdateOpCode) + index += opcodeByteSize + copy(buffer[index:index+addressByteSize], addr.Bytes()) + index += addressByteSize + encodedBalance := balance.Bytes32() + copy(buffer[index:index+balanceByteSize], encodedBalance[:]) + index += balanceByteSize + binary.BigEndian.PutUint64(buffer[index:index+nonceByteSize], nonce) + index += nonceByteSize + copy(buffer[index:index+hashByteSize], codeHash.Bytes()) + _, err := dc.hasher.Write(buffer) + return err +} + +// DeleteAccount captures a delete account operation +func (dc *UpdateCommitter) DeleteAccount(addr gethCommon.Address) error { + buffer := make([]byte, accountDeletionBufferSize) + var index int + buffer[0] = byte(AccountDeletionOpCode) + index += opcodeByteSize + copy(buffer[index:index+addressByteSize], addr.Bytes()) + _, err := dc.hasher.Write(buffer) + return err +} + +// UpdateSlot captures a update slot operation +func (dc *UpdateCommitter) UpdateSlot( + addr gethCommon.Address, + key gethCommon.Hash, + value gethCommon.Hash, +) error { + buffer := make([]byte, slotUpdateBufferSize) + var index int + buffer[0] = byte(SlotUpdateOpCode) + index += opcodeByteSize + copy(buffer[index:index+addressByteSize], addr.Bytes()) + index += addressByteSize + copy(buffer[index:index+hashByteSize], key.Bytes()) + index += hashByteSize + copy(buffer[index:index+hashByteSize], value.Bytes()) + _, err := dc.hasher.Write(buffer) + return err +} + +// Commitment calculates and returns the commitment +func (dc *UpdateCommitter) Commitment() hash.Hash { + return dc.hasher.SumHash() +} diff --git a/fvm/evm/emulator/state/updateCommitter_test.go b/fvm/evm/emulator/state/updateCommitter_test.go new file mode 100644 index 00000000000..ab0be67a08f --- /dev/null +++ b/fvm/evm/emulator/state/updateCommitter_test.go @@ -0,0 +1,129 @@ +package state_test + +import ( + "testing" + + "github.com/holiman/uint256" + "github.com/onflow/crypto/hash" + "github.com/stretchr/testify/require" + + "github.com/onflow/flow-go/fvm/evm/emulator/state" + "github.com/onflow/flow-go/fvm/evm/testutils" +) + +func TestChangeCommitter(t *testing.T) { + + addr := testutils.RandomAddress(t).ToCommon() + balance := uint256.NewInt(200) + nonce := uint64(1) + nonceBytes := []byte{0, 0, 0, 0, 0, 0, 0, 1} + randomHash := testutils.RandomCommonHash(t) + key := testutils.RandomCommonHash(t) + value := testutils.RandomCommonHash(t) + + t.Run("test create account", func(t *testing.T) { + dc := state.NewUpdateCommitter() + err := dc.CreateAccount(addr, balance, nonce, randomHash) + require.NoError(t, err) + + hasher := hash.NewSHA3_256() + + input := []byte{byte(state.AccountCreationOpCode)} + input = append(input, addr.Bytes()...) + encodedBalance := balance.Bytes32() + input = append(input, encodedBalance[:]...) + input = append(input, nonceBytes...) + input = append(input, randomHash.Bytes()...) + + n, err := hasher.Write(input) + require.NoError(t, err) + require.Equal(t, 93, n) + + expectedCommit := hasher.SumHash() + commit := dc.Commitment() + require.Equal(t, expectedCommit, commit) + }) + + t.Run("test update account", func(t *testing.T) { + dc := state.NewUpdateCommitter() + err := dc.UpdateAccount(addr, balance, nonce, randomHash) + require.NoError(t, err) + + hasher := hash.NewSHA3_256() + input := []byte{byte(state.AccountUpdateOpCode)} + input = append(input, addr.Bytes()...) + encodedBalance := balance.Bytes32() + input = append(input, encodedBalance[:]...) + input = append(input, nonceBytes...) + input = append(input, randomHash.Bytes()...) + + n, err := hasher.Write(input) + require.NoError(t, err) + require.Equal(t, 93, n) + + expectedCommit := hasher.SumHash() + commit := dc.Commitment() + require.Equal(t, expectedCommit, commit) + }) + + t.Run("test delete account", func(t *testing.T) { + dc := state.NewUpdateCommitter() + err := dc.DeleteAccount(addr) + require.NoError(t, err) + + hasher := hash.NewSHA3_256() + input := []byte{byte(state.AccountDeletionOpCode)} + input = append(input, addr.Bytes()...) + + n, err := hasher.Write(input) + require.NoError(t, err) + require.Equal(t, 21, n) + + expectedCommit := hasher.SumHash() + commit := dc.Commitment() + require.Equal(t, expectedCommit, commit) + }) + + t.Run("test update slot", func(t *testing.T) { + dc := state.NewUpdateCommitter() + err := dc.UpdateSlot(addr, key, value) + require.NoError(t, err) + + hasher := hash.NewSHA3_256() + + input := []byte{byte(state.SlotUpdateOpCode)} + input = append(input, addr.Bytes()...) + input = append(input, key[:]...) + input = append(input, value[:]...) + + n, err := hasher.Write(input) + require.NoError(t, err) + require.Equal(t, 85, n) + + expectedCommit := hasher.SumHash() + commit := dc.Commitment() + require.Equal(t, expectedCommit, commit) + }) +} + +func BenchmarkDeltaCommitter(b *testing.B) { + addr := testutils.RandomAddress(b) + balance := uint256.NewInt(200) + nonce := uint64(100) + randomHash := testutils.RandomCommonHash(b) + dc := state.NewUpdateCommitter() + + numberOfAccountUpdates := 10 + for i := 0; i < numberOfAccountUpdates; i++ { + err := dc.UpdateAccount(addr.ToCommon(), balance, nonce, randomHash) + require.NoError(b, err) + } + + numberOfSlotUpdates := 10 + for i := 0; i < numberOfSlotUpdates; i++ { + err := dc.UpdateSlot(addr.ToCommon(), randomHash, randomHash) + require.NoError(b, err) + } + com := dc.Commitment() + require.NotEmpty(b, com) +} diff --git a/fvm/evm/emulator/tracker.go b/fvm/evm/emulator/tracker.go index 6cbb1c21d28..a35b39f82b6 100644 --- a/fvm/evm/emulator/tracker.go +++ b/fvm/evm/emulator/tracker.go @@ -38,10 +38,7 @@ func (ct *CallTracker) CaptureRequiredGas(address types.Address, input []byte, o ct.callsByAddress[address] = calls } - calls.RequiredGasCalls = append(calls.RequiredGasCalls, types.RequiredGasCall{ - Input: input, - Output: output, - }) + calls.RequiredGasCalls = append(calls.RequiredGasCalls, output) } // CaptureRun captures a run calls @@ -61,7 +58,6 @@ func (ct *CallTracker) CaptureRun(address types.Address, input []byte, output [] errMsg = err.Error() } calls.RunCalls = append(calls.RunCalls, types.RunCall{ - Input: input, Output: output, ErrorMsg: errMsg, }) diff --git a/fvm/evm/emulator/tracker_test.go b/fvm/evm/emulator/tracker_test.go index 176b43d42e4..19f8ee57a28 100644 --- a/fvm/evm/emulator/tracker_test.go +++ b/fvm/evm/emulator/tracker_test.go @@ -15,19 +15,31 @@ func TestTracker(t *testing.T) { apc := testutils.AggregatedPrecompiledCallsFixture(t) var runCallCounter int var requiredGasCallCounter int + + reqGasCallInputs := make([][]byte, len(apc[0].RequiredGasCalls)) + runCallInputs := make([][]byte, len(apc[0].RunCalls)) + + for i := range apc[0].RequiredGasCalls { + reqGasCallInputs[i] = testutils.RandomData(t) + } + + for i := range apc[0].RunCalls { + runCallInputs[i] = testutils.RandomData(t) + } + pc := &MockedPrecompiled{ AddressFunc: func() types.Address { return apc[0].Address }, RequiredGasFunc: func(input []byte) uint64 { res := apc[0].RequiredGasCalls[requiredGasCallCounter] - require.Equal(t, res.Input, input) + require.Equal(t, reqGasCallInputs[requiredGasCallCounter], input) requiredGasCallCounter += 1 - return res.Output + return res }, RunFunc: func(input []byte) ([]byte, error) { res := apc[0].RunCalls[runCallCounter] - require.Equal(t, res.Input, input) + require.Equal(t, runCallInputs[runCallCounter], input) runCallCounter += 1 var err error if len(res.ErrorMsg) > 0 { @@ -40,12 +52,13 @@ func TestTracker(t *testing.T) { wpc := tracker.RegisterPrecompiledContract(pc) require.Equal(t, apc[0].Address, wpc.Address()) + for _, pc := range apc { - for _, call := range pc.RequiredGasCalls { - require.Equal(t, call.Output, wpc.RequiredGas(call.Input)) + for i, call := range pc.RequiredGasCalls { + require.Equal(t, call, wpc.RequiredGas(reqGasCallInputs[i])) } - for _, call := range pc.RunCalls { - ret, err := wpc.Run(call.Input) + for i, call := range pc.RunCalls { + ret, err := wpc.Run(runCallInputs[i]) require.Equal(t, call.Output, ret) errMsg := "" if err != nil { diff --git a/fvm/evm/events/events.go b/fvm/evm/events/events.go index 8d3d800e7c4..584f6814104 100644 --- a/fvm/evm/events/events.go +++ b/fvm/evm/events/events.go @@ -6,7 +6,6 @@ import ( "github.com/onflow/cadence" "github.com/onflow/cadence/encoding/ccf" gethCommon "github.com/onflow/go-ethereum/common" - "github.com/onflow/go-ethereum/rlp" "github.com/onflow/flow-go/fvm/evm/stdlib" "github.com/onflow/flow-go/fvm/evm/types" @@ -56,28 +55,9 @@ func NewTransactionEvent( } func (p *transactionEvent) ToCadence(chainID flow.ChainID) (cadence.Event, error) { - var encodedLogs []byte - var err error - if len(p.Result.Logs) > 0 { - encodedLogs, err = rlp.EncodeToBytes(p.Result.Logs) - if err != nil { - return cadence.Event{}, err - } - } - - deployedAddress := cadence.String("") - if p.Result.DeployedContractAddress != nil { - deployedAddress = cadence.String(p.Result.DeployedContractAddress.String()) - } - - errorMsg := "" - if p.Result.VMError != nil { - errorMsg = p.Result.VMError.Error() - } - // both error would never happen at the same time - // but in case the priority is by validation error - if p.Result.ValidationError != nil { - errorMsg = p.Result.ValidationError.Error() + encodedLogs, err := p.Result.RLPEncodedLogs() + if err != nil { + return cadence.Event{}, err } eventType := stdlib.CadenceTypesForChain(chainID).TransactionExecuted @@ -88,13 +68,14 @@ func (p *transactionEvent) ToCadence(chainID flow.ChainID) (cadence.Event, error cadence.NewUInt8(p.Result.TxType), bytesToCadenceUInt8ArrayValue(p.Payload), cadence.NewUInt16(uint16(p.Result.ResultSummary().ErrorCode)), - cadence.String(errorMsg), + cadence.String(p.Result.ErrorMsg()), cadence.NewUInt64(p.Result.GasConsumed), - deployedAddress, + cadence.String(p.Result.DeployedContractAddressString()), bytesToCadenceUInt8ArrayValue(encodedLogs), cadence.NewUInt64(p.BlockHeight), bytesToCadenceUInt8ArrayValue(p.Result.ReturnedData), bytesToCadenceUInt8ArrayValue(p.Result.PrecompiledCalls), + checksumToCadenceArrayValue(p.Result.StateChangeChecksum()), }).WithType(eventType), nil } @@ -188,6 +169,23 @@ func DecodeBlockEventPayload(event cadence.Event) (*BlockEventPayload, error) { } type TransactionEventPayload struct { + Hash gethCommon.Hash `cadence:"hash"` + Index uint16 `cadence:"index"` + TransactionType uint8 `cadence:"type"` + Payload []byte `cadence:"payload"` + ErrorCode uint16 `cadence:"errorCode"` + GasConsumed uint64 `cadence:"gasConsumed"` + ContractAddress string `cadence:"contractAddress"` + Logs []byte `cadence:"logs"` + BlockHeight uint64 `cadence:"blockHeight"` + ErrorMessage string `cadence:"errorMessage"` + ReturnedData []byte `cadence:"returnedData"` + PrecompiledCalls []byte `cadence:"precompiledCalls"` + StateUpdateChecksum [types.ChecksumLength]byte `cadence:"stateUpdateChecksum"` +} + +// transactionEventPayloadV0 legacy format of the transaction event without stateUpdateChecksum field +type transactionEventPayloadV0 struct { Hash gethCommon.Hash `cadence:"hash"` Index uint16 `cadence:"index"` TransactionType uint8 `cadence:"type"` @@ -202,11 +200,40 @@ type TransactionEventPayload struct { PrecompiledCalls []byte `cadence:"precompiledCalls"` } +// decodeLegacyTransactionEventPayload decodes any legacy transaction formats into +// current version of the transaction event payload. +func decodeLegacyTransactionEventPayload(event cadence.Event) (*TransactionEventPayload, error) { + var tx transactionEventPayloadV0 + if err := cadence.DecodeFields(event, &tx); err != nil { + return nil, err + } + return &TransactionEventPayload{ + Hash: tx.Hash, + Index: tx.Index, + TransactionType: tx.TransactionType, + Payload: tx.Payload, + ErrorCode: tx.ErrorCode, + GasConsumed: tx.GasConsumed, + ContractAddress: tx.ContractAddress, + Logs: tx.Logs, + BlockHeight: tx.BlockHeight, + ErrorMessage: tx.ErrorMessage, + ReturnedData: tx.ReturnedData, + PrecompiledCalls: tx.PrecompiledCalls, + }, nil +} + // DecodeTransactionEventPayload decodes Cadence event into transaction event payload. func DecodeTransactionEventPayload(event cadence.Event) (*TransactionEventPayload, error) { var tx TransactionEventPayload - err := cadence.DecodeFields(event, &tx) - return &tx, err + if err := cadence.DecodeFields(event, &tx); err != nil { + if legTx, err := decodeLegacyTransactionEventPayload(event); err == nil { + return legTx, nil + } + return nil, err + } + return &tx, nil + } // FLOWTokensDepositedEventPayload captures payloads for a FlowTokenDeposited event diff --git a/fvm/evm/events/events_test.go b/fvm/evm/events/events_test.go index 4b5d3420f6b..50fd5ba15be 100644 --- a/fvm/evm/events/events_test.go +++ b/fvm/evm/events/events_test.go @@ -11,8 +11,8 @@ import ( "github.com/onflow/go-ethereum/core/vm" + cdcCommon "github.com/onflow/cadence/common" "github.com/onflow/cadence/encoding/ccf" - cdcCommon "github.com/onflow/cadence/runtime/common" gethCommon "github.com/onflow/go-ethereum/common" gethTypes "github.com/onflow/go-ethereum/core/types" "github.com/onflow/go-ethereum/rlp" @@ -86,6 +86,7 @@ func TestEVMTransactionExecutedEventCCFEncodingDecoding(t *testing.T) { txHash := testutils.RandomCommonHash(t) blockHash := testutils.RandomCommonHash(t) data := "000000000000000000000000000000000000000000000000000000000000002a" + stateUpdateCommit := testutils.RandomCommonHash(t).Bytes() dataBytes, err := hex.DecodeString(data) require.NoError(t, err) blockHeight := uint64(2) @@ -111,6 +112,7 @@ func TestEVMTransactionExecutedEventCCFEncodingDecoding(t *testing.T) { ReturnedData: dataBytes, Logs: []*gethTypes.Log{log}, TxHash: txHash, + StateChangeCommitment: stateUpdateCommit, } t.Run("evm.TransactionExecuted with failed status", func(t *testing.T) { @@ -129,6 +131,7 @@ func TestEVMTransactionExecutedEventCCFEncodingDecoding(t *testing.T) { assert.Equal(t, tep.GasConsumed, txResult.GasConsumed) assert.Equal(t, tep.ErrorMessage, txResult.VMError.Error()) assert.Equal(t, tep.ReturnedData, txResult.ReturnedData) + assert.Equal(t, tep.StateUpdateChecksum[:], stateUpdateCommit[:types.ChecksumLength]) assert.Equal( t, tep.ContractAddress, diff --git a/fvm/evm/events/utils.go b/fvm/evm/events/utils.go index b75eff4fcfa..1fe164d6b54 100644 --- a/fvm/evm/events/utils.go +++ b/fvm/evm/events/utils.go @@ -3,6 +3,8 @@ package events import ( "github.com/onflow/cadence" gethCommon "github.com/onflow/go-ethereum/common" + + "github.com/onflow/flow-go/fvm/evm/types" ) // cadenceArrayTypeOfUInt8 is the Cadence type [UInt8] @@ -30,3 +32,16 @@ func hashToCadenceArrayValue(hash gethCommon.Hash) cadence.Array { return cadence.NewArray(values). WithType(cadenceHashType) } + +// checksumType is the Cadence type [UInt8;4] +var checksumType = cadence.NewConstantSizedArrayType(types.ChecksumLength, cadence.UInt8Type) + +// checksumToCadenceArrayValue converts a checksum ([4]byte) into a Cadence array of type [UInt8;4] +func checksumToCadenceArrayValue(checksum [types.ChecksumLength]byte) cadence.Array { + values := make([]cadence.Value, types.ChecksumLength) + for i := 0; i < types.ChecksumLength; i++ { + values[i] = cadence.NewUInt8(checksum[i]) + } + return cadence.NewArray(values). + WithType(checksumType) +} diff --git a/fvm/evm/events/utils_test.go b/fvm/evm/events/utils_test.go index 976e37c7d20..6d59c18d4ce 100644 --- a/fvm/evm/events/utils_test.go +++ b/fvm/evm/events/utils_test.go @@ -72,3 +72,19 @@ func TestHashToCadenceArrayValue(t *testing.T) { inCadence, ) } + +func TestHashToChecksumValue(t *testing.T) { + t.Parallel() + + checksum := [4]byte{1, 2, 3, 4} + inCadence := checksumToCadenceArrayValue(checksum) + require.Equal(t, + cadence.NewArray([]cadence.Value{ + cadence.UInt8(1), + cadence.UInt8(2), + cadence.UInt8(3), + cadence.UInt8(4), + }).WithType(checksumType), + inCadence, + ) +} diff --git a/fvm/evm/evm.go b/fvm/evm/evm.go index ce79ceac6d5..47df0257a48 100644 --- a/fvm/evm/evm.go +++ b/fvm/evm/evm.go @@ -3,8 +3,8 @@ package evm import ( "fmt" + "github.com/onflow/cadence/common" "github.com/onflow/cadence/runtime" - "github.com/onflow/cadence/runtime/common" "github.com/onflow/flow-go/fvm/environment" "github.com/onflow/flow-go/fvm/evm/backends" diff --git a/fvm/evm/evm_test.go b/fvm/evm/evm_test.go index e7c947ae5a4..70357202f74 100644 --- a/fvm/evm/evm_test.go +++ b/fvm/evm/evm_test.go @@ -27,7 +27,6 @@ import ( "github.com/onflow/flow-go/fvm/evm/events" "github.com/onflow/flow-go/fvm/evm/impl" "github.com/onflow/flow-go/fvm/evm/stdlib" - "github.com/onflow/flow-go/fvm/evm/testutils" . "github.com/onflow/flow-go/fvm/evm/testutils" "github.com/onflow/flow-go/fvm/evm/types" "github.com/onflow/flow-go/fvm/storage/snapshot" @@ -112,12 +111,12 @@ func TestEVMRun(t *testing.T) { // assert event fields are correct require.Len(t, output.Events, 2) txEvent := output.Events[0] - txEventPayload := testutils.TxEventToPayload(t, txEvent, sc.EVMContract.Address) + txEventPayload := TxEventToPayload(t, txEvent, sc.EVMContract.Address) require.NoError(t, err) // fee transfer event feeTransferEvent := output.Events[1] - feeTranferEventPayload := testutils.TxEventToPayload(t, feeTransferEvent, sc.EVMContract.Address) + feeTranferEventPayload := TxEventToPayload(t, feeTransferEvent, sc.EVMContract.Address) require.NoError(t, err) require.Equal(t, uint16(types.ErrCodeNoError), feeTranferEventPayload.ErrorCode) require.Equal(t, uint16(1), feeTranferEventPayload.Index) @@ -379,7 +378,7 @@ func TestEVMRun(t *testing.T) { require.NotEmpty(t, state.WriteSet) txEvent := output.Events[0] - txEventPayload := testutils.TxEventToPayload(t, txEvent, sc.EVMContract.Address) + txEventPayload := TxEventToPayload(t, txEvent, sc.EVMContract.Address) require.NotEmpty(t, txEventPayload.Hash) @@ -512,7 +511,7 @@ func TestEVMBatchRun(t *testing.T) { // last event is fee transfer event feeTransferEvent := output.Events[batchCount] - feeTranferEventPayload := testutils.TxEventToPayload(t, feeTransferEvent, sc.EVMContract.Address) + feeTranferEventPayload := TxEventToPayload(t, feeTransferEvent, sc.EVMContract.Address) require.NoError(t, err) require.Equal(t, uint16(types.ErrCodeNoError), feeTranferEventPayload.ErrorCode) require.Equal(t, uint16(batchCount), feeTranferEventPayload.Index) @@ -996,7 +995,7 @@ func TestEVMAddressDeposit(t *testing.T) { // tx executed event txEvent := output.Events[2] - txEventPayload := testutils.TxEventToPayload(t, txEvent, sc.EVMContract.Address) + txEventPayload := TxEventToPayload(t, txEvent, sc.EVMContract.Address) // deposit event depositEvent := output.Events[3] @@ -1250,7 +1249,7 @@ func TestCadenceOwnedAccountFunctionalities(t *testing.T) { )) addr := cadence.NewArray( - ConvertToCadence(testutils.RandomAddress(t).Bytes()), + ConvertToCadence(RandomAddress(t).Bytes()), ).WithType(stdlib.EVMAddressBytesCadenceType) script := fvm.Script(code).WithArguments( diff --git a/fvm/evm/handler/blockHashList.go b/fvm/evm/handler/blockHashList.go index cd5bda0caa2..86ea5738b5a 100644 --- a/fvm/evm/handler/blockHashList.go +++ b/fvm/evm/handler/blockHashList.go @@ -32,7 +32,7 @@ const ( // smaller fixed size buckets to minimize the // number of bytes read and written during set/get operations. type BlockHashList struct { - backend types.Backend + backend types.BackendStorage rootAddress flow.Address // cached meta data @@ -46,7 +46,7 @@ type BlockHashList struct { // It tries to load the metadata from the backend // and if not exist it creates one func NewBlockHashList( - backend types.Backend, + backend types.BackendStorage, rootAddress flow.Address, capacity int, ) (*BlockHashList, error) { diff --git a/fvm/evm/handler/handler.go b/fvm/evm/handler/handler.go index 949c4218934..f2943b9243b 100644 --- a/fvm/evm/handler/handler.go +++ b/fvm/evm/handler/handler.go @@ -4,7 +4,7 @@ import ( "fmt" "math/big" - "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/common" gethCommon "github.com/onflow/go-ethereum/common" gethTypes "github.com/onflow/go-ethereum/core/types" "go.opentelemetry.io/otel/attribute" diff --git a/fvm/evm/handler/handler_test.go b/fvm/evm/handler/handler_test.go index 598d7c05ef9..aeebd761cc3 100644 --- a/fvm/evm/handler/handler_test.go +++ b/fvm/evm/handler/handler_test.go @@ -8,7 +8,7 @@ import ( "testing" "time" - "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/common" gethCommon "github.com/onflow/go-ethereum/common" gethCore "github.com/onflow/go-ethereum/core" gethTypes "github.com/onflow/go-ethereum/core/types" @@ -668,16 +668,12 @@ func TestHandler_COA(t *testing.T) { require.Len(t, pc.RequiredGasCalls, 1) require.Equal(t, pc.RequiredGasCalls[0], - types.RequiredGasCall{ - Input: precompiles.FlowBlockHeightFuncSig[:], - Output: precompiles.FlowBlockHeightFixedGas, - }, + precompiles.FlowBlockHeightFixedGas, ) require.Len(t, pc.RunCalls, 1) require.Equal(t, pc.RunCalls[0], types.RunCall{ - Input: precompiles.FlowBlockHeightFuncSig[:], Output: ret.ReturnedData, ErrorMsg: "", }, diff --git a/fvm/evm/handler/precompiles.go b/fvm/evm/handler/precompiles.go index a95e7af8386..7919749cdfc 100644 --- a/fvm/evm/handler/precompiles.go +++ b/fvm/evm/handler/precompiles.go @@ -5,7 +5,7 @@ import ( "fmt" "github.com/onflow/cadence" - "github.com/onflow/cadence/runtime/sema" + "github.com/onflow/cadence/sema" "github.com/onflow/flow-go/fvm/environment" "github.com/onflow/flow-go/fvm/evm/precompiles" diff --git a/fvm/evm/impl/abi.go b/fvm/evm/impl/abi.go index 812e00ba2cb..1987f3f865d 100644 --- a/fvm/evm/impl/abi.go +++ b/fvm/evm/impl/abi.go @@ -6,9 +6,10 @@ import ( "reflect" "strings" - "github.com/onflow/cadence/runtime/common" - "github.com/onflow/cadence/runtime/errors" - "github.com/onflow/cadence/runtime/interpreter" + "github.com/onflow/cadence/common" + "github.com/onflow/cadence/errors" + "github.com/onflow/cadence/interpreter" + "github.com/onflow/cadence/sema" gethABI "github.com/onflow/go-ethereum/accounts/abi" gethCommon "github.com/onflow/go-ethereum/common" @@ -21,7 +22,8 @@ const abiEncodingByteSize = 32 // abiEncodingError type abiEncodingError struct { - Type interpreter.StaticType + Type interpreter.StaticType + Message string } var _ errors.UserError = abiEncodingError{} @@ -38,6 +40,12 @@ func (e abiEncodingError) Error() string { b.WriteString(ty.String()) } + message := e.Message + if message != "" { + b.WriteString(": ") + b.WriteString(message) + } + return b.String() } @@ -70,13 +78,33 @@ func (e abiDecodingError) Error() string { return b.String() } +type evmSpecialTypeIDs struct { + AddressTypeID common.TypeID + BytesTypeID common.TypeID + Bytes4TypeID common.TypeID + Bytes32TypeID common.TypeID +} + +func NewEVMSpecialTypeIDs( + gauge common.MemoryGauge, + location common.AddressLocation, +) evmSpecialTypeIDs { + return evmSpecialTypeIDs{ + AddressTypeID: location.TypeID(gauge, stdlib.EVMAddressTypeQualifiedIdentifier), + BytesTypeID: location.TypeID(gauge, stdlib.EVMBytesTypeQualifiedIdentifier), + Bytes4TypeID: location.TypeID(gauge, stdlib.EVMBytes4TypeQualifiedIdentifier), + Bytes32TypeID: location.TypeID(gauge, stdlib.EVMBytes32TypeQualifiedIdentifier), + } +} + func reportABIEncodingComputation( inter *interpreter.Interpreter, locationRange interpreter.LocationRange, values *interpreter.ArrayValue, - evmAddressTypeID common.TypeID, + evmTypeIDs evmSpecialTypeIDs, reportComputation func(intensity uint), ) { + values.Iterate( inter, func(element interpreter.Value) (resume bool) { @@ -95,12 +123,14 @@ func reportABIEncodingComputation( reportComputation(computation) case interpreter.BoolValue, + interpreter.UIntValue, interpreter.UInt8Value, interpreter.UInt16Value, interpreter.UInt32Value, interpreter.UInt64Value, interpreter.UInt128Value, interpreter.UInt256Value, + interpreter.IntValue, interpreter.Int8Value, interpreter.Int16Value, interpreter.Int32Value, @@ -113,15 +143,39 @@ func reportABIEncodingComputation( reportComputation(abiEncodingByteSize) case *interpreter.CompositeValue: - if value.TypeID() == evmAddressTypeID { + switch value.TypeID() { + case evmTypeIDs.AddressTypeID: // EVM addresses are static variables with a fixed // size of 32 bytes. reportComputation(abiEncodingByteSize) - } else { + + case evmTypeIDs.BytesTypeID: + computation := uint(2 * abiEncodingByteSize) + valueMember := value.GetMember(inter, locationRange, stdlib.EVMBytesTypeValueFieldName) + bytesArray, ok := valueMember.(*interpreter.ArrayValue) + if !ok { + panic(abiEncodingError{ + Type: value.StaticType(inter), + Message: "could not convert value field to array", + }) + } + bytesLength := bytesArray.Count() + chunks := math.Ceil(float64(bytesLength) / float64(abiEncodingByteSize)) + computation += uint(chunks * abiEncodingByteSize) + reportComputation(computation) + + case evmTypeIDs.Bytes4TypeID: + reportComputation(abiEncodingByteSize) + + case evmTypeIDs.Bytes32TypeID: + reportComputation(abiEncodingByteSize) + + default: panic(abiEncodingError{ Type: value.StaticType(inter), }) } + case *interpreter.ArrayValue: // Dynamic variables, such as arrays & slices, are encoded // in 2+ chunks of 32 bytes. The first chunk contains @@ -135,7 +189,7 @@ func reportABIEncodingComputation( inter, locationRange, value, - evmAddressTypeID, + evmTypeIDs, reportComputation, ) @@ -158,7 +212,7 @@ func newInternalEVMTypeEncodeABIFunction( location common.AddressLocation, ) *interpreter.HostFunctionValue { - evmAddressTypeID := location.TypeID(gauge, stdlib.EVMAddressTypeQualifiedIdentifier) + evmSpecialTypeIDs := NewEVMSpecialTypeIDs(gauge, location) return interpreter.NewStaticHostFunctionValue( gauge, @@ -178,7 +232,7 @@ func newInternalEVMTypeEncodeABIFunction( inter, locationRange, valuesArray, - evmAddressTypeID, + evmSpecialTypeIDs, func(intensity uint) { inter.ReportComputation(environment.ComputationKindEVMEncodeABI, intensity) }, @@ -197,7 +251,7 @@ func newInternalEVMTypeEncodeABIFunction( locationRange, element, element.StaticType(inter), - evmAddressTypeID, + evmSpecialTypeIDs, ) if err != nil { panic(err) @@ -215,7 +269,11 @@ func newInternalEVMTypeEncodeABIFunction( encodedValues, err := arguments.Pack(values...) if err != nil { - panic(abiEncodingError{}) + panic( + abiEncodingError{ + Message: err.Error(), + }, + ) } return interpreter.ByteSliceToByteArrayValue(inter, encodedValues) @@ -227,6 +285,8 @@ var gethTypeString = gethABI.Type{T: gethABI.StringTy} var gethTypeBool = gethABI.Type{T: gethABI.BoolTy} +var gethTypeUint = gethABI.Type{T: gethABI.UintTy, Size: 256} + var gethTypeUint8 = gethABI.Type{T: gethABI.UintTy, Size: 8} var gethTypeUint16 = gethABI.Type{T: gethABI.UintTy, Size: 16} @@ -239,6 +299,8 @@ var gethTypeUint128 = gethABI.Type{T: gethABI.UintTy, Size: 128} var gethTypeUint256 = gethABI.Type{T: gethABI.UintTy, Size: 256} +var gethTypeInt = gethABI.Type{T: gethABI.IntTy, Size: 256} + var gethTypeInt8 = gethABI.Type{T: gethABI.IntTy, Size: 8} var gethTypeInt16 = gethABI.Type{T: gethABI.IntTy, Size: 16} @@ -251,14 +313,25 @@ var gethTypeInt128 = gethABI.Type{T: gethABI.IntTy, Size: 128} var gethTypeInt256 = gethABI.Type{T: gethABI.IntTy, Size: 256} -var gethTypeAddress = gethABI.Type{Size: 20, T: gethABI.AddressTy} +var gethTypeAddress = gethABI.Type{T: gethABI.AddressTy, Size: 20} + +var gethTypeBytes = gethABI.Type{T: gethABI.BytesTy} + +var gethTypeBytes4 = gethABI.Type{T: gethABI.FixedBytesTy, Size: 4} + +var gethTypeBytes32 = gethABI.Type{T: gethABI.FixedBytesTy, Size: 32} -func gethABIType(staticType interpreter.StaticType, evmAddressTypeID common.TypeID) (gethABI.Type, bool) { +func gethABIType( + staticType interpreter.StaticType, + evmTypeIDs evmSpecialTypeIDs, +) (gethABI.Type, bool) { switch staticType { case interpreter.PrimitiveStaticTypeString: return gethTypeString, true case interpreter.PrimitiveStaticTypeBool: return gethTypeBool, true + case interpreter.PrimitiveStaticTypeUInt: + return gethTypeUint, true case interpreter.PrimitiveStaticTypeUInt8: return gethTypeUint8, true case interpreter.PrimitiveStaticTypeUInt16: @@ -271,6 +344,8 @@ func gethABIType(staticType interpreter.StaticType, evmAddressTypeID common.Type return gethTypeUint128, true case interpreter.PrimitiveStaticTypeUInt256: return gethTypeUint256, true + case interpreter.PrimitiveStaticTypeInt: + return gethTypeInt, true case interpreter.PrimitiveStaticTypeInt8: return gethTypeInt8, true case interpreter.PrimitiveStaticTypeInt16: @@ -289,16 +364,21 @@ func gethABIType(staticType interpreter.StaticType, evmAddressTypeID common.Type switch staticType := staticType.(type) { case *interpreter.CompositeStaticType: - if staticType.TypeID != evmAddressTypeID { - break + switch staticType.TypeID { + case evmTypeIDs.AddressTypeID: + return gethTypeAddress, true + case evmTypeIDs.BytesTypeID: + return gethTypeBytes, true + case evmTypeIDs.Bytes4TypeID: + return gethTypeBytes4, true + case evmTypeIDs.Bytes32TypeID: + return gethTypeBytes32, true } - return gethTypeAddress, true - case *interpreter.ConstantSizedStaticType: elementGethABIType, ok := gethABIType( staticType.ElementType(), - evmAddressTypeID, + evmTypeIDs, ) if !ok { break @@ -313,7 +393,7 @@ func gethABIType(staticType interpreter.StaticType, evmAddressTypeID common.Type case *interpreter.VariableSizedStaticType: elementGethABIType, ok := gethABIType( staticType.ElementType(), - evmAddressTypeID, + evmTypeIDs, ) if !ok { break @@ -331,13 +411,15 @@ func gethABIType(staticType interpreter.StaticType, evmAddressTypeID common.Type func goType( staticType interpreter.StaticType, - evmAddressTypeID common.TypeID, + evmTypeIDs evmSpecialTypeIDs, ) (reflect.Type, bool) { switch staticType { case interpreter.PrimitiveStaticTypeString: return reflect.TypeOf(""), true case interpreter.PrimitiveStaticTypeBool: return reflect.TypeOf(true), true + case interpreter.PrimitiveStaticTypeUInt: + return reflect.TypeOf((*big.Int)(nil)), true case interpreter.PrimitiveStaticTypeUInt8: return reflect.TypeOf(uint8(0)), true case interpreter.PrimitiveStaticTypeUInt16: @@ -350,6 +432,8 @@ func goType( return reflect.TypeOf((*big.Int)(nil)), true case interpreter.PrimitiveStaticTypeUInt256: return reflect.TypeOf((*big.Int)(nil)), true + case interpreter.PrimitiveStaticTypeInt: + return reflect.TypeOf((*big.Int)(nil)), true case interpreter.PrimitiveStaticTypeInt8: return reflect.TypeOf(int8(0)), true case interpreter.PrimitiveStaticTypeInt16: @@ -368,7 +452,7 @@ func goType( switch staticType := staticType.(type) { case *interpreter.ConstantSizedStaticType: - elementType, ok := goType(staticType.ElementType(), evmAddressTypeID) + elementType, ok := goType(staticType.ElementType(), evmTypeIDs) if !ok { break } @@ -376,7 +460,7 @@ func goType( return reflect.ArrayOf(int(staticType.Size), elementType), true case *interpreter.VariableSizedStaticType: - elementType, ok := goType(staticType.ElementType(), evmAddressTypeID) + elementType, ok := goType(staticType.ElementType(), evmTypeIDs) if !ok { break } @@ -384,8 +468,15 @@ func goType( return reflect.SliceOf(elementType), true } - if staticType.ID() == evmAddressTypeID { + switch staticType.ID() { + case evmTypeIDs.AddressTypeID: return reflect.TypeOf(gethCommon.Address{}), true + case evmTypeIDs.BytesTypeID: + return reflect.SliceOf(reflect.TypeOf(byte(0))), true + case evmTypeIDs.Bytes4TypeID: + return reflect.ArrayOf(stdlib.EVMBytes4Length, reflect.TypeOf(byte(0))), true + case evmTypeIDs.Bytes32TypeID: + return reflect.ArrayOf(stdlib.EVMBytes32Length, reflect.TypeOf(byte(0))), true } return nil, false @@ -396,7 +487,7 @@ func encodeABI( locationRange interpreter.LocationRange, value interpreter.Value, staticType interpreter.StaticType, - evmAddressTypeID common.TypeID, + evmTypeIDs evmSpecialTypeIDs, ) ( any, gethABI.Type, @@ -414,6 +505,17 @@ func encodeABI( return bool(value), gethTypeBool, nil } + case interpreter.UIntValue: + if staticType == interpreter.PrimitiveStaticTypeUInt { + if value.BigInt.Cmp(sema.UInt256TypeMaxIntBig) > 0 || value.BigInt.Cmp(sema.UInt256TypeMinIntBig) < 0 { + return nil, gethABI.Type{}, abiEncodingError{ + Type: value.StaticType(inter), + Message: "value outside the boundaries of uint256", + } + } + return value.BigInt, gethTypeUint, nil + } + case interpreter.UInt8Value: if staticType == interpreter.PrimitiveStaticTypeUInt8 { return uint8(value), gethTypeUint8, nil @@ -444,6 +546,17 @@ func encodeABI( return value.BigInt, gethTypeUint256, nil } + case interpreter.IntValue: + if staticType == interpreter.PrimitiveStaticTypeInt { + if value.BigInt.Cmp(sema.Int256TypeMaxIntBig) > 0 || value.BigInt.Cmp(sema.Int256TypeMinIntBig) < 0 { + return nil, gethABI.Type{}, abiEncodingError{ + Type: value.StaticType(inter), + Message: "value outside the boundaries of int256", + } + } + return value.BigInt, gethTypeInt, nil + } + case interpreter.Int8Value: if staticType == interpreter.PrimitiveStaticTypeInt8 { return int8(value), gethTypeInt8, nil @@ -475,7 +588,8 @@ func encodeABI( } case *interpreter.CompositeValue: - if value.TypeID() == evmAddressTypeID { + switch value.TypeID() { + case evmTypeIDs.AddressTypeID: addressBytesArrayValue := value.GetMember(inter, locationRange, stdlib.EVMAddressTypeBytesFieldName) bytes, err := interpreter.ByteArrayValueToByteSlice( inter, @@ -485,21 +599,56 @@ func encodeABI( if err != nil { panic(err) } - return gethCommon.Address(bytes), gethTypeAddress, nil + + case evmTypeIDs.BytesTypeID: + bytesValue := value.GetMember(inter, locationRange, stdlib.EVMBytesTypeValueFieldName) + bytes, err := interpreter.ByteArrayValueToByteSlice( + inter, + bytesValue, + locationRange, + ) + if err != nil { + panic(err) + } + return bytes, gethTypeBytes, nil + + case evmTypeIDs.Bytes4TypeID: + bytesValue := value.GetMember(inter, locationRange, stdlib.EVMBytesTypeValueFieldName) + bytes, err := interpreter.ByteArrayValueToByteSlice( + inter, + bytesValue, + locationRange, + ) + if err != nil { + panic(err) + } + return [stdlib.EVMBytes4Length]byte(bytes), gethTypeBytes4, nil + + case evmTypeIDs.Bytes32TypeID: + bytesValue := value.GetMember(inter, locationRange, stdlib.EVMBytesTypeValueFieldName) + bytes, err := interpreter.ByteArrayValueToByteSlice( + inter, + bytesValue, + locationRange, + ) + if err != nil { + panic(err) + } + return [stdlib.EVMBytes32Length]byte(bytes), gethTypeBytes32, nil } case *interpreter.ArrayValue: arrayStaticType := value.Type - arrayGethABIType, ok := gethABIType(arrayStaticType, evmAddressTypeID) + arrayGethABIType, ok := gethABIType(arrayStaticType, evmTypeIDs) if !ok { break } elementStaticType := arrayStaticType.ElementType() - elementGoType, ok := goType(elementStaticType, evmAddressTypeID) + elementGoType, ok := goType(elementStaticType, evmTypeIDs) if !ok { break } @@ -526,7 +675,7 @@ func encodeABI( locationRange, element, element.StaticType(inter), - evmAddressTypeID, + evmTypeIDs, ) if err != nil { panic(err) @@ -554,7 +703,7 @@ func newInternalEVMTypeDecodeABIFunction( gauge common.MemoryGauge, location common.AddressLocation, ) *interpreter.HostFunctionValue { - evmAddressTypeID := location.TypeID(gauge, stdlib.EVMAddressTypeQualifiedIdentifier) + evmSpecialTypeIDs := NewEVMSpecialTypeIDs(gauge, location) return interpreter.NewStaticHostFunctionValue( gauge, @@ -598,7 +747,7 @@ func newInternalEVMTypeDecodeABIFunction( staticType := typeValue.Type - gethABITy, ok := gethABIType(staticType, evmAddressTypeID) + gethABITy, ok := gethABIType(staticType, evmSpecialTypeIDs) if !ok { panic(abiDecodingError{ Type: staticType, @@ -643,7 +792,7 @@ func newInternalEVMTypeDecodeABIFunction( decodedValues[index], staticType, location, - evmAddressTypeID, + evmSpecialTypeIDs, ) if err != nil { panic(err) @@ -685,7 +834,7 @@ func decodeABI( value any, staticType interpreter.StaticType, location common.AddressLocation, - evmAddressTypeID common.TypeID, + evmTypeIDs evmSpecialTypeIDs, ) ( interpreter.Value, error, @@ -712,6 +861,16 @@ func decodeABI( } return interpreter.BoolValue(value), nil + case interpreter.PrimitiveStaticTypeUInt: + value, ok := value.(*big.Int) + if !ok { + break + } + memoryUsage := common.NewBigIntMemoryUsage( + common.BigIntByteLength(value), + ) + return interpreter.NewUIntValueFromBigInt(inter, memoryUsage, func() *big.Int { return value }), nil + case interpreter.PrimitiveStaticTypeUInt8: value, ok := value.(uint8) if !ok { @@ -754,6 +913,16 @@ func decodeABI( } return interpreter.NewUInt256ValueFromBigInt(inter, func() *big.Int { return value }), nil + case interpreter.PrimitiveStaticTypeInt: + value, ok := value.(*big.Int) + if !ok { + break + } + memoryUsage := common.NewBigIntMemoryUsage( + common.BigIntByteLength(value), + ) + return interpreter.NewIntValueFromBigInt(inter, memoryUsage, func() *big.Int { return value }), nil + case interpreter.PrimitiveStaticTypeInt8: value, ok := value.(int8) if !ok { @@ -824,7 +993,7 @@ func decodeABI( element, elementStaticType, location, - evmAddressTypeID, + evmTypeIDs, ) if err != nil { panic(err) @@ -837,23 +1006,58 @@ func decodeABI( ), nil case *interpreter.CompositeStaticType: - if staticType.TypeID != evmAddressTypeID { - break - } + switch staticType.TypeID { + case evmTypeIDs.AddressTypeID: + addr, ok := value.(gethCommon.Address) + if !ok { + break + } - addr, ok := value.(gethCommon.Address) - if !ok { - break - } + var address types.Address + copy(address[:], addr.Bytes()) + return NewEVMAddress( + inter, + locationRange, + location, + address, + ), nil - var address types.Address - copy(address[:], addr.Bytes()) - return NewEVMAddress( - inter, - locationRange, - location, - address, - ), nil + case evmTypeIDs.BytesTypeID: + bytes, ok := value.([]byte) + if !ok { + break + } + return NewEVMBytes( + inter, + locationRange, + location, + bytes, + ), nil + + case evmTypeIDs.Bytes4TypeID: + bytes, ok := value.([stdlib.EVMBytes4Length]byte) + if !ok { + break + } + return NewEVMBytes4( + inter, + locationRange, + location, + bytes, + ), nil + + case evmTypeIDs.Bytes32TypeID: + bytes, ok := value.([stdlib.EVMBytes32Length]byte) + if !ok { + break + } + return NewEVMBytes32( + inter, + locationRange, + location, + bytes, + ), nil + } } return nil, abiDecodingError{ diff --git a/fvm/evm/impl/impl.go b/fvm/evm/impl/impl.go index 73e3ff8eb49..1412158a976 100644 --- a/fvm/evm/impl/impl.go +++ b/fvm/evm/impl/impl.go @@ -5,10 +5,10 @@ import ( "math/big" "github.com/onflow/cadence" - "github.com/onflow/cadence/runtime/common" - "github.com/onflow/cadence/runtime/errors" - "github.com/onflow/cadence/runtime/interpreter" - "github.com/onflow/cadence/runtime/sema" + "github.com/onflow/cadence/common" + "github.com/onflow/cadence/errors" + "github.com/onflow/cadence/interpreter" + "github.com/onflow/cadence/sema" "github.com/onflow/flow-go/fvm/evm/stdlib" "github.com/onflow/flow-go/fvm/evm/types" @@ -150,6 +150,72 @@ func NewEVMAddress( ) } +func NewEVMBytes( + inter *interpreter.Interpreter, + locationRange interpreter.LocationRange, + location common.AddressLocation, + bytes []byte, +) *interpreter.CompositeValue { + return interpreter.NewCompositeValue( + inter, + locationRange, + location, + stdlib.EVMBytesTypeQualifiedIdentifier, + common.CompositeKindStructure, + []interpreter.CompositeField{ + { + Name: stdlib.EVMBytesTypeValueFieldName, + Value: EVMBytesToBytesArrayValue(inter, bytes), + }, + }, + common.ZeroAddress, + ) +} + +func NewEVMBytes4( + inter *interpreter.Interpreter, + locationRange interpreter.LocationRange, + location common.AddressLocation, + bytes [4]byte, +) *interpreter.CompositeValue { + return interpreter.NewCompositeValue( + inter, + locationRange, + location, + stdlib.EVMBytes4TypeQualifiedIdentifier, + common.CompositeKindStructure, + []interpreter.CompositeField{ + { + Name: stdlib.EVMBytesTypeValueFieldName, + Value: EVMBytes4ToBytesArrayValue(inter, bytes), + }, + }, + common.ZeroAddress, + ) +} + +func NewEVMBytes32( + inter *interpreter.Interpreter, + locationRange interpreter.LocationRange, + location common.AddressLocation, + bytes [32]byte, +) *interpreter.CompositeValue { + return interpreter.NewCompositeValue( + inter, + locationRange, + location, + stdlib.EVMBytes32TypeQualifiedIdentifier, + common.CompositeKindStructure, + []interpreter.CompositeField{ + { + Name: stdlib.EVMBytesTypeValueFieldName, + Value: EVMBytes32ToBytesArrayValue(inter, bytes), + }, + }, + common.ZeroAddress, + ) +} + func AddressBytesArrayValueToEVMAddress( inter *interpreter.Interpreter, locationRange interpreter.LocationRange, @@ -210,6 +276,75 @@ func EVMAddressToAddressBytesArrayValue( ) } +func EVMBytesToBytesArrayValue( + inter *interpreter.Interpreter, + bytes []byte, +) *interpreter.ArrayValue { + var index int + return interpreter.NewArrayValueWithIterator( + inter, + stdlib.EVMBytesValueStaticType, + common.ZeroAddress, + uint64(len(bytes)), + func() interpreter.Value { + if index >= len(bytes) { + return nil + } + result := interpreter.NewUInt8Value(inter, func() uint8 { + return bytes[index] + }) + index++ + return result + }, + ) +} + +func EVMBytes4ToBytesArrayValue( + inter *interpreter.Interpreter, + bytes [4]byte, +) *interpreter.ArrayValue { + var index int + return interpreter.NewArrayValueWithIterator( + inter, + stdlib.EVMBytes4ValueStaticType, + common.ZeroAddress, + stdlib.EVMBytes4Length, + func() interpreter.Value { + if index >= stdlib.EVMBytes4Length { + return nil + } + result := interpreter.NewUInt8Value(inter, func() uint8 { + return bytes[index] + }) + index++ + return result + }, + ) +} + +func EVMBytes32ToBytesArrayValue( + inter *interpreter.Interpreter, + bytes [32]byte, +) *interpreter.ArrayValue { + var index int + return interpreter.NewArrayValueWithIterator( + inter, + stdlib.EVMBytes32ValueStaticType, + common.ZeroAddress, + stdlib.EVMBytes32Length, + func() interpreter.Value { + if index >= stdlib.EVMBytes32Length { + return nil + } + result := interpreter.NewUInt8Value(inter, func() uint8 { + return bytes[index] + }) + index++ + return result + }, + ) +} + func newInternalEVMTypeCreateCadenceOwnedAccountFunction( gauge common.MemoryGauge, handler types.ContractHandler, diff --git a/fvm/evm/offchain/blocks/blocks.go b/fvm/evm/offchain/blocks/blocks.go new file mode 100644 index 00000000000..7852d0bd90c --- /dev/null +++ b/fvm/evm/offchain/blocks/blocks.go @@ -0,0 +1,149 @@ +package blocks + +import ( + "fmt" + + gethCommon "github.com/onflow/go-ethereum/common" + + "github.com/onflow/flow-go/fvm/evm/handler" + "github.com/onflow/flow-go/fvm/evm/types" + "github.com/onflow/flow-go/model/flow" +) + +const BlockStoreLatestBlockMetaKey = "LatestBlockMeta" + +// Blocks facilitates access to the recent block hash values +// and also the latest executed block meta data +type Blocks struct { + chainID flow.ChainID + storage types.BackendStorage + rootAddress flow.Address + bhl *handler.BlockHashList +} + +var _ types.BlockSnapshot = (*Blocks)(nil) + +// NewBlocks constructs a new blocks type +func NewBlocks( + chainID flow.ChainID, + rootAddress flow.Address, + storage types.BackendStorage, +) (*Blocks, error) { + var err error + blocks := &Blocks{ + chainID: chainID, + storage: storage, + rootAddress: rootAddress, + } + blocks.bhl, err = handler.NewBlockHashList( + storage, + rootAddress, + handler.BlockHashListCapacity, + ) + if err != nil { + return nil, err + } + // if empty insert genesis block hash + if blocks.bhl.IsEmpty() { + genesis := types.GenesisBlock(chainID) + err = blocks.PushBlockMeta( + NewMeta( + genesis.Height, + genesis.Timestamp, + genesis.PrevRandao, + )) + if err != nil { + return nil, err + } + // push block hash + err = blocks.PushBlockHash( + genesis.Height, + types.GenesisBlockHash(chainID)) + if err != nil { + return nil, err + } + } + return blocks, nil +} + +// PushBlock pushes a new block into the storage +func (b *Blocks) PushBlockMeta( + meta *Meta, +) error { + // check height order + if meta.Height > 0 { + bm, err := b.LatestBlock() + if err != nil { + return err + } + if meta.Height != bm.Height+1 { + return fmt.Errorf("out of order block meta push! got: %d, expected %d ", meta.Height, bm.Height+1) + } + } + return b.storeBlockMetaData(meta) +} + +// PushBlockHash pushes a new block block hash into the storage +func (b *Blocks) PushBlockHash( + height uint64, + hash gethCommon.Hash, +) error { + return b.bhl.Push(height, hash) +} + +func (b *Blocks) LatestBlock() (*Meta, error) { + return b.loadBlockMetaData() +} + +// BlockHash returns the block hash for the given height +func (b *Blocks) BlockHash(height uint64) (gethCommon.Hash, error) { + _, hash, err := b.bhl.BlockHashByHeight(height) + return hash, err +} + +// BlockContext constructs a block context for the latest block +func (b *Blocks) BlockContext() (types.BlockContext, error) { + bm, err := b.LatestBlock() + if err != nil { + return types.BlockContext{}, err + } + + return types.BlockContext{ + ChainID: types.EVMChainIDFromFlowChainID(b.chainID), + BlockNumber: bm.Height, + BlockTimestamp: bm.Timestamp, + DirectCallBaseGasUsage: types.DefaultDirectCallBaseGasUsage, + DirectCallGasPrice: types.DefaultDirectCallGasPrice, + GasFeeCollector: types.CoinbaseAddress, + GetHashFunc: func(n uint64) gethCommon.Hash { + hash, err := b.BlockHash(n) + if err != nil { + panic(err) + } + return hash + }, + Random: bm.Random, + }, nil +} + +// storeBlockMetaData stores the block meta data into storage +func (b *Blocks) storeBlockMetaData(bm *Meta) error { + // store the encoded data into backend + return b.storage.SetValue( + b.rootAddress[:], + []byte(BlockStoreLatestBlockMetaKey), + bm.Encode(), + ) +} + +// loadBlockMetaData loads the block meta data from the storage +func (b *Blocks) loadBlockMetaData() (*Meta, error) { + data, err := b.storage.GetValue( + b.rootAddress[:], + []byte(BlockStoreLatestBlockMetaKey), + ) + if err != nil { + return nil, err + } + return MetaFromEncoded(data) +} diff --git a/fvm/evm/offchain/blocks/blocks_test.go b/fvm/evm/offchain/blocks/blocks_test.go new file mode 100644 index 00000000000..a5268ca66b6 --- /dev/null +++ b/fvm/evm/offchain/blocks/blocks_test.go @@ -0,0 +1,55 @@ +package blocks_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/onflow/flow-go/fvm/evm/offchain/blocks" + "github.com/onflow/flow-go/fvm/evm/testutils" + "github.com/onflow/flow-go/fvm/evm/types" + "github.com/onflow/flow-go/model/flow" +) + +func TestBlocks(t *testing.T) { + storage := testutils.GetSimpleValueStore() + chainID := flow.Emulator.Chain().ChainID() + rootAddr := flow.Address{1, 2, 3, 4} + blks, err := blocks.NewBlocks(chainID, rootAddr, storage) + require.NoError(t, err) + + // no insertion - genesis block + bm, err := blks.LatestBlock() + require.NoError(t, err) + genesis := types.GenesisBlock(chainID) + require.Equal(t, genesis.Height, bm.Height) + require.Equal(t, genesis.Timestamp, bm.Timestamp) + require.Equal(t, genesis.PrevRandao, bm.Random) + + h, err := blks.BlockHash(0) + require.NoError(t, err) + expectedHash, err := genesis.Hash() + require.NoError(t, err) + require.Equal(t, expectedHash, h) + + // push next block + height := uint64(1) + timestamp := uint64(2) + random := testutils.RandomCommonHash(t) + hash := testutils.RandomCommonHash(t) + + err = blks.PushBlockMeta(blocks.NewMeta(height, timestamp, random)) + require.NoError(t, err) + err = blks.PushBlockHash(height, hash) + require.NoError(t, err) + + // check values + h, err = blks.BlockHash(1) + require.NoError(t, err) + require.Equal(t, hash, h) + bm, err = blks.LatestBlock() + require.NoError(t, err) + require.Equal(t, height, bm.Height) + require.Equal(t, timestamp, bm.Timestamp) + require.Equal(t, random, bm.Random) +} diff --git a/fvm/evm/offchain/blocks/meta.go b/fvm/evm/offchain/blocks/meta.go new file mode 100644 index 00000000000..c47ed71fdbc --- /dev/null +++ b/fvm/evm/offchain/blocks/meta.go @@ -0,0 +1,81 @@ +package blocks + +import ( + "encoding/binary" + "fmt" + + gethCommon "github.com/onflow/go-ethereum/common" +) + +const ( + heightEncodingSize = 8 + timestampEncodingSize = 8 + randomEncodingSize = 32 + metaEncodingSize = heightEncodingSize + + timestampEncodingSize + + randomEncodingSize +) + +// Meta holds meta data about a block +type Meta struct { + Height uint64 + Timestamp uint64 + Random gethCommon.Hash +} + +// NewBlockMeta constructs a new block meta +func NewMeta( + height uint64, + timestamp uint64, + random gethCommon.Hash, +) *Meta { + return &Meta{ + Height: height, + Timestamp: timestamp, + Random: random, + } +} + +// Encode encodes a meta +func (bm *Meta) Encode() []byte { + // encode meta data + buffer := make([]byte, metaEncodingSize) + pos := 0 + + // encode height + binary.BigEndian.PutUint64(buffer[pos:], uint64(bm.Height)) + pos += heightEncodingSize + + // encode timestamp + binary.BigEndian.PutUint64(buffer[pos:], uint64(bm.Timestamp)) + pos += timestampEncodingSize + + // encode random + copy(buffer[pos:pos+randomEncodingSize], bm.Random[:]) + + return buffer +} + +// MetaFromEncoded constructs a Meta from encoded data +func MetaFromEncoded(data []byte) (*Meta, error) { + // check the data size + if len(data) < metaEncodingSize { + return nil, fmt.Errorf("encoded input too short: %d < %d", len(data), metaEncodingSize) + } + + bm := &Meta{} + + pos := 0 + // decode height + bm.Height = binary.BigEndian.Uint64(data[pos:]) + pos += heightEncodingSize + + // decode timestamp + bm.Timestamp = binary.BigEndian.Uint64(data[pos:]) + pos += timestampEncodingSize + + // decode random + bm.Random = gethCommon.BytesToHash(data[pos:]) + + return bm, nil +} diff --git a/fvm/evm/offchain/blocks/meta_test.go b/fvm/evm/offchain/blocks/meta_test.go new file mode 100644 index 00000000000..a777200533d --- /dev/null +++ b/fvm/evm/offchain/blocks/meta_test.go @@ -0,0 +1,18 @@ +package blocks_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/onflow/flow-go/fvm/evm/offchain/blocks" + "github.com/onflow/flow-go/fvm/evm/testutils" +) + +func TestBlockMetaEncodingDecoding(t *testing.T) { + bm := blocks.NewMeta(1, 2, testutils.RandomCommonHash(t)) + + ret, err := blocks.MetaFromEncoded(bm.Encode()) + require.NoError(t, err) + require.Equal(t, ret, bm) +} diff --git a/fvm/evm/offchain/blocks/provider.go b/fvm/evm/offchain/blocks/provider.go new file mode 100644 index 00000000000..9111be4ac64 --- /dev/null +++ b/fvm/evm/offchain/blocks/provider.go @@ -0,0 +1,74 @@ +package blocks + +import ( + "fmt" + + "github.com/onflow/flow-go/fvm/evm/events" + "github.com/onflow/flow-go/fvm/evm/types" + "github.com/onflow/flow-go/model/flow" +) + +// BasicProvider implements a ledger-backed basic block snapshot provider +// it assumes sequential progress on blocks and expects a +// a OnBlockReceived call before block execution and +// a follow up OnBlockExecuted call after block execution. +type BasicProvider struct { + blks *Blocks + latestBlockPayload *events.BlockEventPayload +} + +var _ types.BlockSnapshotProvider = (*BasicProvider)(nil) + +func NewBasicProvider( + chainID flow.ChainID, + storage types.BackendStorage, + rootAddr flow.Address, +) (*BasicProvider, error) { + blks, err := NewBlocks(chainID, rootAddr, storage) + if err != nil { + return nil, err + } + return &BasicProvider{blks: blks}, nil +} + +// GetSnapshotAt returns a block snapshot at the given height +// Snapshot at a height is not available until `OnBlockReceived` is called for that height. +func (p *BasicProvider) GetSnapshotAt(height uint64) ( + types.BlockSnapshot, + error, +) { + if p.latestBlockPayload.Height != height { + return nil, fmt.Errorf("active block height doesn't match expected: %d, got: %d", p.latestBlockPayload.Height, height) + } + return p.blks, nil +} + +// OnBlockReceived should be called before executing blocks. +func (p *BasicProvider) OnBlockReceived(blockEvent *events.BlockEventPayload) error { + p.latestBlockPayload = blockEvent + // push the new block meta + // it should be done before execution so block context creation + // can be done properly + return p.blks.PushBlockMeta( + NewMeta( + blockEvent.Height, + blockEvent.Timestamp, + blockEvent.PrevRandao, + ), + ) +} + +// OnBlockExecuted should be called after executing blocks. +func (p *BasicProvider) OnBlockExecuted( + height uint64, + resCol types.ReplayResultCollector) error { + // we push the block hash after execution, so the behaviour of the blockhash is + // identical to the evm.handler. + if p.latestBlockPayload.Height != height { + return fmt.Errorf("active block height doesn't match expected: %d, got: %d", p.latestBlockPayload.Height, height) + } + return p.blks.PushBlockHash( + p.latestBlockPayload.Height, + p.latestBlockPayload.Hash, + ) +} diff --git a/fvm/evm/offchain/query/view.go b/fvm/evm/offchain/query/view.go new file mode 100644 index 00000000000..bb1647ec657 --- /dev/null +++ b/fvm/evm/offchain/query/view.go @@ -0,0 +1,346 @@ +package query + +import ( + "errors" + "fmt" + "math/big" + + "github.com/holiman/uint256" + gethCommon "github.com/onflow/go-ethereum/common" + gethTypes "github.com/onflow/go-ethereum/core/types" + gethCrypto "github.com/onflow/go-ethereum/crypto" + gethTracers "github.com/onflow/go-ethereum/eth/tracers" + + "github.com/onflow/flow-go/fvm/evm/emulator" + "github.com/onflow/flow-go/fvm/evm/emulator/state" + "github.com/onflow/flow-go/fvm/evm/offchain/storage" + "github.com/onflow/flow-go/fvm/evm/types" + "github.com/onflow/flow-go/model/flow" +) + +// View provides query capabilities over +// an specific state of the EVM chain. +type View struct { + chainID flow.ChainID + rootAddr flow.Address + storage *storage.EphemeralStorage + blockSnapshot types.BlockSnapshot + tracer *gethTracers.Tracer + extraPCs []types.PrecompiledContract + maxCallGasLimit uint64 +} + +// NewView constructs a new view. +func NewView( + chainID flow.ChainID, + rootAddr flow.Address, + storage *storage.EphemeralStorage, + blockSnapshot types.BlockSnapshot, + maxCallGasLimit uint64, +) *View { + return &View{ + chainID: chainID, + rootAddr: rootAddr, + storage: storage, + blockSnapshot: blockSnapshot, + maxCallGasLimit: maxCallGasLimit, + } +} + +// GetBalance returns the balance for the given address +// can be used for the `eth_getBalance` endpoint +func (v *View) GetBalance(addr gethCommon.Address) (*big.Int, error) { + bv, err := state.NewBaseView(v.storage, v.rootAddr) + if err != nil { + return nil, err + } + bal, err := bv.GetBalance(addr) + if err != nil { + return nil, err + } + return bal.ToBig(), nil +} + +// GetNonce returns the nonce for the given address +// can be used for the `eth_getTransactionCount` endpoint +func (v *View) GetNonce(addr gethCommon.Address) (uint64, error) { + bv, err := state.NewBaseView(v.storage, v.rootAddr) + if err != nil { + return 0, err + } + return bv.GetNonce(addr) +} + +// GetCode returns the code for the given address +// can be used for the `eth_getCode` endpoint +func (v *View) GetCode(addr gethCommon.Address) ([]byte, error) { + bv, err := state.NewBaseView(v.storage, v.rootAddr) + if err != nil { + return nil, err + } + return bv.GetCode(addr) +} + +// GetCodeHash returns the codehash for the given address +func (v *View) GetCodeHash(addr gethCommon.Address) (gethCommon.Hash, error) { + bv, err := state.NewBaseView(v.storage, v.rootAddr) + if err != nil { + return gethCommon.Hash{}, err + } + return bv.GetCodeHash(addr) +} + +// GetSlab returns the slab for the given address and key +// can be used for the `eth_getStorageAt` endpoint +func (v *View) GetSlab(addr gethCommon.Address, key gethCommon.Hash) (gethCommon.Hash, error) { + bv, err := state.NewBaseView(v.storage, v.rootAddr) + if err != nil { + return gethCommon.Hash{}, err + } + return bv.GetState(types.SlotAddress{ + Address: addr, + Key: key, + }) +} + +// DryCall runs a call off-chain and returns the results +// accepts override storage and precompiled call options +// as well as custom tracer. +func (v *View) DryCall( + from gethCommon.Address, + to gethCommon.Address, + data []byte, + value *big.Int, + gasLimit uint64, + opts ...DryCallOption, +) (*types.Result, error) { + + if gasLimit > v.maxCallGasLimit { + return nil, fmt.Errorf( + "gas limit is bigger than max gas limit allowed %d > %d", + gasLimit, v.maxCallGasLimit, + ) + } + + // apply all the options + for _, op := range opts { + err := op(v) + if err != nil { + return nil, err + } + } + + // create context + ctx, err := v.blockSnapshot.BlockContext() + if err != nil { + return nil, err + } + ctx.Tracer = v.tracer + ctx.ExtraPrecompiledContracts = v.extraPCs + + // create emulator + em := emulator.NewEmulator(v.storage, v.rootAddr) + + // create a new block view + bv, err := em.NewBlockView(ctx) + if err != nil { + return nil, err + } + + res, err := bv.DirectCall( + &types.DirectCall{ + From: types.NewAddress(from), + To: types.NewAddress(to), + Data: data, + Value: value, + GasLimit: gasLimit, + }, + ) + if err != nil { + return nil, err + } + + return res, nil +} + +// DryRunOption captures a options +// to be applied before the execution of a dry call. +type DryCallOption func(v *View) error + +// WithStateOverrideBalance constructs a dry call option +// that replaces the balance of an address before the execution a dry call. +func WithStateOverrideBalance( + addr gethCommon.Address, + balance *big.Int, +) DryCallOption { + return func(v *View) error { + baseView, err := state.NewBaseView(v.storage, v.rootAddr) + if err != nil { + return err + } + nonce, err := baseView.GetNonce(addr) + if err != nil { + return err + } + code, err := baseView.GetCode(addr) + if err != nil { + return err + } + codeHash, err := baseView.GetCodeHash(addr) + if err != nil { + return err + } + + convertedBalance, overflow := uint256.FromBig(balance) + if overflow { + return errors.New("balance too large") + } + + err = baseView.UpdateAccount(addr, convertedBalance, nonce, code, codeHash) + if err != nil { + return err + } + return baseView.Commit() + } +} + +// WithStateOverrideNonce constructs a dry call option +// that replaces the nonce of an address before the execution a dry call. +func WithStateOverrideNonce( + addr gethCommon.Address, + nonce uint64, +) DryCallOption { + return func(v *View) error { + baseView, err := state.NewBaseView(v.storage, v.rootAddr) + if err != nil { + return err + } + balance, err := baseView.GetBalance(addr) + if err != nil { + return err + } + code, err := baseView.GetCode(addr) + if err != nil { + return err + } + codeHash, err := baseView.GetCodeHash(addr) + if err != nil { + return err + } + err = baseView.UpdateAccount(addr, balance, nonce, code, codeHash) + if err != nil { + return err + } + return baseView.Commit() + } +} + +// WithStateOverrideCode constructs a dry call option +// that replaces the code of an address before the dry call. +func WithStateOverrideCode( + addr gethCommon.Address, + code []byte, +) DryCallOption { + return func(v *View) error { + baseView, err := state.NewBaseView(v.storage, v.rootAddr) + if err != nil { + return err + } + balance, err := baseView.GetBalance(addr) + if err != nil { + return err + } + nonce, err := baseView.GetNonce(addr) + if err != nil { + return err + } + codeHash := gethTypes.EmptyCodeHash + if len(code) > 0 { + codeHash = gethCrypto.Keccak256Hash(code) + } + err = baseView.UpdateAccount(addr, balance, nonce, code, codeHash) + if err != nil { + return err + } + return baseView.Commit() + } +} + +// WithStateOverrideState constructs a dry call option +// that overrides all slots in the account storage before executing the call. +func WithStateOverrideState( + addr gethCommon.Address, + slots map[gethCommon.Hash]gethCommon.Hash, +) DryCallOption { + return func(v *View) error { + baseView, err := state.NewBaseView(v.storage, v.rootAddr) + if err != nil { + return err + } + // purge all the slots + err = baseView.PurgeAllSlotsOfAnAccount(addr) + if err != nil { + return err + } + // no need to be sorted this is off-chain operation + for k, v := range slots { + err = baseView.UpdateSlot(types.SlotAddress{ + Address: addr, + Key: k, + }, v) + if err != nil { + return err + } + } + return baseView.Commit() + } +} + +// WithStateOverrideStateDiff constructs a dry call option +// that overrides slots of an account before executing the call. +func WithStateOverrideStateDiff( + addr gethCommon.Address, + slots map[gethCommon.Hash]gethCommon.Hash, +) DryCallOption { + return func(v *View) error { + baseView, err := state.NewBaseView(v.storage, v.rootAddr) + if err != nil { + return err + } + // no need to be sorted this is off-chain operation + for k, v := range slots { + err = baseView.UpdateSlot(types.SlotAddress{ + Address: addr, + Key: k, + }, v) + if err != nil { + return err + } + } + return baseView.Commit() + } +} + +// WithTracer constructs a dry call option +// that allows running the dry call with the +// custom tracer. +func WithTracer( + tracer *gethTracers.Tracer, +) DryCallOption { + return func(v *View) error { + v.tracer = tracer + return nil + } +} + +// WithExtraPrecompiledContracts constructs a dry call option +// that allows adding the precompiled contracts +// while executing the dry-call. +// +// this method can be used with remote PC caller for cadence arch calls +func WithExtraPrecompiledContracts(pcs []types.PrecompiledContract) DryCallOption { + return func(v *View) error { + v.extraPCs = pcs + return nil + } +} diff --git a/fvm/evm/offchain/query/viewProvider.go b/fvm/evm/offchain/query/viewProvider.go new file mode 100644 index 00000000000..b4e258cb9a3 --- /dev/null +++ b/fvm/evm/offchain/query/viewProvider.go @@ -0,0 +1,55 @@ +package query + +import ( + "github.com/onflow/flow-go/fvm/evm/offchain/storage" + "github.com/onflow/flow-go/fvm/evm/types" + "github.com/onflow/flow-go/model/flow" +) + +// ViewProvider constructs views +// based on the requirements +type ViewProvider struct { + chainID flow.ChainID + rootAddr flow.Address + storageProvider types.StorageProvider + blockProvider types.BlockSnapshotProvider + maxCallGasLimit uint64 +} + +// NewViewProvider constructs a new ViewProvider +func NewViewProvider( + chainID flow.ChainID, + rootAddr flow.Address, + sp types.StorageProvider, + bp types.BlockSnapshotProvider, + maxCallGasLimit uint64, +) *ViewProvider { + return &ViewProvider{ + chainID: chainID, + storageProvider: sp, + blockProvider: bp, + rootAddr: rootAddr, + maxCallGasLimit: maxCallGasLimit, + } +} + +// GetBlockView returns the block view for the given height +func (evp *ViewProvider) GetBlockView(height uint64) (*View, error) { + readOnly, err := evp.storageProvider.GetSnapshotAt(height) + if err != nil { + return nil, err + } + blockSnapshot, err := evp.blockProvider.GetSnapshotAt(height) + if err != nil { + return nil, err + } + return &View{ + chainID: evp.chainID, + rootAddr: evp.rootAddr, + maxCallGasLimit: evp.maxCallGasLimit, + storage: storage.NewEphemeralStorage( + storage.NewReadOnlyStorage(readOnly), + ), + blockSnapshot: blockSnapshot, + }, nil +} diff --git a/fvm/evm/offchain/query/view_test.go b/fvm/evm/offchain/query/view_test.go new file mode 100644 index 00000000000..22aa9c4d99c --- /dev/null +++ b/fvm/evm/offchain/query/view_test.go @@ -0,0 +1,135 @@ +package query_test + +import ( + "bytes" + "math/big" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/onflow/flow-go/fvm/evm/handler" + "github.com/onflow/flow-go/fvm/evm/offchain/blocks" + "github.com/onflow/flow-go/fvm/evm/offchain/query" + "github.com/onflow/flow-go/fvm/evm/offchain/storage" + "github.com/onflow/flow-go/fvm/evm/precompiles" + . "github.com/onflow/flow-go/fvm/evm/testutils" + "github.com/onflow/flow-go/fvm/evm/types" + "github.com/onflow/flow-go/model/flow" +) + +func TestView(t *testing.T) { + + const chainID = flow.Emulator + RunWithTestBackend(t, func(backend *TestBackend) { + RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { + RunWithDeployedContract(t, + GetStorageTestContract(t), backend, rootAddr, func(testContract *TestContract) { + RunWithEOATestAccount(t, backend, rootAddr, func(testAccount *EOATestAccount) { + + h := SetupHandler(chainID, backend, rootAddr) + + blks, err := blocks.NewBlocks(chainID, rootAddr, backend) + require.NoError(t, err) + + maxCallGasLimit := uint64(5_000_000) + view := query.NewView( + chainID, + rootAddr, + storage.NewEphemeralStorage( + backend, + ), + blks, + maxCallGasLimit, + ) + + // test make balance query + expectedBalance := h.AccountByAddress(testAccount.Address(), false).Balance() + bal, err := view.GetBalance(testAccount.Address().ToCommon()) + require.NoError(t, err) + require.True(t, types.BalancesAreEqual(expectedBalance, bal)) + + // test make nonce query + expectedNonce := h.AccountByAddress(testAccount.Address(), false).Nonce() + nonce, err := view.GetNonce(testAccount.Address().ToCommon()) + require.NoError(t, err) + require.Equal(t, expectedNonce, nonce) + + // test make code query + expectedCode := h.AccountByAddress(testContract.DeployedAt, false).Code() + code, err := view.GetCode(testContract.DeployedAt.ToCommon()) + require.NoError(t, err) + require.True(t, bytes.Equal(expectedCode[:], code[:])) + + // test make code hash query + expectedCodeHash := h.AccountByAddress(testContract.DeployedAt, false).CodeHash() + codeHash, err := view.GetCodeHash(testContract.DeployedAt.ToCommon()) + require.NoError(t, err) + require.True(t, bytes.Equal(expectedCodeHash[:], codeHash[:])) + + // test dry call + // make call to test contract - set + expectedFlowHeight := uint64(3) + pc := &TestPrecompiledContract{ + RequiredGasFunc: func(input []byte) uint64 { + return 1 + }, + RunFunc: func(input []byte) ([]byte, error) { + output := make([]byte, 32) + err := precompiles.EncodeUint64(expectedFlowHeight, output, 0) + return output, err + }, + AddressFunc: func() types.Address { + // cadence arch address + return handler.NewAddressAllocator().AllocatePrecompileAddress(1) + }, + } + res, err := view.DryCall( + testAccount.Address().ToCommon(), + testContract.DeployedAt.ToCommon(), + testContract.MakeCallData(t, "verifyArchCallToFlowBlockHeight", expectedFlowHeight), + big.NewInt(0), + uint64(1_000_000), + query.WithExtraPrecompiledContracts( + []types.PrecompiledContract{pc}), + ) + require.NoError(t, err) + require.NoError(t, res.ValidationError) + require.NoError(t, res.VMError) + + // test dry call with overrides + newBalance := big.NewInt(3000) + res, err = view.DryCall( + testAccount.Address().ToCommon(), + testContract.DeployedAt.ToCommon(), + testContract.MakeCallData(t, + "checkBalance", + testAccount.Address().ToCommon(), + newBalance), + big.NewInt(0), + uint64(1_000_000), + query.WithStateOverrideBalance( + testAccount.Address().ToCommon(), + newBalance, + ), + ) + require.NoError(t, err) + require.NoError(t, res.ValidationError) + require.NoError(t, res.VMError) + + // test max gas limit + _, err = view.DryCall( + testAccount.Address().ToCommon(), + testContract.DeployedAt.ToCommon(), + testContract.MakeCallData(t, + "store", + big.NewInt(2)), + big.NewInt(0), + maxCallGasLimit+1, + ) + require.Error(t, err) + + }) + }) + }) + }) +} diff --git a/fvm/evm/offchain/storage/ephemeral.go b/fvm/evm/offchain/storage/ephemeral.go new file mode 100644 index 00000000000..a1687460526 --- /dev/null +++ b/fvm/evm/offchain/storage/ephemeral.go @@ -0,0 +1,94 @@ +package storage + +import ( + "fmt" + + "github.com/onflow/atree" + + "github.com/onflow/flow-go/fvm/environment" + "github.com/onflow/flow-go/fvm/evm/types" + "github.com/onflow/flow-go/model/flow" +) + +// EphemeralStorage holds on to register changes instead of applying them directly to +// the provided backend storage. It can be used for dry running transaction/calls +// or batching updates for atomic operations. +type EphemeralStorage struct { + parent types.BackendStorage + deltas map[flow.RegisterID]flow.RegisterValue +} + +// NewEphemeralStorage constructs a new EphemeralStorage +func NewEphemeralStorage(parent types.BackendStorage) *EphemeralStorage { + return &EphemeralStorage{ + parent: parent, + deltas: make(map[flow.RegisterID]flow.RegisterValue), + } +} + +var _ types.BackendStorage = (*EphemeralStorage)(nil) + +var _ types.ReplayResultCollector = (*EphemeralStorage)(nil) + +// GetValue reads a register value +func (s *EphemeralStorage) GetValue(owner []byte, key []byte) ([]byte, error) { + // check delta first + ret, found := s.deltas[RegisterID(owner, key)] + if found { + return ret, nil + } + return s.parent.GetValue(owner, key) +} + +// SetValue sets a register value +func (s *EphemeralStorage) SetValue(owner, key, value []byte) error { + s.deltas[RegisterID(owner, key)] = value + return nil +} + +// ValueExists checks if a register exists +func (s *EphemeralStorage) ValueExists(owner []byte, key []byte) (bool, error) { + ret, err := s.GetValue(owner, key) + return len(ret) > 0, err +} + +// AllocateSlabIndex allocates an slab index based on the given owner +func (s *EphemeralStorage) AllocateSlabIndex(owner []byte) (atree.SlabIndex, error) { + statusBytes, err := s.GetValue(owner, []byte(flow.AccountStatusKey)) + if err != nil { + return atree.SlabIndex{}, err + } + if len(statusBytes) == 0 { + return atree.SlabIndex{}, fmt.Errorf("state for account not found") + } + + status, err := environment.AccountStatusFromBytes(statusBytes) + if err != nil { + return atree.SlabIndex{}, err + } + + // get and increment the index + index := status.SlabIndex() + newIndexBytes := index.Next() + + // update the storageIndex bytes + status.SetStorageIndex(newIndexBytes) + err = s.SetValue(owner, []byte(flow.AccountStatusKey), status.ToBytes()) + if err != nil { + return atree.SlabIndex{}, err + } + return index, nil +} + +// StorageRegisterUpdates returns a map of register updates +func (s *EphemeralStorage) StorageRegisterUpdates() map[flow.RegisterID]flow.RegisterValue { + return s.deltas +} + +// RegisterID creates a RegisterID from owner and key +func RegisterID(owner []byte, key []byte) flow.RegisterID { + return flow.RegisterID{ + Owner: string(owner), + Key: string(key), + } +} diff --git a/fvm/evm/offchain/storage/ephemeral_test.go b/fvm/evm/offchain/storage/ephemeral_test.go new file mode 100644 index 00000000000..52a029737a0 --- /dev/null +++ b/fvm/evm/offchain/storage/ephemeral_test.go @@ -0,0 +1,71 @@ +package storage_test + +import ( + "testing" + + "github.com/onflow/atree" + "github.com/stretchr/testify/require" + + "github.com/onflow/flow-go/fvm/environment" + "github.com/onflow/flow-go/fvm/evm/offchain/storage" + "github.com/onflow/flow-go/fvm/evm/testutils" + "github.com/onflow/flow-go/model/flow" +) + +func TestEphemeralStorage(t *testing.T) { + + parent := testutils.GetSimpleValueStore() + // preset value + owner := []byte("owner") + key1 := []byte("key1") + value1 := []byte{1} + value2 := []byte{2} + err := parent.SetValue(owner, key1, value1) + require.NoError(t, err) + + s := storage.NewEphemeralStorage(parent) + ret, err := s.GetValue(owner, key1) + require.NoError(t, err) + require.Equal(t, value1, ret) + found, err := s.ValueExists(owner, key1) + require.NoError(t, err) + require.True(t, found) + + // test set value + err = s.SetValue(owner, key1, value2) + require.NoError(t, err) + ret, err = s.GetValue(owner, key1) + require.NoError(t, err) + require.Equal(t, value2, ret) + // the parent should still return the value1 + ret, err = parent.GetValue(owner, key1) + require.NoError(t, err) + require.Equal(t, value1, ret) + + // test allocate slab id + _, err = s.AllocateSlabIndex(owner) + require.Error(t, err) + + // setup account + err = s.SetValue(owner, []byte(flow.AccountStatusKey), environment.NewAccountStatus().ToBytes()) + require.NoError(t, err) + + sid, err := s.AllocateSlabIndex(owner) + require.NoError(t, err) + expected := atree.SlabIndex([8]byte{0, 0, 0, 0, 0, 0, 0, 1}) + require.Equal(t, expected, sid) + + sid, err = s.AllocateSlabIndex(owner) + require.NoError(t, err) + expected = atree.SlabIndex([8]byte{0, 0, 0, 0, 0, 0, 0, 2}) + require.Equal(t, expected, sid) + + // fetch delta + delta := s.StorageRegisterUpdates() + require.Len(t, delta, 2) + ret = delta[flow.RegisterID{ + Owner: string(owner), + Key: string(key1), + }] + require.Equal(t, value2, ret) +} diff --git a/fvm/evm/offchain/storage/readonly.go b/fvm/evm/offchain/storage/readonly.go new file mode 100644 index 00000000000..4ed33a6fe44 --- /dev/null +++ b/fvm/evm/offchain/storage/readonly.go @@ -0,0 +1,44 @@ +package storage + +import ( + "errors" + + "github.com/onflow/atree" + + "github.com/onflow/flow-go/fvm/evm/types" +) + +// ReadOnlyStorage wraps an snapshot and only provides read functionality. +type ReadOnlyStorage struct { + snapshot types.BackendStorageSnapshot +} + +var _ types.BackendStorage = &ReadOnlyStorage{} + +// NewReadOnlyStorage constructs a new ReadOnlyStorage using the given snapshot +func NewReadOnlyStorage(snapshot types.BackendStorageSnapshot) *ReadOnlyStorage { + return &ReadOnlyStorage{ + snapshot, + } +} + +// GetValue reads a register value +func (s *ReadOnlyStorage) GetValue(owner []byte, key []byte) ([]byte, error) { + return s.snapshot.GetValue(owner, key) +} + +// SetValue returns an error if called +func (s *ReadOnlyStorage) SetValue(owner, key, value []byte) error { + return errors.New("unexpected call received") +} + +// ValueExists checks if a register exists +func (s *ReadOnlyStorage) ValueExists(owner []byte, key []byte) (bool, error) { + val, err := s.snapshot.GetValue(owner, key) + return len(val) > 0, err +} + +// AllocateSlabIndex returns an error if called +func (s *ReadOnlyStorage) AllocateSlabIndex(owner []byte) (atree.SlabIndex, error) { + return atree.SlabIndex{}, errors.New("unexpected call received") +} diff --git a/fvm/evm/offchain/storage/readonly_test.go b/fvm/evm/offchain/storage/readonly_test.go new file mode 100644 index 00000000000..6020a381156 --- /dev/null +++ b/fvm/evm/offchain/storage/readonly_test.go @@ -0,0 +1,36 @@ +package storage_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/onflow/flow-go/fvm/evm/offchain/storage" + "github.com/onflow/flow-go/fvm/evm/testutils" +) + +func TestReadOnlyStorage(t *testing.T) { + parent := testutils.GetSimpleValueStore() + + owner := []byte("owner") + key1 := []byte("key1") + value1 := []byte{1} + err := parent.SetValue(owner, key1, value1) + require.NoError(t, err) + + rs := storage.NewReadOnlyStorage(parent) + ret, err := rs.GetValue(owner, key1) + require.NoError(t, err) + require.Equal(t, value1, ret) + + found, err := rs.ValueExists(owner, key1) + require.NoError(t, err) + require.True(t, found) + + err = rs.SetValue(owner, key1, value1) + require.Error(t, err) + + _, err = rs.AllocateSlabIndex(owner) + require.Error(t, err) + +} diff --git a/fvm/evm/offchain/sync/replay.go b/fvm/evm/offchain/sync/replay.go new file mode 100644 index 00000000000..4516f37007d --- /dev/null +++ b/fvm/evm/offchain/sync/replay.go @@ -0,0 +1,192 @@ +package sync + +import ( + "bytes" + "fmt" + + "github.com/onflow/atree" + gethTypes "github.com/onflow/go-ethereum/core/types" + gethTracer "github.com/onflow/go-ethereum/eth/tracers" + gethTrie "github.com/onflow/go-ethereum/trie" + + "github.com/onflow/flow-go/fvm/evm/emulator" + "github.com/onflow/flow-go/fvm/evm/events" + "github.com/onflow/flow-go/fvm/evm/precompiles" + "github.com/onflow/flow-go/fvm/evm/types" + "github.com/onflow/flow-go/model/flow" +) + +var emptyChecksum = [types.ChecksumLength]byte{0, 0, 0, 0} + +// ReplayBlockExecution re-executes transactions of a block using the +// events emitted when transactions where executed. +// it updates the state of the given ledger and uses the trace +func ReplayBlockExecution( + chainID flow.ChainID, + rootAddr flow.Address, + storage types.BackendStorage, + blockSnapshot types.BlockSnapshot, + tracer *gethTracer.Tracer, + transactionEvents []events.TransactionEventPayload, + blockEvent *events.BlockEventPayload, + validateResults bool, +) error { + // check the passed block event + if blockEvent == nil { + return fmt.Errorf("nil block event has been passed") + } + + // create a base block context for all transactions + // tx related context values will be replaced during execution + ctx, err := blockSnapshot.BlockContext() + if err != nil { + return err + } + // update the tracer + ctx.Tracer = tracer + + gasConsumedSoFar := uint64(0) + txHashes := make(types.TransactionHashes, len(transactionEvents)) + for idx, tx := range transactionEvents { + err = replayTransactionExecution( + rootAddr, + ctx, + uint(idx), + gasConsumedSoFar, + storage, + &tx, + validateResults, + ) + if err != nil { + return fmt.Errorf("transaction execution failed, txIndex: %d, err: %w", idx, err) + } + gasConsumedSoFar += tx.GasConsumed + txHashes[idx] = tx.Hash + } + + if validateResults { + // check transaction inclusion + txHashRoot := gethTypes.DeriveSha(txHashes, gethTrie.NewStackTrie(nil)) + if txHashRoot != blockEvent.TransactionHashRoot { + return fmt.Errorf("transaction root hash doesn't match [%x] != [%x]", txHashRoot, blockEvent.TransactionHashRoot) + } + + // check total gas used + if blockEvent.TotalGasUsed != gasConsumedSoFar { + return fmt.Errorf("total gas used doesn't match [%d] != [%d]", gasConsumedSoFar, blockEvent.TotalGasUsed) + } + // no need to check the receipt root hash given we have checked the logs and other + // values during tx execution. + } + + return nil +} + +func replayTransactionExecution( + rootAddr flow.Address, + ctx types.BlockContext, + txIndex uint, + gasUsedSoFar uint64, + ledger atree.Ledger, + txEvent *events.TransactionEventPayload, + validate bool, +) error { + + // create emulator + em := emulator.NewEmulator(ledger, rootAddr) + + // update block context with tx level info + ctx.TotalGasUsedSoFar = gasUsedSoFar + ctx.TxCountSoFar = txIndex + // populate precompiled calls + if len(txEvent.PrecompiledCalls) > 0 { + pcs, err := types.AggregatedPrecompileCallsFromEncoded(txEvent.PrecompiledCalls) + if err != nil { + return fmt.Errorf("error decoding precompiled calls [%x]: %w", txEvent.Payload, err) + } + ctx.ExtraPrecompiledContracts = precompiles.AggregatedPrecompiledCallsToPrecompiledContracts(pcs) + } + + // create a new block view + bv, err := em.NewBlockView(ctx) + if err != nil { + return err + } + + var res *types.Result + // check if the transaction payload is actually from a direct call, + // which is a special state transition in Flow EVM. + if txEvent.TransactionType == types.DirectCallTxType { + call, err := types.DirectCallFromEncoded(txEvent.Payload) + if err != nil { + return fmt.Errorf("failed to RLP-decode direct call [%x]: %w", txEvent.Payload, err) + } + res, err = bv.DirectCall(call) + if err != nil { + return fmt.Errorf("failed to execute direct call [%x]: %w", txEvent.Hash, err) + } + } else { + gethTx := &gethTypes.Transaction{} + if err := gethTx.UnmarshalBinary(txEvent.Payload); err != nil { + return fmt.Errorf("failed to RLP-decode transaction [%x]: %w", txEvent.Payload, err) + } + res, err = bv.RunTransaction(gethTx) + if err != nil { + return fmt.Errorf("failed to run transaction [%x]: %w", txEvent.Hash, err) + } + } + + // validate results + if validate { + if err := ValidateResult(res, txEvent); err != nil { + return fmt.Errorf("transaction replay failed (txHash %x): %w", txEvent.Hash, err) + } + } + + return nil +} + +func ValidateResult( + res *types.Result, + txEvent *events.TransactionEventPayload, +) error { + + // we should never produce invalid transaction, since if the transaction was emitted from the evm core + // it must have either been successful or failed, invalid transactions are not emitted + if res.Invalid() { + return fmt.Errorf("invalid transaction: %w", res.ValidationError) + } + + // check gas consumed + if res.GasConsumed != txEvent.GasConsumed { + return fmt.Errorf("gas consumption mismatch %d != %d", res.GasConsumed, txEvent.GasConsumed) + } + + // check error msg + if errMsg := res.ErrorMsg(); errMsg != txEvent.ErrorMessage { + return fmt.Errorf("error msg mismatch %s != %s", errMsg, txEvent.ErrorMessage) + } + + // check encoded logs + encodedLogs, err := res.RLPEncodedLogs() + if err != nil { + return fmt.Errorf("failed to RLP-encode logs: %w", err) + } + if !bytes.Equal(encodedLogs, txEvent.Logs) { + return fmt.Errorf("encoded logs mismatch %x != %x", encodedLogs, txEvent.Logs) + } + + // check deployed address + if deployedAddress := res.DeployedContractAddressString(); deployedAddress != txEvent.ContractAddress { + return fmt.Errorf("deployed address mismatch %s != %s", deployedAddress, txEvent.ContractAddress) + } + + // check the state change checksum + // if empty checksum skip (supporting blocks before checksum integration) + if checksum := res.StateChangeChecksum(); txEvent.StateUpdateChecksum != emptyChecksum && + checksum != txEvent.StateUpdateChecksum { + return fmt.Errorf("state change checksum mismatch %x != %x", checksum, txEvent.StateUpdateChecksum) + } + + return nil +} diff --git a/fvm/evm/offchain/sync/replayer.go b/fvm/evm/offchain/sync/replayer.go new file mode 100644 index 00000000000..25ccdc10cbf --- /dev/null +++ b/fvm/evm/offchain/sync/replayer.go @@ -0,0 +1,91 @@ +package sync + +import ( + gethTracers "github.com/onflow/go-ethereum/eth/tracers" + "github.com/rs/zerolog" + + "github.com/onflow/flow-go/fvm/evm/events" + "github.com/onflow/flow-go/fvm/evm/offchain/storage" + "github.com/onflow/flow-go/fvm/evm/types" + "github.com/onflow/flow-go/model/flow" +) + +// Replayer consumes EVM transaction and block +// events, re-execute EVM transaction and follows EVM chain. +// this allows using different tracers and storage solutions. +type Replayer struct { + chainID flow.ChainID + rootAddr flow.Address + logger zerolog.Logger + storageProvider types.StorageProvider + blockProvider types.BlockSnapshotProvider + tracer *gethTracers.Tracer + validateResults bool +} + +// NewReplayer constructs a new Replayer +func NewReplayer( + chainID flow.ChainID, + rootAddr flow.Address, + sp types.StorageProvider, + bp types.BlockSnapshotProvider, + logger zerolog.Logger, + tracer *gethTracers.Tracer, + validateResults bool, +) *Replayer { + return &Replayer{ + chainID: chainID, + rootAddr: rootAddr, + storageProvider: sp, + blockProvider: bp, + logger: logger, + tracer: tracer, + validateResults: validateResults, + } +} + +// ReplayBlock replays the execution of the transactions of an EVM block +// using the provided transactionEvents and blockEvents, +// which include all the context data for re-executing the transactions, and returns the replay result. +// this method can be called concurrently if underlying storage +// tracer and block snapshot provider support concurrency. +// +// Warning! the list of transaction events has to be sorted based on their +// execution, sometimes the access node might return events out of order +// it needs to be sorted by txIndex and eventIndex respectively. +func (cr *Replayer) ReplayBlock( + transactionEvents []events.TransactionEventPayload, + blockEvent *events.BlockEventPayload, +) (types.ReplayResultCollector, error) { + // prepare storage + st, err := cr.storageProvider.GetSnapshotAt(blockEvent.Height) + if err != nil { + return nil, err + } + + // create storage + state := storage.NewEphemeralStorage(storage.NewReadOnlyStorage(st)) + + // get block snapshot + bs, err := cr.blockProvider.GetSnapshotAt(blockEvent.Height) + if err != nil { + return nil, err + } + + // replay transactions + err = ReplayBlockExecution( + cr.chainID, + cr.rootAddr, + state, + bs, + cr.tracer, + transactionEvents, + blockEvent, + cr.validateResults, + ) + if err != nil { + return nil, err + } + + return state, nil +} diff --git a/fvm/evm/offchain/sync/replayer_test.go b/fvm/evm/offchain/sync/replayer_test.go new file mode 100644 index 00000000000..f7c05ab63b5 --- /dev/null +++ b/fvm/evm/offchain/sync/replayer_test.go @@ -0,0 +1,205 @@ +package sync_test + +import ( + "math" + "math/big" + "testing" + + gethCommon "github.com/onflow/go-ethereum/common" + "github.com/rs/zerolog" + "github.com/stretchr/testify/require" + + "github.com/onflow/flow-go/fvm/evm" + "github.com/onflow/flow-go/fvm/evm/events" + "github.com/onflow/flow-go/fvm/evm/offchain/blocks" + "github.com/onflow/flow-go/fvm/evm/offchain/sync" + . "github.com/onflow/flow-go/fvm/evm/testutils" + "github.com/onflow/flow-go/fvm/evm/types" + "github.com/onflow/flow-go/model/flow" +) + +func TestChainReplay(t *testing.T) { + + const chainID = flow.Emulator + var snapshot *TestValueStore + RunWithTestBackend(t, func(backend *TestBackend) { + RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { + RunWithDeployedContract(t, + GetStorageTestContract(t), backend, rootAddr, func(testContract *TestContract) { + RunWithEOATestAccount(t, backend, rootAddr, func(testAccount *EOATestAccount) { + handler := SetupHandler(chainID, backend, rootAddr) + + // clone state before apply transactions + snapshot = backend.Clone() + gasFeeCollector := RandomAddress(t) + + totalTxCount := 0 + + // case: check sequential updates to a slot + for i := 0; i < 5; i++ { + tx := testAccount.PrepareSignAndEncodeTx(t, + testContract.DeployedAt.ToCommon(), + testContract.MakeCallData(t, "checkThenStore", big.NewInt(int64(i)), big.NewInt(int64(i+1))), + big.NewInt(0), + uint64(100_000), + big.NewInt(1), + ) + rs := handler.Run(tx, gasFeeCollector) + require.Equal(t, types.ErrorCode(0), rs.ErrorCode) + totalTxCount += 2 // one for tx, one for gas refund + } + + // case: add batch run BatchRun + batchSize := 4 + txBatch := make([][]byte, batchSize) + for i := 0; i < batchSize; i++ { + txBatch[i] = testAccount.PrepareSignAndEncodeTx(t, + testContract.DeployedAt.ToCommon(), + testContract.MakeCallData(t, "store", big.NewInt(int64(i))), + big.NewInt(0), + uint64(100_000), + big.NewInt(1), + ) + } + rss := handler.BatchRun(txBatch, gasFeeCollector) + for _, rs := range rss { + require.Equal(t, types.ErrorCode(0), rs.ErrorCode) + } + totalTxCount += batchSize + 1 // plus one for gas refund + + // case: fetching evm block number + tx := testAccount.PrepareSignAndEncodeTx(t, + testContract.DeployedAt.ToCommon(), + testContract.MakeCallData(t, "checkBlockNumber", big.NewInt(1)), + big.NewInt(0), + uint64(100_000), + big.NewInt(1), + ) + rs := handler.Run(tx, gasFeeCollector) + require.Equal(t, types.ErrorCode(0), rs.ErrorCode) + totalTxCount += 2 // one for tx, one for gas refund + + // case: making a call to the cadence arch + expectedFlowHeight := uint64(3) + tx = testAccount.PrepareSignAndEncodeTx(t, + testContract.DeployedAt.ToCommon(), + testContract.MakeCallData(t, "verifyArchCallToFlowBlockHeight", expectedFlowHeight), + big.NewInt(0), + uint64(2_000_000), + big.NewInt(1), + ) + rs = handler.Run(tx, gasFeeCollector) + require.Equal(t, types.ErrorCode(0), rs.ErrorCode) + totalTxCount += 2 // one for tx, one for gas refund + + // case: fetch evm block hash - last block + expected := types.GenesisBlockHash(chainID) + tx = testAccount.PrepareSignAndEncodeTx(t, + testContract.DeployedAt.ToCommon(), + testContract.MakeCallData(t, "checkBlockHash", big.NewInt(0), expected), + big.NewInt(0), + uint64(100_000), + big.NewInt(1), + ) + rs = handler.Run(tx, gasFeeCollector) + require.Equal(t, types.ErrorCode(0), rs.ErrorCode) + totalTxCount += 2 // one for tx, one for gas refund + + // case: fetch evm block hash - current block + expected = gethCommon.Hash{} + tx = testAccount.PrepareSignAndEncodeTx(t, + testContract.DeployedAt.ToCommon(), + testContract.MakeCallData(t, "checkBlockHash", big.NewInt(1), expected), + big.NewInt(0), + uint64(100_000), + big.NewInt(1), + ) + rs = handler.Run(tx, gasFeeCollector) + require.Equal(t, types.ErrorCode(0), rs.ErrorCode) + totalTxCount += 2 // one for tx, one for gas refund + + // case: coa operations + addr := handler.DeployCOA(100) + totalTxCount += 1 + + coa := handler.AccountByAddress(addr, true) + coa.Deposit(types.NewFlowTokenVault(types.MakeABalanceInFlow(10))) + totalTxCount += 1 + coa.Withdraw(types.NewBalance(types.MakeABalanceInFlow(4))) + totalTxCount += 1 + + expectedBalance := (*big.Int)(types.MakeABalanceInFlow(6)) + rs = coa.Call( + testContract.DeployedAt, + testContract.MakeCallData(t, "checkBalance", addr.ToCommon(), expectedBalance), + 100_000, + types.EmptyBalance, + ) + require.Equal(t, types.ErrorCode(0), rs.ErrorCode) + totalTxCount += 1 + + rs = coa.Deploy(testContract.ByteCode, math.MaxUint64, types.EmptyBalance) + require.Equal(t, types.ErrorCode(0), rs.ErrorCode) + totalTxCount += 1 + + // commit block + handler.CommitBlockProposal() + + // prepare events + txEventPayloads, blockEventPayload := prepareEvents(t, chainID, backend.Events()) + + // because we are doing direct calls, there is no extra + // events (e.g. COA created) events emitted. + require.Len(t, txEventPayloads, totalTxCount) + + // check replay + + bp, err := blocks.NewBasicProvider(chainID, snapshot, rootAddr) + require.NoError(t, err) + + err = bp.OnBlockReceived(blockEventPayload) + require.NoError(t, err) + + sp := NewTestStorageProvider(snapshot, 1) + cr := sync.NewReplayer(chainID, rootAddr, sp, bp, zerolog.Logger{}, nil, true) + res, err := cr.ReplayBlock(txEventPayloads, blockEventPayload) + require.NoError(t, err) + + err = bp.OnBlockExecuted(blockEventPayload.Height, res) + require.NoError(t, err) + + // TODO: verify the state delta + // currently the backend storage doesn't work well with this + // changes needed to make this work, which is left for future PRs + // + // for k, v := range result.StorageRegisterUpdates() { + // ret, err := backend.GetValue([]byte(k.Owner), []byte(k.Key)) + // require.NoError(t, err) + // require.Equal(t, ret[:], v[:]) + // } + }) + }) + }) + }) +} + +func prepareEvents( + t *testing.T, + chainID flow.ChainID, + allEvents flow.EventsList) ( + []events.TransactionEventPayload, + *events.BlockEventPayload, +) { + evmContract := evm.ContractAccountAddress(chainID) + var blockEventPayload *events.BlockEventPayload + txEventPayloads := make([]events.TransactionEventPayload, len(allEvents)-1) + for i, event := range allEvents { + // last event is block event + if i == len(allEvents)-1 { + blockEventPayload = BlockEventToPayload(t, event, evmContract) + continue + } + txEventPayloads[i] = *TxEventToPayload(t, event, evmContract) + } + return txEventPayloads, blockEventPayload +} diff --git a/fvm/evm/offchain/utils/collection.go b/fvm/evm/offchain/utils/collection.go new file mode 100644 index 00000000000..143c2bd3eee --- /dev/null +++ b/fvm/evm/offchain/utils/collection.go @@ -0,0 +1,104 @@ +package utils + +import ( + "bufio" + "context" + "encoding/hex" + "encoding/json" + "fmt" + "math" + "os" + "path/filepath" + "sort" + + "github.com/onflow/flow-go-sdk/access/grpc" +) + +func CollectEventData(conf *Config, path string) error { + + flowClient, err := grpc.NewClient(conf.host) + if err != nil { + return err + } + outputFile := filepath.Join(path, "events.jsonl") + fi, err := os.Create(outputFile) + if err != nil { + return err + } + defer fi.Close() + + writer := bufio.NewWriter(fi) + defer writer.Flush() + + ctx := context.Background() + + txEventType := fmt.Sprintf("A.%s.EVM.TransactionExecuted", conf.evmContractAddress) + blockEventType := fmt.Sprintf("A.%s.EVM.BlockExecuted", conf.evmContractAddress) + + for height := conf.startHeight; height < conf.endHeight; height += conf.batchSize { + events := make([]Event, 0) + result, err := flowClient.GetEventsForHeightRange(ctx, txEventType, height, height+conf.batchSize-1) + if err != nil { + return err + } + if len(result) > 0 { + for _, tEvent := range result { + evs := tEvent.Events + for _, e := range evs { + events = append(events, Event{ + FlowBlockHeight: tEvent.Height, + EventType: e.Type, + EventPayload: hex.EncodeToString(e.Payload), + txIndex: e.TransactionIndex, + eventIndex: e.EventIndex, + }) + } + } + } + result, err = flowClient.GetEventsForHeightRange(ctx, blockEventType, height, height+conf.batchSize-1) + if err != nil { + return err + } + if len(result) > 0 { + for _, bEvent := range result { + evs := bEvent.Events + for _, e := range evs { + events = append(events, Event{ + FlowBlockHeight: bEvent.Height, + EventType: e.Type, + EventPayload: hex.EncodeToString(e.Payload), + // setting to max int to make sure it is order as the last event of the evm block + txIndex: math.MaxInt, + }) + } + } + } + + // sort events by flow height, tx index and then event index + sort.Slice(events, func(i, j int) bool { + if events[i].FlowBlockHeight == events[j].FlowBlockHeight { + if events[i].txIndex == events[j].txIndex { + return events[i].eventIndex < events[j].eventIndex + } + return events[i].txIndex < events[j].txIndex + } + return events[i].FlowBlockHeight < events[j].FlowBlockHeight + }) + + for _, ev := range events { + jsonData, err := json.Marshal(ev) + if err != nil { + return err + } + _, err = writer.WriteString(string(jsonData) + "\n") + if err != nil { + return err + } + err = writer.Flush() + if err != nil { + return err + } + } + } + return writer.Flush() +} diff --git a/fvm/evm/offchain/utils/collection_test.go b/fvm/evm/offchain/utils/collection_test.go new file mode 100644 index 00000000000..a90a8f57bea --- /dev/null +++ b/fvm/evm/offchain/utils/collection_test.go @@ -0,0 +1,99 @@ +package utils_test + +import ( + "bufio" + "encoding/hex" + "encoding/json" + "os" + "strings" + "testing" + + "github.com/onflow/cadence" + "github.com/onflow/cadence/encoding/ccf" + "github.com/rs/zerolog" + "github.com/stretchr/testify/require" + + "github.com/onflow/flow-go/fvm/environment" + "github.com/onflow/flow-go/fvm/evm" + "github.com/onflow/flow-go/fvm/evm/events" + "github.com/onflow/flow-go/fvm/evm/offchain/blocks" + "github.com/onflow/flow-go/fvm/evm/offchain/sync" + "github.com/onflow/flow-go/fvm/evm/offchain/utils" + . "github.com/onflow/flow-go/fvm/evm/testutils" + "github.com/onflow/flow-go/fvm/evm/types" + "github.com/onflow/flow-go/model/flow" +) + +func ReplyingCollectionFromScratch( + t *testing.T, + chainID flow.ChainID, + storage types.BackendStorage, + filePath string, +) { + + rootAddr := evm.StorageAccountAddress(chainID) + + // setup the rootAddress account + as := environment.NewAccountStatus() + err := storage.SetValue(rootAddr[:], []byte(flow.AccountStatusKey), as.ToBytes()) + require.NoError(t, err) + + bp, err := blocks.NewBasicProvider(chainID, storage, rootAddr) + require.NoError(t, err) + + file, err := os.Open(filePath) + require.NoError(t, err) + defer file.Close() + + scanner := bufio.NewScanner(file) + + buf := make([]byte, 0, 64*1024) + scanner.Buffer(buf, 1024*1024) + + txEvents := make([]events.TransactionEventPayload, 0) + + for scanner.Scan() { + data := scanner.Bytes() + var e utils.Event + err := json.Unmarshal(data, &e) + require.NoError(t, err) + if strings.Contains(e.EventType, "BlockExecuted") { + temp, err := hex.DecodeString(e.EventPayload) + require.NoError(t, err) + ev, err := ccf.Decode(nil, temp) + require.NoError(t, err) + blockEventPayload, err := events.DecodeBlockEventPayload(ev.(cadence.Event)) + require.NoError(t, err) + + err = bp.OnBlockReceived(blockEventPayload) + require.NoError(t, err) + + sp := NewTestStorageProvider(storage, blockEventPayload.Height) + cr := sync.NewReplayer(chainID, rootAddr, sp, bp, zerolog.Logger{}, nil, true) + res, err := cr.ReplayBlock(txEvents, blockEventPayload) + require.NoError(t, err) + // commit all changes + for k, v := range res.StorageRegisterUpdates() { + err = storage.SetValue([]byte(k.Owner), []byte(k.Key), v) + require.NoError(t, err) + } + + err = bp.OnBlockExecuted(blockEventPayload.Height, res) + require.NoError(t, err) + + txEvents = make([]events.TransactionEventPayload, 0) + continue + } + + temp, err := hex.DecodeString(e.EventPayload) + require.NoError(t, err) + ev, err := ccf.Decode(nil, temp) + require.NoError(t, err) + txEv, err := events.DecodeTransactionEventPayload(ev.(cadence.Event)) + require.NoError(t, err) + txEvents = append(txEvents, *txEv) + } + if err := scanner.Err(); err != nil { + t.Fatal(err) + } +} diff --git a/fvm/evm/offchain/utils/types.go b/fvm/evm/offchain/utils/types.go new file mode 100644 index 00000000000..f40b19e4e91 --- /dev/null +++ b/fvm/evm/offchain/utils/types.go @@ -0,0 +1,33 @@ +package utils + +type Event struct { + FlowBlockHeight uint64 `json:"flow_height"` + EventType string `json:"type"` + EventPayload string `json:"payload"` + txIndex int + eventIndex int +} + +type Config struct { + host string + evmContractAddress string // no prefix + startHeight uint64 + endHeight uint64 + batchSize uint64 +} + +var Devnet51Config = Config{ + host: "access-001.devnet51.nodes.onflow.org:9000", + evmContractAddress: "8c5303eaa26202d6", + startHeight: uint64(211176670), + endHeight: uint64(218215349), + batchSize: uint64(50), +} + +var Mainnet25Config = Config{ + host: "access-001.mainnet25.nodes.onflow.org:9000", + evmContractAddress: "e467b9dd11fa00df", + startHeight: uint64(85981135), + endHeight: uint64(88226266), + batchSize: uint64(50), +} diff --git a/fvm/evm/precompiles/replayer.go b/fvm/evm/precompiles/replayer.go index deebce92153..e35f3e0f588 100644 --- a/fvm/evm/precompiles/replayer.go +++ b/fvm/evm/precompiles/replayer.go @@ -1,7 +1,6 @@ package precompiles import ( - "bytes" "errors" "fmt" @@ -54,10 +53,7 @@ func (p *ReplayerPrecompiledContract) RequiredGas(input []byte) (output uint64) if p.requiredGasIndex > len(p.expectedCalls.RequiredGasCalls) { panic(errUnexpectedCall) } - if !bytes.Equal(p.expectedCalls.RequiredGasCalls[p.requiredGasIndex].Input, input) { - panic(errUnexpectedCall) - } - output = p.expectedCalls.RequiredGasCalls[p.requiredGasIndex].Output + output = p.expectedCalls.RequiredGasCalls[p.requiredGasIndex] p.requiredGasIndex++ return } @@ -66,9 +62,6 @@ func (p *ReplayerPrecompiledContract) Run(input []byte) (output []byte, err erro if p.runIndex > len(p.expectedCalls.RunCalls) { panic(errUnexpectedCall) } - if !bytes.Equal(p.expectedCalls.RunCalls[p.runIndex].Input, input) { - panic(errUnexpectedCall) - } output = p.expectedCalls.RunCalls[p.runIndex].Output errMsg := p.expectedCalls.RunCalls[p.runIndex].ErrorMsg if len(errMsg) > 0 { diff --git a/fvm/evm/precompiles/replayer_test.go b/fvm/evm/precompiles/replayer_test.go index 50f8d256899..02ee281a74c 100644 --- a/fvm/evm/precompiles/replayer_test.go +++ b/fvm/evm/precompiles/replayer_test.go @@ -24,23 +24,15 @@ func TestReplayer(t *testing.T) { pc := &types.PrecompiledCalls{ Address: address, - RequiredGasCalls: []types.RequiredGasCall{ - { - Input: input1, - Output: gas1, - }, - { - Input: input2, - Output: gas2, - }, + RequiredGasCalls: []uint64{ + gas1, + gas2, }, RunCalls: []types.RunCall{ { - Input: input1, Output: output1, }, { - Input: input2, Output: output2, ErrorMsg: errMsg2, }, diff --git a/fvm/evm/stdlib/checking.go b/fvm/evm/stdlib/checking.go index d4862cb56c6..81faed8c298 100644 --- a/fvm/evm/stdlib/checking.go +++ b/fvm/evm/stdlib/checking.go @@ -1,11 +1,11 @@ package stdlib import ( - "fmt" - + "github.com/onflow/cadence/common" + "github.com/onflow/cadence/interpreter" "github.com/onflow/cadence/runtime" - "github.com/onflow/cadence/runtime/common" - "github.com/onflow/cadence/runtime/interpreter" + + "github.com/onflow/flow-go/fvm/environment" ) // checkingInterface is a runtime.Interface implementation @@ -13,68 +13,41 @@ import ( // It is not suitable for execution. type checkingInterface struct { runtime.EmptyRuntimeInterface - SystemContractCodes map[common.AddressLocation][]byte - Programs map[runtime.Location]*interpreter.Program + SystemContractCodes map[common.Location][]byte + Programs map[runtime.Location]*interpreter.Program + cryptoContractAddress common.Address } var _ runtime.Interface = &checkingInterface{} -func (*checkingInterface) ResolveLocation( +func (i *checkingInterface) ResolveLocation( identifiers []runtime.Identifier, location runtime.Location, ) ( []runtime.ResolvedLocation, error, ) { - - addressLocation, isAddress := location.(common.AddressLocation) - - // if the location is not an address location, e.g. an identifier location - // (`import Crypto`), then return a single resolved location which declares - // all identifiers. - if !isAddress { - return []runtime.ResolvedLocation{ - { - Location: location, - Identifiers: identifiers, - }, - }, nil - } - - if len(identifiers) == 0 { - return nil, fmt.Errorf("no identifiers provided") - } - - // return one resolved location per identifier. - // each resolved location is an address contract location - resolvedLocations := make([]runtime.ResolvedLocation, len(identifiers)) - for i := range resolvedLocations { - identifier := identifiers[i] - resolvedLocations[i] = runtime.ResolvedLocation{ - Location: common.AddressLocation{ - Address: addressLocation.Address, - Name: identifier.Identifier, - }, - Identifiers: []runtime.Identifier{identifier}, - } - } - - return resolvedLocations, nil + return environment.ResolveLocation( + identifiers, + location, + nil, + i.cryptoContractAddress, + ) } -func (r *checkingInterface) GetOrLoadProgram( +func (i *checkingInterface) GetOrLoadProgram( location runtime.Location, load func() (*interpreter.Program, error), ) ( program *interpreter.Program, err error, ) { - if r.Programs == nil { - r.Programs = map[runtime.Location]*interpreter.Program{} + if i.Programs == nil { + i.Programs = map[runtime.Location]*interpreter.Program{} } var ok bool - program, ok = r.Programs[location] + program, ok = i.Programs[location] if ok { return } @@ -84,11 +57,15 @@ func (r *checkingInterface) GetOrLoadProgram( // NOTE: important: still set empty program, // even if error occurred - r.Programs[location] = program + i.Programs[location] = program return } -func (r *checkingInterface) GetAccountContractCode(location common.AddressLocation) (code []byte, err error) { - return r.SystemContractCodes[location], nil +func (i *checkingInterface) GetCode(location common.Location) ([]byte, error) { + return i.SystemContractCodes[location], nil +} + +func (i *checkingInterface) GetAccountContractCode(location common.AddressLocation) (code []byte, err error) { + return i.SystemContractCodes[location], nil } diff --git a/fvm/evm/stdlib/contract.cdc b/fvm/evm/stdlib/contract.cdc index 21ed5a0af78..a547768362e 100644 --- a/fvm/evm/stdlib/contract.cdc +++ b/fvm/evm/stdlib/contract.cdc @@ -73,7 +73,10 @@ contract EVM { /// precompiled contracts (e.g. Cadence Arch) during the transaction execution. /// This data helps to replay the transactions without the need to /// have access to the full cadence state data. - precompiledCalls: [UInt8] + precompiledCalls: [UInt8], + /// stateUpdateChecksum provides a mean to validate + /// the updates to the storage when re-executing a transaction off-chain. + stateUpdateChecksum: [UInt8; 4] ) access(all) @@ -200,6 +203,48 @@ contract EVM { } } + /// EVMBytes is a type wrapper used for ABI encoding/decoding into + /// Solidity `bytes` type + access(all) + struct EVMBytes { + + /// Byte array representing the `bytes` value + access(all) + let value: [UInt8] + + view init(value: [UInt8]) { + self.value = value + } + } + + /// EVMBytes4 is a type wrapper used for ABI encoding/decoding into + /// Solidity `bytes4` type + access(all) + struct EVMBytes4 { + + /// Byte array representing the `bytes4` value + access(all) + let value: [UInt8; 4] + + view init(value: [UInt8; 4]) { + self.value = value + } + } + + /// EVMBytes32 is a type wrapper used for ABI encoding/decoding into + /// Solidity `bytes32` type + access(all) + struct EVMBytes32 { + + /// Byte array representing the `bytes32` value + access(all) + let value: [UInt8; 32] + + view init(value: [UInt8; 32]) { + self.value = value + } + } + /// Converts a hex string to an EVM address if the string is a valid hex string /// Future implementations should pass data to InternalEVM for native deserialization access(all) @@ -634,7 +679,6 @@ contract EVM { signatures: [[UInt8]], evmAddress: [UInt8; 20] ): ValidationResult { - // make signature set first // check number of signatures matches number of key indices if keyIndices.length != signatures.length { @@ -644,39 +688,58 @@ contract EVM { ) } - var signatureSet: [Crypto.KeyListSignature] = [] - for signatureIndex, signature in signatures{ - signatureSet.append(Crypto.KeyListSignature( - keyIndex: Int(keyIndices[signatureIndex]), - signature: signature - )) - } - // fetch account let acc = getAccount(address) - // constructing key list + var signatureSet: [Crypto.KeyListSignature] = [] let keyList = Crypto.KeyList() - for signature in signatureSet { - let keyRef = acc.keys.get(keyIndex: signature.keyIndex) - if keyRef == nil { - return ValidationResult( - isValid: false, - problem: "invalid key index" - ) - } - let key = keyRef! - if key.isRevoked { - return ValidationResult( - isValid: false, - problem: "account key is revoked" - ) + var keyListLength = 0 + let seenAccountKeyIndices: {Int: Int} = {} + for signatureIndex, signature in signatures{ + // index of the key on the account + let accountKeyIndex = Int(keyIndices[signatureIndex]!) + // index of the key in the key list + var keyListIndex = 0 + + if !seenAccountKeyIndices.containsKey(accountKeyIndex) { + // fetch account key with accountKeyIndex + if let key = acc.keys.get(keyIndex: accountKeyIndex) { + if key.isRevoked { + return ValidationResult( + isValid: false, + problem: "account key is revoked" + ) + } + + keyList.add( + key.publicKey, + hashAlgorithm: key.hashAlgorithm, + // normalization factor. We need to divide by 1000 because the + // `Crypto.KeyList.verify()` function expects the weight to be + // in the range [0, 1]. 1000 is the key weight threshold. + weight: key.weight / 1000.0, + ) + + keyListIndex = keyListLength + keyListLength = keyListLength + 1 + seenAccountKeyIndices[accountKeyIndex] = keyListIndex + } else { + return ValidationResult( + isValid: false, + problem: "invalid key index" + ) + } + } else { + // if we have already seen this accountKeyIndex, use the keyListIndex + // that was previously assigned to it + // `Crypto.KeyList.verify()` knows how to handle duplicate keys + keyListIndex = seenAccountKeyIndices[accountKeyIndex]! } - keyList.add( - key.publicKey, - hashAlgorithm: key.hashAlgorithm, - weight: key.weight, - ) + + signatureSet.append(Crypto.KeyListSignature( + keyIndex: keyListIndex, + signature: signature + )) } let isValid = keyList.verify( diff --git a/fvm/evm/stdlib/contract.go b/fvm/evm/stdlib/contract.go index 7c580190a07..df53623e2d8 100644 --- a/fvm/evm/stdlib/contract.go +++ b/fvm/evm/stdlib/contract.go @@ -6,11 +6,11 @@ import ( "regexp" "github.com/onflow/cadence" + "github.com/onflow/cadence/common" + "github.com/onflow/cadence/interpreter" "github.com/onflow/cadence/runtime" - "github.com/onflow/cadence/runtime/common" - "github.com/onflow/cadence/runtime/interpreter" - "github.com/onflow/cadence/runtime/sema" - "github.com/onflow/cadence/runtime/stdlib" + "github.com/onflow/cadence/sema" + "github.com/onflow/cadence/stdlib" "github.com/onflow/flow-go/model/flow" ) @@ -46,10 +46,18 @@ const ( EVMAddressTypeBytesFieldName = "bytes" + EVMBytesTypeValueFieldName = "value" + EVMAddressTypeQualifiedIdentifier = "EVM.EVMAddress" EVMBalanceTypeQualifiedIdentifier = "EVM.Balance" + EVMBytesTypeQualifiedIdentifier = "EVM.EVMBytes" + + EVMBytes4TypeQualifiedIdentifier = "EVM.EVMBytes4" + + EVMBytes32TypeQualifiedIdentifier = "EVM.EVMBytes32" + EVMResultTypeQualifiedIdentifier = "EVM.Result" EVMResultTypeStatusFieldName = "status" EVMResultTypeErrorCodeFieldName = "errorCode" @@ -63,7 +71,11 @@ const ( EVMBlockTypeQualifiedIdentifier = "EVM.EVMBlock" ) -const EVMAddressLength = 20 +const ( + EVMAddressLength = 20 + EVMBytes4Length = 4 + EVMBytes32Length = 32 +) var ( EVMTransactionBytesCadenceType = cadence.NewVariableSizedArrayType(cadence.UInt8Type) @@ -74,6 +86,18 @@ var ( EVMAddressBytesStaticType = interpreter.ConvertSemaArrayTypeToStaticArrayType(nil, EVMAddressBytesType) + EVMBytesValueStaticType = interpreter.ConvertSemaArrayTypeToStaticArrayType(nil, EVMTransactionBytesType) + + EVMBytes4ValueStaticType = interpreter.ConvertSemaArrayTypeToStaticArrayType( + nil, + sema.NewConstantSizedType(nil, sema.UInt8Type, EVMBytes4Length), + ) + + EVMBytes32ValueStaticType = interpreter.ConvertSemaArrayTypeToStaticArrayType( + nil, + sema.NewConstantSizedType(nil, sema.UInt8Type, EVMBytes32Length), + ) + EVMAddressBytesCadenceType = cadence.NewConstantSizedArrayType(EVMAddressLength, cadence.UInt8Type) ) diff --git a/fvm/evm/stdlib/contract_test.go b/fvm/evm/stdlib/contract_test.go index 88f05e63983..9bfdc02a495 100644 --- a/fvm/evm/stdlib/contract_test.go +++ b/fvm/evm/stdlib/contract_test.go @@ -1,6 +1,7 @@ package stdlib_test import ( + "bytes" "encoding/binary" "encoding/hex" "math/big" @@ -8,13 +9,13 @@ import ( "testing" "github.com/onflow/cadence" + "github.com/onflow/cadence/common" "github.com/onflow/cadence/encoding/json" "github.com/onflow/cadence/runtime" - "github.com/onflow/cadence/runtime/common" - "github.com/onflow/cadence/runtime/sema" - cadenceStdlib "github.com/onflow/cadence/runtime/stdlib" - . "github.com/onflow/cadence/runtime/tests/runtime_utils" - "github.com/onflow/cadence/runtime/tests/utils" + "github.com/onflow/cadence/sema" + cadenceStdlib "github.com/onflow/cadence/stdlib" + . "github.com/onflow/cadence/test_utils/common_utils" + . "github.com/onflow/cadence/test_utils/runtime_utils" coreContracts "github.com/onflow/flow-core-contracts/lib/go/contracts" coreContractstemplates "github.com/onflow/flow-core-contracts/lib/go/templates" "github.com/onflow/go-ethereum/crypto" @@ -30,6 +31,26 @@ import ( "github.com/onflow/flow-go/model/flow" ) +func newLocationResolver( + cryptoContractAddress flow.Address, +) func( + identifiers []runtime.Identifier, + location runtime.Location, +) ([]runtime.ResolvedLocation, error) { + cryptoContractAddress2 := common.Address(cryptoContractAddress) + return func( + identifiers []runtime.Identifier, + location runtime.Location, + ) ([]runtime.ResolvedLocation, error) { + return environment.ResolveLocation( + identifiers, + location, + nil, + cryptoContractAddress2, + ) + } +} + type testContractHandler struct { flowTokenAddress common.Address evmContractAddress common.Address @@ -222,6 +243,7 @@ func deployContracts( NonFungibleTokenAddress: contractsAddressHex, MetadataViewsAddress: contractsAddressHex, FungibleTokenMetadataViewsAddress: contractsAddressHex, + CryptoAddress: contractsAddressHex, } contracts := []struct { @@ -229,6 +251,10 @@ func deployContracts( code []byte deployTx []byte }{ + { + name: "Crypto", + code: coreContracts.Crypto(), + }, { name: "ViewResolver", code: coreContracts.ViewResolver(), @@ -373,7 +399,7 @@ func TestEVMEncodeABI(t *testing.T) { OnGetSigningAccounts: func() ([]runtime.Address, error) { return []runtime.Address{runtime.Address(contractsAddress)}, nil }, - OnResolveLocation: LocationResolver, + OnResolveLocation: newLocationResolver(contractsAddress), OnUpdateAccountContractCode: func(location common.AddressLocation, code []byte) error { accountCodes[location] = code return nil @@ -455,42 +481,16 @@ func TestEVMEncodeABI(t *testing.T) { assert.Equal(t, computation, uint(len(cdcBytes))) } -func TestEVMEncodeABIComputation(t *testing.T) { +func TestEVMEncodeABIByteTypes(t *testing.T) { t.Parallel() handler := &testContractHandler{} - contractsAddress := flow.BytesToAddress([]byte{0x1}) - transactionEnvironment := newEVMTransactionEnvironment(handler, contractsAddress) scriptEnvironment := newEVMScriptEnvironment(handler, contractsAddress) - rt := runtime.NewInterpreterRuntime(runtime.Config{}) - script := []byte(` - import EVM from 0x1 - - access(all) - fun main(): [UInt8] { - let address = EVM.EVMAddress( - bytes: "7A58c0Be72BE218B41C608b7Fe7C5bB630736C71" - .decodeHex() - .toConstantSized<[UInt8; 20]>()! - ) - let arr: [UInt8] = [1, 2, 3, 4, 5] - - return EVM.encodeABI([ - "John Doe", - UInt64(33), - false, - address, - [arr], - ["one", "two", "three"] - ]) - } - `) - accountCodes := map[common.Location][]byte{} var events []cadence.Event @@ -500,7 +500,7 @@ func TestEVMEncodeABIComputation(t *testing.T) { OnGetSigningAccounts: func() ([]runtime.Address, error) { return []runtime.Address{runtime.Address(contractsAddress)}, nil }, - OnResolveLocation: LocationResolver, + OnResolveLocation: newLocationResolver(contractsAddress), OnUpdateAccountContractCode: func(location common.AddressLocation, code []byte) error { accountCodes[location] = code return nil @@ -528,7 +528,6 @@ func TestEVMEncodeABIComputation(t *testing.T) { nextScriptLocation := NewScriptLocationGenerator() // Deploy contracts - deployContracts( t, rt, @@ -538,54 +537,407 @@ func TestEVMEncodeABIComputation(t *testing.T) { nextTransactionLocation, ) - // Run script + t.Run("ABI encode into `bytes` Solidity type", func(t *testing.T) { + script := []byte(` + import EVM from 0x1 - result, err := rt.ExecuteScript( - runtime.Script{ - Source: script, - Arguments: [][]byte{}, - }, - runtime.Context{ - Interface: runtimeInterface, - Environment: scriptEnvironment, - Location: nextScriptLocation(), - }, - ) - require.NoError(t, err) + access(all) + fun main(): [UInt8] { + let bytes: EVM.EVMBytes = EVM.EVMBytes(value: [5, 10, 15, 20, 25]) + return EVM.encodeABI([bytes]) + } + `) - cdcBytes, ok := result.(cadence.Array) - require.True(t, ok) - // computation & len(cdcBytes.Values) is equal to 832 - assert.Equal(t, computation, uint(len(cdcBytes.Values))) + // Run script + result, err := rt.ExecuteScript( + runtime.Script{ + Source: script, + Arguments: [][]byte{}, + }, + runtime.Context{ + Interface: runtimeInterface, + Environment: scriptEnvironment, + Location: nextScriptLocation(), + }, + ) + require.NoError(t, err) + + abiBytes := []byte{ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, 0x5, + 0xa, 0xf, 0x14, 0x19, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, + } + expected := "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005050a0f1419000000000000000000000000000000000000000000000000000000" + assert.Equal( + t, + expected, + hex.EncodeToString(abiBytes), + ) + cdcBytes := make([]cadence.Value, 0) + for _, bt := range abiBytes { + cdcBytes = append(cdcBytes, cadence.UInt8(bt)) + } + encodedABI := cadence.NewArray( + cdcBytes, + ).WithType(cadence.NewVariableSizedArrayType(cadence.UInt8Type)) + + assert.Equal(t, + encodedABI, + result, + ) + assert.Equal(t, computation, uint(len(cdcBytes))) + + // Reset computation + computation = uint(0) + }) + + t.Run("ABI encode into `bytes[]` Solidity type", func(t *testing.T) { + script := []byte(` + import EVM from 0x1 + + access(all) + fun main(): [UInt8] { + let bytesArray: [EVM.EVMBytes] = [ + EVM.EVMBytes(value: [5]), + EVM.EVMBytes(value: [10]) + ] + return EVM.encodeABI([bytesArray]) + } + `) + + // Run script + result, err := rt.ExecuteScript( + runtime.Script{ + Source: script, + Arguments: [][]byte{}, + }, + runtime.Context{ + Interface: runtimeInterface, + Environment: scriptEnvironment, + Location: nextScriptLocation(), + }, + ) + require.NoError(t, err) + + abiBytes := []byte{ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x1, 0x5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xa, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + } + expected := "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001050000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010a00000000000000000000000000000000000000000000000000000000000000" + assert.Equal( + t, + expected, + hex.EncodeToString(abiBytes), + ) + cdcBytes := make([]cadence.Value, 0) + for _, bt := range abiBytes { + cdcBytes = append(cdcBytes, cadence.UInt8(bt)) + } + encodedABI := cadence.NewArray( + cdcBytes, + ).WithType(cadence.NewVariableSizedArrayType(cadence.UInt8Type)) + + assert.Equal(t, + encodedABI, + result, + ) + assert.Equal(t, computation, uint(len(cdcBytes))) + + // Reset computation + computation = uint(0) + }) + + t.Run("ABI encode into `bytes4` Solidity type", func(t *testing.T) { + script := []byte(` + import EVM from 0x1 + + access(all) + fun main(): [UInt8] { + let bytes: EVM.EVMBytes4 = EVM.EVMBytes4(value: [5, 10, 15, 20]) + return EVM.encodeABI([bytes]) + } + `) + + // Run script + result, err := rt.ExecuteScript( + runtime.Script{ + Source: script, + Arguments: [][]byte{}, + }, + runtime.Context{ + Interface: runtimeInterface, + Environment: scriptEnvironment, + Location: nextScriptLocation(), + }, + ) + require.NoError(t, err) + + abiBytes := []byte{ + 0x5, 0xa, 0xf, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + } + expected := "050a0f1400000000000000000000000000000000000000000000000000000000" + assert.Equal( + t, + expected, + hex.EncodeToString(abiBytes), + ) + cdcBytes := make([]cadence.Value, 0) + for _, bt := range abiBytes { + cdcBytes = append(cdcBytes, cadence.UInt8(bt)) + } + encodedABI := cadence.NewArray( + cdcBytes, + ).WithType(cadence.NewVariableSizedArrayType(cadence.UInt8Type)) + + assert.Equal(t, + encodedABI, + result, + ) + assert.Equal(t, computation, uint(len(cdcBytes))) + + // Reset computation + computation = uint(0) + }) + + t.Run("ABI encode into `bytes4[]` Solidity type", func(t *testing.T) { + script := []byte(` + import EVM from 0x1 + + access(all) + fun main(): [UInt8] { + let bytesArray: [EVM.EVMBytes4] = [ + EVM.EVMBytes4(value: [5, 10, 15, 20]), + EVM.EVMBytes4(value: [25, 30, 35, 40]) + ] + return EVM.encodeABI([bytesArray]) + } + `) + + // Run script + result, err := rt.ExecuteScript( + runtime.Script{ + Source: script, + Arguments: [][]byte{}, + }, + runtime.Context{ + Interface: runtimeInterface, + Environment: scriptEnvironment, + Location: nextScriptLocation(), + }, + ) + require.NoError(t, err) + + abiBytes := []byte{ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x2, 0x5, 0xa, 0xf, 0x14, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x19, 0x1e, 0x23, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + } + expected := "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002050a0f1400000000000000000000000000000000000000000000000000000000191e232800000000000000000000000000000000000000000000000000000000" + assert.Equal( + t, + expected, + hex.EncodeToString(abiBytes), + ) + cdcBytes := make([]cadence.Value, 0) + for _, bt := range abiBytes { + cdcBytes = append(cdcBytes, cadence.UInt8(bt)) + } + encodedABI := cadence.NewArray( + cdcBytes, + ).WithType(cadence.NewVariableSizedArrayType(cadence.UInt8Type)) + + assert.Equal(t, + encodedABI, + result, + ) + assert.Equal(t, computation, uint(len(cdcBytes))) + + // Reset computation + computation = uint(0) + }) + + t.Run("ABI encode into `bytes32` Solidity type", func(t *testing.T) { + script := []byte(` + import EVM from 0x1 + + access(all) + fun main(): [UInt8] { + let bytes: EVM.EVMBytes32 = EVM.EVMBytes32( + value: [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31, 32 + ] + ) + return EVM.encodeABI([bytes]) + } + `) + + // Run script + result, err := rt.ExecuteScript( + runtime.Script{ + Source: script, + Arguments: [][]byte{}, + }, + runtime.Context{ + Interface: runtimeInterface, + Environment: scriptEnvironment, + Location: nextScriptLocation(), + }, + ) + require.NoError(t, err) + + abiBytes := []byte{ + 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, + 0xd, 0xe, 0xf, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, + } + expected := "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20" + assert.Equal( + t, + expected, + hex.EncodeToString(abiBytes), + ) + cdcBytes := make([]cadence.Value, 0) + for _, bt := range abiBytes { + cdcBytes = append(cdcBytes, cadence.UInt8(bt)) + } + encodedABI := cadence.NewArray( + cdcBytes, + ).WithType(cadence.NewVariableSizedArrayType(cadence.UInt8Type)) + + assert.Equal(t, + encodedABI, + result, + ) + assert.Equal(t, computation, uint(len(cdcBytes))) + + // Reset computation + computation = uint(0) + }) + + t.Run("ABI encode into `bytes32[]` Solidity type", func(t *testing.T) { + script := []byte(` + import EVM from 0x1 + + access(all) + fun main(): [UInt8] { + let bytesA: EVM.EVMBytes32 = EVM.EVMBytes32( + value: [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31, 32 + ] + ) + let bytesB: EVM.EVMBytes32 = EVM.EVMBytes32( + value: [ + 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, + 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, + 2, 1 + ] + ) + let bytesArray: [EVM.EVMBytes32] = [bytesA, bytesB] + return EVM.encodeABI([bytesArray]) + } + `) + + // Run script + result, err := rt.ExecuteScript( + runtime.Script{ + Source: script, + Arguments: [][]byte{}, + }, + runtime.Context{ + Interface: runtimeInterface, + Environment: scriptEnvironment, + Location: nextScriptLocation(), + }, + ) + require.NoError(t, err) + + abiBytes := []byte{ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x1, + 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, + 0xf, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, + 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x20, 0x1f, 0x1e, 0x1d, + 0x1c, 0x1b, 0x1a, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, + 0x11, 0x10, 0xf, 0xe, 0xd, 0xc, 0xb, 0xa, 0x9, 0x8, 0x7, 0x6, 0x5, + 0x4, 0x3, 0x2, 0x1, + } + expected := "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000020102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a090807060504030201" + assert.Equal( + t, + expected, + hex.EncodeToString(abiBytes), + ) + cdcBytes := make([]cadence.Value, 0) + for _, bt := range abiBytes { + cdcBytes = append(cdcBytes, cadence.UInt8(bt)) + } + encodedABI := cadence.NewArray( + cdcBytes, + ).WithType(cadence.NewVariableSizedArrayType(cadence.UInt8Type)) + + assert.Equal(t, + encodedABI, + result, + ) + assert.Equal(t, computation, uint(len(cdcBytes))) + + // Reset computation + computation = uint(0) + }) } -func TestEVMEncodeABIComputationEmptyDynamicVariables(t *testing.T) { +func TestEVMEncodeABIBytesRoundtrip(t *testing.T) { t.Parallel() handler := &testContractHandler{} - contractsAddress := flow.BytesToAddress([]byte{0x1}) - transactionEnvironment := newEVMTransactionEnvironment(handler, contractsAddress) scriptEnvironment := newEVMScriptEnvironment(handler, contractsAddress) - rt := runtime.NewInterpreterRuntime(runtime.Config{}) - script := []byte(` - import EVM from 0x1 - - access(all) - fun main(): [UInt8] { - return EVM.encodeABI([ - "", - [[""], [] as [String]], - [] as [UInt8], - ["", "", ""] - ]) - } - `) - accountCodes := map[common.Location][]byte{} var events []cadence.Event @@ -595,7 +947,7 @@ func TestEVMEncodeABIComputationEmptyDynamicVariables(t *testing.T) { OnGetSigningAccounts: func() ([]runtime.Address, error) { return []runtime.Address{runtime.Address(contractsAddress)}, nil }, - OnResolveLocation: LocationResolver, + OnResolveLocation: newLocationResolver(contractsAddress), OnUpdateAccountContractCode: func(location common.AddressLocation, code []byte) error { accountCodes[location] = code return nil @@ -612,7 +964,7 @@ func TestEVMEncodeABIComputationEmptyDynamicVariables(t *testing.T) { return json.Decode(nil, b) }, OnMeterComputation: func(compKind common.ComputationKind, intensity uint) error { - if compKind == environment.ComputationKindEVMEncodeABI { + if compKind == environment.ComputationKindEVMDecodeABI { computation += intensity } return nil @@ -623,7 +975,6 @@ func TestEVMEncodeABIComputationEmptyDynamicVariables(t *testing.T) { nextScriptLocation := NewScriptLocationGenerator() // Deploy contracts - deployContracts( t, rt, @@ -633,18 +984,472 @@ func TestEVMEncodeABIComputationEmptyDynamicVariables(t *testing.T) { nextTransactionLocation, ) - // Run script + t.Run("ABI encode/decode into `bytes` Solidity type", func(t *testing.T) { + script := []byte(` + import EVM from 0x1 - result, err := rt.ExecuteScript( - runtime.Script{ - Source: script, - Arguments: [][]byte{}, - }, - runtime.Context{ - Interface: runtimeInterface, - Environment: scriptEnvironment, - Location: nextScriptLocation(), - }, + access(all) + fun main(): Bool { + let bytes: EVM.EVMBytes = EVM.EVMBytes(value: [5, 10, 15, 20, 25]) + let encodedData = EVM.encodeABI([bytes]) + let types = [Type()] + let values = EVM.decodeABI(types: types, data: encodedData) + + assert(values.length == 1) + let evmBytes = values[0] as! EVM.EVMBytes + assert(evmBytes.value == [5, 10, 15, 20, 25]) + + return true + } + `) + + // Run script + result, err := rt.ExecuteScript( + runtime.Script{ + Source: script, + Arguments: [][]byte{}, + }, + runtime.Context{ + Interface: runtimeInterface, + Environment: scriptEnvironment, + Location: nextScriptLocation(), + }, + ) + require.NoError(t, err) + + assert.Equal(t, + cadence.Bool(true), + result, + ) + + // Reset computation + computation = uint(0) + }) + + t.Run("ABI encode/decode into `bytes[]` Solidity type", func(t *testing.T) { + script := []byte(` + import EVM from 0x1 + + access(all) + fun main(): Bool { + let bytes: EVM.EVMBytes = EVM.EVMBytes(value: [5, 10, 15, 20, 25]) + let bytesArray: [EVM.EVMBytes] = [bytes] + let encodedData = EVM.encodeABI([bytesArray]) + let types = [Type<[EVM.EVMBytes]>()] + let values = EVM.decodeABI(types: types, data: encodedData) + + assert(values.length == 1) + let evmBytes = values[0] as! [EVM.EVMBytes] + assert(evmBytes[0].value == [5, 10, 15, 20, 25]) + + return true + } + `) + + // Run script + result, err := rt.ExecuteScript( + runtime.Script{ + Source: script, + Arguments: [][]byte{}, + }, + runtime.Context{ + Interface: runtimeInterface, + Environment: scriptEnvironment, + Location: nextScriptLocation(), + }, + ) + require.NoError(t, err) + + assert.Equal(t, + cadence.Bool(true), + result, + ) + + // Reset computation + computation = uint(0) + }) + + t.Run("ABI encode/decode into `bytes4` Solidity type", func(t *testing.T) { + script := []byte(` + import EVM from 0x1 + + access(all) + fun main(): Bool { + let bytes: EVM.EVMBytes4 = EVM.EVMBytes4(value: [5, 10, 15, 20]) + let encodedData = EVM.encodeABI([bytes]) + let types = [Type()] + let values = EVM.decodeABI(types: types, data: encodedData) + + assert(values.length == 1) + let evmBytes = values[0] as! EVM.EVMBytes4 + assert(evmBytes.value == [5, 10, 15, 20]) + + return true + } + `) + + // Run script + result, err := rt.ExecuteScript( + runtime.Script{ + Source: script, + Arguments: [][]byte{}, + }, + runtime.Context{ + Interface: runtimeInterface, + Environment: scriptEnvironment, + Location: nextScriptLocation(), + }, + ) + require.NoError(t, err) + + assert.Equal(t, + cadence.Bool(true), + result, + ) + + // Reset computation + computation = uint(0) + }) + + t.Run("ABI encode/decode into `bytes4[]` Solidity type", func(t *testing.T) { + script := []byte(` + import EVM from 0x1 + + access(all) + fun main(): Bool { + let bytes: EVM.EVMBytes4 = EVM.EVMBytes4(value: [5, 10, 15, 20]) + let bytesArray: [EVM.EVMBytes4] = [bytes] + let encodedData = EVM.encodeABI([bytesArray]) + let types = [Type<[EVM.EVMBytes4]>()] + let values = EVM.decodeABI(types: types, data: encodedData) + + assert(values.length == 1) + let evmBytes = values[0] as! [EVM.EVMBytes4] + assert(evmBytes[0].value == [5, 10, 15, 20]) + + return true + } + `) + + // Run script + result, err := rt.ExecuteScript( + runtime.Script{ + Source: script, + Arguments: [][]byte{}, + }, + runtime.Context{ + Interface: runtimeInterface, + Environment: scriptEnvironment, + Location: nextScriptLocation(), + }, + ) + require.NoError(t, err) + + assert.Equal(t, + cadence.Bool(true), + result, + ) + + // Reset computation + computation = uint(0) + }) + + t.Run("ABI encode/decode into `bytes32` Solidity type", func(t *testing.T) { + script := []byte(` + import EVM from 0x1 + + access(all) + fun main(): Bool { + let bytes: EVM.EVMBytes32 = EVM.EVMBytes32( + value: [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31, 32 + ] + ) + let encodedData = EVM.encodeABI([bytes]) + let types = [Type()] + let values = EVM.decodeABI(types: types, data: encodedData) + + assert(values.length == 1) + let evmBytes = values[0] as! EVM.EVMBytes32 + assert(evmBytes.value == [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31, 32 + ]) + + return true + } + `) + + // Run script + result, err := rt.ExecuteScript( + runtime.Script{ + Source: script, + Arguments: [][]byte{}, + }, + runtime.Context{ + Interface: runtimeInterface, + Environment: scriptEnvironment, + Location: nextScriptLocation(), + }, + ) + require.NoError(t, err) + + assert.Equal(t, + cadence.Bool(true), + result, + ) + + // Reset computation + computation = uint(0) + }) + + t.Run("ABI encode/decode into `bytes32[]` Solidity type", func(t *testing.T) { + script := []byte(` + import EVM from 0x1 + + access(all) + fun main(): Bool { + let bytes: EVM.EVMBytes32 = EVM.EVMBytes32( + value: [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31, 32 + ] + ) + let bytesArray: [EVM.EVMBytes32] = [bytes] + let encodedData = EVM.encodeABI([bytesArray]) + let types = [Type<[EVM.EVMBytes32]>()] + let values = EVM.decodeABI(types: types, data: encodedData) + + assert(values.length == 1) + let evmBytes = values[0] as! [EVM.EVMBytes32] + assert(evmBytes[0].value == [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31, 32 + ]) + + return true + } + `) + + // Run script + result, err := rt.ExecuteScript( + runtime.Script{ + Source: script, + Arguments: [][]byte{}, + }, + runtime.Context{ + Interface: runtimeInterface, + Environment: scriptEnvironment, + Location: nextScriptLocation(), + }, + ) + require.NoError(t, err) + + assert.Equal(t, + cadence.Bool(true), + result, + ) + + // Reset computation + computation = uint(0) + }) +} + +func TestEVMEncodeABIComputation(t *testing.T) { + + t.Parallel() + + handler := &testContractHandler{} + + contractsAddress := flow.BytesToAddress([]byte{0x1}) + + transactionEnvironment := newEVMTransactionEnvironment(handler, contractsAddress) + scriptEnvironment := newEVMScriptEnvironment(handler, contractsAddress) + + rt := runtime.NewInterpreterRuntime(runtime.Config{}) + + script := []byte(` + import EVM from 0x1 + + access(all) + fun main(): [UInt8] { + let address = EVM.EVMAddress( + bytes: "7A58c0Be72BE218B41C608b7Fe7C5bB630736C71" + .decodeHex() + .toConstantSized<[UInt8; 20]>()! + ) + let arr: [UInt8] = [1, 2, 3, 4, 5] + + return EVM.encodeABI([ + "John Doe", + UInt64(33), + false, + address, + [arr], + ["one", "two", "three"] + ]) + } + `) + + accountCodes := map[common.Location][]byte{} + var events []cadence.Event + + computation := uint(0) + runtimeInterface := &TestRuntimeInterface{ + Storage: NewTestLedger(nil, nil), + OnGetSigningAccounts: func() ([]runtime.Address, error) { + return []runtime.Address{runtime.Address(contractsAddress)}, nil + }, + OnResolveLocation: newLocationResolver(contractsAddress), + OnUpdateAccountContractCode: func(location common.AddressLocation, code []byte) error { + accountCodes[location] = code + return nil + }, + OnGetAccountContractCode: func(location common.AddressLocation) (code []byte, err error) { + code = accountCodes[location] + return code, nil + }, + OnEmitEvent: func(event cadence.Event) error { + events = append(events, event) + return nil + }, + OnDecodeArgument: func(b []byte, t cadence.Type) (cadence.Value, error) { + return json.Decode(nil, b) + }, + OnMeterComputation: func(compKind common.ComputationKind, intensity uint) error { + if compKind == environment.ComputationKindEVMEncodeABI { + computation += intensity + } + return nil + }, + } + + nextTransactionLocation := NewTransactionLocationGenerator() + nextScriptLocation := NewScriptLocationGenerator() + + // Deploy contracts + + deployContracts( + t, + rt, + contractsAddress, + runtimeInterface, + transactionEnvironment, + nextTransactionLocation, + ) + + // Run script + + result, err := rt.ExecuteScript( + runtime.Script{ + Source: script, + Arguments: [][]byte{}, + }, + runtime.Context{ + Interface: runtimeInterface, + Environment: scriptEnvironment, + Location: nextScriptLocation(), + }, + ) + require.NoError(t, err) + + cdcBytes, ok := result.(cadence.Array) + require.True(t, ok) + // computation & len(cdcBytes.Values) is equal to 832 + assert.Equal(t, computation, uint(len(cdcBytes.Values))) +} + +func TestEVMEncodeABIComputationEmptyDynamicVariables(t *testing.T) { + + t.Parallel() + + handler := &testContractHandler{} + + contractsAddress := flow.BytesToAddress([]byte{0x1}) + + transactionEnvironment := newEVMTransactionEnvironment(handler, contractsAddress) + scriptEnvironment := newEVMScriptEnvironment(handler, contractsAddress) + + rt := runtime.NewInterpreterRuntime(runtime.Config{}) + + script := []byte(` + import EVM from 0x1 + + access(all) + fun main(): [UInt8] { + return EVM.encodeABI([ + "", + [[""], [] as [String]], + [] as [UInt8], + ["", "", ""] + ]) + } + `) + + accountCodes := map[common.Location][]byte{} + var events []cadence.Event + + computation := uint(0) + runtimeInterface := &TestRuntimeInterface{ + Storage: NewTestLedger(nil, nil), + OnGetSigningAccounts: func() ([]runtime.Address, error) { + return []runtime.Address{runtime.Address(contractsAddress)}, nil + }, + OnResolveLocation: newLocationResolver(contractsAddress), + OnUpdateAccountContractCode: func(location common.AddressLocation, code []byte) error { + accountCodes[location] = code + return nil + }, + OnGetAccountContractCode: func(location common.AddressLocation) (code []byte, err error) { + code = accountCodes[location] + return code, nil + }, + OnEmitEvent: func(event cadence.Event) error { + events = append(events, event) + return nil + }, + OnDecodeArgument: func(b []byte, t cadence.Type) (cadence.Value, error) { + return json.Decode(nil, b) + }, + OnMeterComputation: func(compKind common.ComputationKind, intensity uint) error { + if compKind == environment.ComputationKindEVMEncodeABI { + computation += intensity + } + return nil + }, + } + + nextTransactionLocation := NewTransactionLocationGenerator() + nextScriptLocation := NewScriptLocationGenerator() + + // Deploy contracts + + deployContracts( + t, + rt, + contractsAddress, + runtimeInterface, + transactionEnvironment, + nextTransactionLocation, + ) + + // Run script + + result, err := rt.ExecuteScript( + runtime.Script{ + Source: script, + Arguments: [][]byte{}, + }, + runtime.Context{ + Interface: runtimeInterface, + Environment: scriptEnvironment, + Location: nextScriptLocation(), + }, ) require.NoError(t, err) @@ -699,7 +1504,7 @@ func TestEVMEncodeABIComputationDynamicVariablesAboveChunkSize(t *testing.T) { OnGetSigningAccounts: func() ([]runtime.Address, error) { return []runtime.Address{runtime.Address(contractsAddress)}, nil }, - OnResolveLocation: LocationResolver, + OnResolveLocation: newLocationResolver(contractsAddress), OnUpdateAccountContractCode: func(location common.AddressLocation, code []byte) error { accountCodes[location] = code return nil @@ -797,7 +1602,7 @@ func TestEVMDecodeABI(t *testing.T) { OnGetSigningAccounts: func() ([]runtime.Address, error) { return []runtime.Address{runtime.Address(contractsAddress)}, nil }, - OnResolveLocation: LocationResolver, + OnResolveLocation: newLocationResolver(contractsAddress), OnUpdateAccountContractCode: func(location common.AddressLocation, code []byte) error { accountCodes[location] = code return nil @@ -924,69 +1729,329 @@ func TestEVMDecodeABIComputation(t *testing.T) { accountCodes := map[common.Location][]byte{} var events []cadence.Event - computation := uint(0) - runtimeInterface := &TestRuntimeInterface{ - Storage: NewTestLedger(nil, nil), - OnGetSigningAccounts: func() ([]runtime.Address, error) { - return []runtime.Address{runtime.Address(contractsAddress)}, nil - }, - OnResolveLocation: LocationResolver, - OnUpdateAccountContractCode: func(location common.AddressLocation, code []byte) error { - accountCodes[location] = code - return nil - }, - OnGetAccountContractCode: func(location common.AddressLocation) (code []byte, err error) { - code = accountCodes[location] - return code, nil - }, - OnEmitEvent: func(event cadence.Event) error { - events = append(events, event) - return nil - }, - OnDecodeArgument: func(b []byte, t cadence.Type) (cadence.Value, error) { - return json.Decode(nil, b) - }, - OnMeterComputation: func(compKind common.ComputationKind, intensity uint) error { - if compKind == environment.ComputationKindEVMDecodeABI { - computation += intensity - } - return nil - }, - } + computation := uint(0) + runtimeInterface := &TestRuntimeInterface{ + Storage: NewTestLedger(nil, nil), + OnGetSigningAccounts: func() ([]runtime.Address, error) { + return []runtime.Address{runtime.Address(contractsAddress)}, nil + }, + OnResolveLocation: newLocationResolver(contractsAddress), + OnUpdateAccountContractCode: func(location common.AddressLocation, code []byte) error { + accountCodes[location] = code + return nil + }, + OnGetAccountContractCode: func(location common.AddressLocation) (code []byte, err error) { + code = accountCodes[location] + return code, nil + }, + OnEmitEvent: func(event cadence.Event) error { + events = append(events, event) + return nil + }, + OnDecodeArgument: func(b []byte, t cadence.Type) (cadence.Value, error) { + return json.Decode(nil, b) + }, + OnMeterComputation: func(compKind common.ComputationKind, intensity uint) error { + if compKind == environment.ComputationKindEVMDecodeABI { + computation += intensity + } + return nil + }, + } + + nextTransactionLocation := NewTransactionLocationGenerator() + nextScriptLocation := NewScriptLocationGenerator() + + // Deploy contracts + + deployContracts( + t, + rt, + contractsAddress, + runtimeInterface, + transactionEnvironment, + nextTransactionLocation, + ) + + // Run script + + result, err := rt.ExecuteScript( + runtime.Script{ + Source: script, + Arguments: [][]byte{}, + }, + runtime.Context{ + Interface: runtimeInterface, + Environment: scriptEnvironment, + Location: nextScriptLocation(), + }, + ) + require.NoError(t, err) + + cdcBytes, ok := result.(cadence.Array) + require.True(t, ok) + // computation & len(cdcBytes.Values) is equal to 832 + assert.Equal(t, computation, uint(len(cdcBytes.Values))) +} + +func TestEVMEncodeDecodeABIRoundtripForUintIntTypes(t *testing.T) { + + t.Parallel() + + handler := &testContractHandler{} + contractsAddress := flow.BytesToAddress([]byte{0x1}) + transactionEnvironment := newEVMTransactionEnvironment(handler, contractsAddress) + scriptEnvironment := newEVMScriptEnvironment(handler, contractsAddress) + rt := runtime.NewInterpreterRuntime(runtime.Config{}) + + accountCodes := map[common.Location][]byte{} + var events []cadence.Event + + runtimeInterface := &TestRuntimeInterface{ + Storage: NewTestLedger(nil, nil), + OnGetSigningAccounts: func() ([]runtime.Address, error) { + return []runtime.Address{runtime.Address(contractsAddress)}, nil + }, + OnResolveLocation: newLocationResolver(contractsAddress), + OnUpdateAccountContractCode: func(location common.AddressLocation, code []byte) error { + accountCodes[location] = code + return nil + }, + OnGetAccountContractCode: func(location common.AddressLocation) (code []byte, err error) { + code = accountCodes[location] + return code, nil + }, + OnEmitEvent: func(event cadence.Event) error { + events = append(events, event) + return nil + }, + OnDecodeArgument: func(b []byte, t cadence.Type) (cadence.Value, error) { + return json.Decode(nil, b) + }, + OnHash: func( + data []byte, + tag string, + hashAlgorithm runtime.HashAlgorithm, + ) ([]byte, error) { + return crypto.Keccak256(data), nil + }, + } + + nextTransactionLocation := NewTransactionLocationGenerator() + nextScriptLocation := NewScriptLocationGenerator() + + // Deploy contracts + + deployContracts( + t, + rt, + contractsAddress, + runtimeInterface, + transactionEnvironment, + nextTransactionLocation, + ) + + t.Run("with values between the boundaries", func(t *testing.T) { + + script := []byte(` + import EVM from 0x1 + + access(all) + fun main(): Bool { + // Check UInt/Int encode/decode + let amount: UInt256 = 18446744073709551615 + let minBalance: Int256 = -18446744073709551615 + let data = EVM.encodeABIWithSignature( + "withdraw(uint,int)", + [UInt(amount), Int(minBalance)] + ) + let values = EVM.decodeABIWithSignature( + "withdraw(uint,int)", + types: [Type(), Type()], + data: data + ) + assert((values[0] as! UInt) == UInt(amount)) + assert((values[1] as! Int) == Int(minBalance)) + + return true + } + `) + + // Run script + + result, err := rt.ExecuteScript( + runtime.Script{ + Source: script, + Arguments: [][]byte{}, + }, + runtime.Context{ + Interface: runtimeInterface, + Environment: scriptEnvironment, + Location: nextScriptLocation(), + }, + ) + require.NoError(t, err) + + assert.Equal(t, cadence.Bool(true), result) + }) + + t.Run("with values at the boundaries", func(t *testing.T) { + + script := []byte(` + import EVM from 0x1 + + access(all) + fun main(): Bool { + // Check UInt*/Int* encode/decode + let data = EVM.encodeABIWithSignature( + "withdraw(uint,int,uint,int)", + [UInt(UInt256.max), Int(Int256.max),UInt(UInt256.min), Int(Int256.min)] + ) + let values = EVM.decodeABIWithSignature( + "withdraw(uint,int,uint,int)", + types: [Type(), Type(),Type(), Type()], + data: data + ) + assert((values[0] as! UInt) == UInt(UInt256.max)) + assert((values[1] as! Int) == Int(Int256.max)) + assert((values[2] as! UInt) == UInt(UInt256.min)) + assert((values[3] as! Int) == Int(Int256.min)) + + return true + } + `) + + // Run script + + result, err := rt.ExecuteScript( + runtime.Script{ + Source: script, + Arguments: [][]byte{}, + }, + runtime.Context{ + Interface: runtimeInterface, + Environment: scriptEnvironment, + Location: nextScriptLocation(), + }, + ) + require.NoError(t, err) + + assert.Equal(t, cadence.Bool(true), result) + }) + + t.Run("with UInt values outside the boundaries", func(t *testing.T) { + + script := []byte(` + import EVM from 0x1 + + access(all) + fun main(): Bool { + let data = EVM.encodeABIWithSignature( + "withdraw(uint)", + [UInt(UInt256.max)+10] + ) + + return true + } + `) + + // Run script + + _, err := rt.ExecuteScript( + runtime.Script{ + Source: script, + Arguments: [][]byte{}, + }, + runtime.Context{ + Interface: runtimeInterface, + Environment: scriptEnvironment, + Location: nextScriptLocation(), + }, + ) + require.Error(t, err) + + assert.ErrorContains( + t, + err, + "failed to ABI encode value of type UInt: value outside the boundaries of uint256", + ) + }) + + t.Run("with Int values outside the max boundary", func(t *testing.T) { + + script := []byte(` + import EVM from 0x1 + + access(all) + fun main(): Bool { + let data = EVM.encodeABIWithSignature( + "withdraw(int)", + [Int(Int256.max)+10] + ) + + return true + } + `) + + // Run script + + _, err := rt.ExecuteScript( + runtime.Script{ + Source: script, + Arguments: [][]byte{}, + }, + runtime.Context{ + Interface: runtimeInterface, + Environment: scriptEnvironment, + Location: nextScriptLocation(), + }, + ) + require.Error(t, err) + + assert.ErrorContains( + t, + err, + "failed to ABI encode value of type Int: value outside the boundaries of int256", + ) + }) - nextTransactionLocation := NewTransactionLocationGenerator() - nextScriptLocation := NewScriptLocationGenerator() + t.Run("with Int values outside the min boundary", func(t *testing.T) { - // Deploy contracts + script := []byte(` + import EVM from 0x1 - deployContracts( - t, - rt, - contractsAddress, - runtimeInterface, - transactionEnvironment, - nextTransactionLocation, - ) + access(all) + fun main(): Bool { + let data = EVM.encodeABIWithSignature( + "withdraw(int)", + [Int(Int256.min)-10] + ) - // Run script + return true + } + `) - result, err := rt.ExecuteScript( - runtime.Script{ - Source: script, - Arguments: [][]byte{}, - }, - runtime.Context{ - Interface: runtimeInterface, - Environment: scriptEnvironment, - Location: nextScriptLocation(), - }, - ) - require.NoError(t, err) + // Run script - cdcBytes, ok := result.(cadence.Array) - require.True(t, ok) - // computation & len(cdcBytes.Values) is equal to 832 - assert.Equal(t, computation, uint(len(cdcBytes.Values))) + _, err := rt.ExecuteScript( + runtime.Script{ + Source: script, + Arguments: [][]byte{}, + }, + runtime.Context{ + Interface: runtimeInterface, + Environment: scriptEnvironment, + Location: nextScriptLocation(), + }, + ) + require.Error(t, err) + + assert.ErrorContains( + t, + err, + "failed to ABI encode value of type Int: value outside the boundaries of int256", + ) + }) } func TestEVMEncodeDecodeABIRoundtrip(t *testing.T) { @@ -1043,7 +2108,9 @@ func TestEVMEncodeDecodeABIRoundtrip(t *testing.T) { Int32(-33), Int64(-33), Int128(-33), - Int256(-33) + Int256(-33), + UInt(33), + Int(-33) ]) values = EVM.decodeABI( types: [ @@ -1058,7 +2125,9 @@ func TestEVMEncodeDecodeABIRoundtrip(t *testing.T) { Type(), Type(), Type(), - Type() + Type(), + Type(), + Type() ], data: data ) @@ -1074,6 +2143,8 @@ func TestEVMEncodeDecodeABIRoundtrip(t *testing.T) { assert((values[9] as! Int64) == -33) assert((values[10] as! Int128) == -33) assert((values[11] as! Int256) == -33) + assert((values[12] as! UInt) == 33) + assert((values[13] as! Int) == -33) // Check variable-size array of leaf types encode/decode data = EVM.encodeABI([ @@ -1207,7 +2278,7 @@ func TestEVMEncodeDecodeABIRoundtrip(t *testing.T) { OnGetSigningAccounts: func() ([]runtime.Address, error) { return []runtime.Address{runtime.Address(contractsAddress)}, nil }, - OnResolveLocation: LocationResolver, + OnResolveLocation: newLocationResolver(contractsAddress), OnUpdateAccountContractCode: func(location common.AddressLocation, code []byte) error { accountCodes[location] = code return nil @@ -1285,7 +2356,7 @@ func TestEVMEncodeDecodeABIErrors(t *testing.T) { OnGetSigningAccounts: func() ([]runtime.Address, error) { return []runtime.Address{runtime.Address(contractsAddress)}, nil }, - OnResolveLocation: LocationResolver, + OnResolveLocation: newLocationResolver(contractsAddress), OnUpdateAccountContractCode: func(location common.AddressLocation, code []byte) error { accountCodes[location] = code return nil @@ -1342,7 +2413,7 @@ func TestEVMEncodeDecodeABIErrors(t *testing.T) { Location: nextScriptLocation(), }, ) - utils.RequireError(t, err) + RequireError(t, err) assert.ErrorContains( t, err, @@ -1371,7 +2442,7 @@ func TestEVMEncodeDecodeABIErrors(t *testing.T) { OnGetSigningAccounts: func() ([]runtime.Address, error) { return []runtime.Address{runtime.Address(contractsAddress)}, nil }, - OnResolveLocation: LocationResolver, + OnResolveLocation: newLocationResolver(contractsAddress), OnUpdateAccountContractCode: func(location common.AddressLocation, code []byte) error { accountCodes[location] = code return nil @@ -1427,7 +2498,7 @@ func TestEVMEncodeDecodeABIErrors(t *testing.T) { Location: nextScriptLocation(), }, ) - utils.RequireError(t, err) + RequireError(t, err) assert.ErrorContains( t, err, @@ -1456,7 +2527,7 @@ func TestEVMEncodeDecodeABIErrors(t *testing.T) { OnGetSigningAccounts: func() ([]runtime.Address, error) { return []runtime.Address{runtime.Address(contractsAddress)}, nil }, - OnResolveLocation: LocationResolver, + OnResolveLocation: newLocationResolver(contractsAddress), OnUpdateAccountContractCode: func(location common.AddressLocation, code []byte) error { accountCodes[location] = code return nil @@ -1513,7 +2584,7 @@ func TestEVMEncodeDecodeABIErrors(t *testing.T) { Location: nextScriptLocation(), }, ) - utils.RequireError(t, err) + RequireError(t, err) assert.ErrorContains( t, err, @@ -1542,7 +2613,7 @@ func TestEVMEncodeDecodeABIErrors(t *testing.T) { OnGetSigningAccounts: func() ([]runtime.Address, error) { return []runtime.Address{runtime.Address(contractsAddress)}, nil }, - OnResolveLocation: LocationResolver, + OnResolveLocation: newLocationResolver(contractsAddress), OnUpdateAccountContractCode: func(location common.AddressLocation, code []byte) error { accountCodes[location] = code return nil @@ -1599,7 +2670,7 @@ func TestEVMEncodeDecodeABIErrors(t *testing.T) { Location: nextScriptLocation(), }, ) - utils.RequireError(t, err) + RequireError(t, err) assert.ErrorContains( t, err, @@ -1628,7 +2699,7 @@ func TestEVMEncodeDecodeABIErrors(t *testing.T) { OnGetSigningAccounts: func() ([]runtime.Address, error) { return []runtime.Address{runtime.Address(contractsAddress)}, nil }, - OnResolveLocation: LocationResolver, + OnResolveLocation: newLocationResolver(contractsAddress), OnUpdateAccountContractCode: func(location common.AddressLocation, code []byte) error { accountCodes[location] = code return nil @@ -1695,7 +2766,7 @@ func TestEVMEncodeDecodeABIErrors(t *testing.T) { Location: nextScriptLocation(), }, ) - utils.RequireError(t, err) + RequireError(t, err) assert.ErrorContains( t, err, @@ -1724,7 +2795,7 @@ func TestEVMEncodeDecodeABIErrors(t *testing.T) { OnGetSigningAccounts: func() ([]runtime.Address, error) { return []runtime.Address{runtime.Address(contractsAddress)}, nil }, - OnResolveLocation: LocationResolver, + OnResolveLocation: newLocationResolver(contractsAddress), OnUpdateAccountContractCode: func(location common.AddressLocation, code []byte) error { accountCodes[location] = code return nil @@ -1781,7 +2852,7 @@ func TestEVMEncodeDecodeABIErrors(t *testing.T) { Location: nextScriptLocation(), }, ) - utils.RequireError(t, err) + RequireError(t, err) assert.ErrorContains( t, err, @@ -1810,7 +2881,7 @@ func TestEVMEncodeDecodeABIErrors(t *testing.T) { OnGetSigningAccounts: func() ([]runtime.Address, error) { return []runtime.Address{runtime.Address(contractsAddress)}, nil }, - OnResolveLocation: LocationResolver, + OnResolveLocation: newLocationResolver(contractsAddress), OnUpdateAccountContractCode: func(location common.AddressLocation, code []byte) error { accountCodes[location] = code return nil @@ -1867,7 +2938,7 @@ func TestEVMEncodeDecodeABIErrors(t *testing.T) { Location: nextScriptLocation(), }, ) - utils.RequireError(t, err) + RequireError(t, err) assert.ErrorContains( t, err, @@ -1896,7 +2967,7 @@ func TestEVMEncodeDecodeABIErrors(t *testing.T) { OnGetSigningAccounts: func() ([]runtime.Address, error) { return []runtime.Address{runtime.Address(contractsAddress)}, nil }, - OnResolveLocation: LocationResolver, + OnResolveLocation: newLocationResolver(contractsAddress), OnUpdateAccountContractCode: func(location common.AddressLocation, code []byte) error { accountCodes[location] = code return nil @@ -1953,7 +3024,7 @@ func TestEVMEncodeDecodeABIErrors(t *testing.T) { Location: nextScriptLocation(), }, ) - utils.RequireError(t, err) + RequireError(t, err) assert.ErrorContains( t, err, @@ -1982,7 +3053,7 @@ func TestEVMEncodeDecodeABIErrors(t *testing.T) { OnGetSigningAccounts: func() ([]runtime.Address, error) { return []runtime.Address{runtime.Address(contractsAddress)}, nil }, - OnResolveLocation: LocationResolver, + OnResolveLocation: newLocationResolver(contractsAddress), OnUpdateAccountContractCode: func(location common.AddressLocation, code []byte) error { accountCodes[location] = code return nil @@ -2039,7 +3110,7 @@ func TestEVMEncodeDecodeABIErrors(t *testing.T) { Location: nextScriptLocation(), }, ) - utils.RequireError(t, err) + RequireError(t, err) assert.ErrorContains( t, err, @@ -2068,7 +3139,7 @@ func TestEVMEncodeDecodeABIErrors(t *testing.T) { OnGetSigningAccounts: func() ([]runtime.Address, error) { return []runtime.Address{runtime.Address(contractsAddress)}, nil }, - OnResolveLocation: LocationResolver, + OnResolveLocation: newLocationResolver(contractsAddress), OnUpdateAccountContractCode: func(location common.AddressLocation, code []byte) error { accountCodes[location] = code return nil @@ -2125,7 +3196,7 @@ func TestEVMEncodeDecodeABIErrors(t *testing.T) { Location: nextScriptLocation(), }, ) - utils.RequireError(t, err) + RequireError(t, err) assert.ErrorContains( t, err, @@ -2154,7 +3225,7 @@ func TestEVMEncodeDecodeABIErrors(t *testing.T) { OnGetSigningAccounts: func() ([]runtime.Address, error) { return []runtime.Address{runtime.Address(contractsAddress)}, nil }, - OnResolveLocation: LocationResolver, + OnResolveLocation: newLocationResolver(contractsAddress), OnUpdateAccountContractCode: func(location common.AddressLocation, code []byte) error { accountCodes[location] = code return nil @@ -2221,7 +3292,7 @@ func TestEVMEncodeDecodeABIErrors(t *testing.T) { Location: nextScriptLocation(), }, ) - utils.RequireError(t, err) + RequireError(t, err) assert.ErrorContains( t, err, @@ -2270,7 +3341,7 @@ func TestEVMEncodeABIWithSignature(t *testing.T) { OnGetSigningAccounts: func() ([]runtime.Address, error) { return []runtime.Address{runtime.Address(contractsAddress)}, nil }, - OnResolveLocation: LocationResolver, + OnResolveLocation: newLocationResolver(contractsAddress), OnUpdateAccountContractCode: func(location common.AddressLocation, code []byte) error { accountCodes[location] = code return nil @@ -2403,7 +3474,7 @@ func TestEVMDecodeABIWithSignature(t *testing.T) { OnGetSigningAccounts: func() ([]runtime.Address, error) { return []runtime.Address{runtime.Address(contractsAddress)}, nil }, - OnResolveLocation: LocationResolver, + OnResolveLocation: newLocationResolver(contractsAddress), OnUpdateAccountContractCode: func(location common.AddressLocation, code []byte) error { accountCodes[location] = code return nil @@ -2523,7 +3594,7 @@ func TestEVMDecodeABIWithSignatureMismatch(t *testing.T) { OnGetSigningAccounts: func() ([]runtime.Address, error) { return []runtime.Address{runtime.Address(contractsAddress)}, nil }, - OnResolveLocation: LocationResolver, + OnResolveLocation: newLocationResolver(contractsAddress), OnUpdateAccountContractCode: func(location common.AddressLocation, code []byte) error { accountCodes[location] = code return nil @@ -2626,7 +3697,7 @@ func TestEVMAddressConstructionAndReturn(t *testing.T) { OnGetSigningAccounts: func() ([]runtime.Address, error) { return []runtime.Address{runtime.Address(contractsAddress)}, nil }, - OnResolveLocation: LocationResolver, + OnResolveLocation: newLocationResolver(contractsAddress), OnUpdateAccountContractCode: func(location common.AddressLocation, code []byte) error { accountCodes[location] = code return nil @@ -2728,7 +3799,7 @@ func TestEVMAddressSerializationAndDeserialization(t *testing.T) { OnGetSigningAccounts: func() ([]runtime.Address, error) { return []runtime.Address{runtime.Address(contractsAddress)}, nil }, - OnResolveLocation: LocationResolver, + OnResolveLocation: newLocationResolver(contractsAddress), OnUpdateAccountContractCode: func(location common.AddressLocation, code []byte) error { accountCodes[location] = code return nil @@ -2905,7 +3976,7 @@ func TestBalanceConstructionAndReturn(t *testing.T) { OnGetSigningAccounts: func() ([]runtime.Address, error) { return []runtime.Address{runtime.Address(contractsAddress)}, nil }, - OnResolveLocation: LocationResolver, + OnResolveLocation: newLocationResolver(contractsAddress), OnUpdateAccountContractCode: func(location common.AddressLocation, code []byte) error { accountCodes[location] = code return nil @@ -3035,7 +4106,7 @@ func TestEVMRun(t *testing.T) { OnGetSigningAccounts: func() ([]runtime.Address, error) { return []runtime.Address{runtime.Address(contractsAddress)}, nil }, - OnResolveLocation: LocationResolver, + OnResolveLocation: newLocationResolver(contractsAddress), OnUpdateAccountContractCode: func(location common.AddressLocation, code []byte) error { accountCodes[location] = code return nil @@ -3152,7 +4223,7 @@ func TestEVMDryRun(t *testing.T) { OnGetSigningAccounts: func() ([]runtime.Address, error) { return []runtime.Address{runtime.Address(contractsAddress)}, nil }, - OnResolveLocation: LocationResolver, + OnResolveLocation: newLocationResolver(contractsAddress), OnUpdateAccountContractCode: func(location common.AddressLocation, code []byte) error { accountCodes[location] = code return nil @@ -3291,7 +4362,7 @@ func TestEVMBatchRun(t *testing.T) { OnGetSigningAccounts: func() ([]runtime.Address, error) { return []runtime.Address{runtime.Address(contractsAddress)}, nil }, - OnResolveLocation: LocationResolver, + OnResolveLocation: newLocationResolver(contractsAddress), OnUpdateAccountContractCode: func(location common.AddressLocation, code []byte) error { accountCodes[location] = code return nil @@ -3391,7 +4462,7 @@ func TestEVMCreateCadenceOwnedAccount(t *testing.T) { OnGetSigningAccounts: func() ([]runtime.Address, error) { return []runtime.Address{runtime.Address(contractsAddress)}, nil }, - OnResolveLocation: LocationResolver, + OnResolveLocation: newLocationResolver(contractsAddress), OnUpdateAccountContractCode: func(location common.AddressLocation, code []byte) error { accountCodes[location] = code return nil @@ -3547,7 +4618,7 @@ func TestCadenceOwnedAccountCall(t *testing.T) { OnGetSigningAccounts: func() ([]runtime.Address, error) { return []runtime.Address{runtime.Address(contractsAddress)}, nil }, - OnResolveLocation: LocationResolver, + OnResolveLocation: newLocationResolver(contractsAddress), OnUpdateAccountContractCode: func(location common.AddressLocation, code []byte) error { accountCodes[location] = code return nil @@ -3666,7 +4737,7 @@ func TestEVMAddressDeposit(t *testing.T) { OnGetSigningAccounts: func() ([]runtime.Address, error) { return []runtime.Address{runtime.Address(contractsAddress)}, nil }, - OnResolveLocation: LocationResolver, + OnResolveLocation: newLocationResolver(contractsAddress), OnUpdateAccountContractCode: func(location common.AddressLocation, code []byte) error { accountCodes[location] = code return nil @@ -3779,7 +4850,7 @@ func TestCOADeposit(t *testing.T) { OnGetSigningAccounts: func() ([]runtime.Address, error) { return []runtime.Address{runtime.Address(contractsAddress)}, nil }, - OnResolveLocation: LocationResolver, + OnResolveLocation: newLocationResolver(contractsAddress), OnUpdateAccountContractCode: func(location common.AddressLocation, code []byte) error { accountCodes[location] = code return nil @@ -3950,7 +5021,7 @@ func TestCadenceOwnedAccountWithdraw(t *testing.T) { OnGetSigningAccounts: func() ([]runtime.Address, error) { return []runtime.Address{runtime.Address(contractsAddress)}, nil }, - OnResolveLocation: LocationResolver, + OnResolveLocation: newLocationResolver(contractsAddress), OnUpdateAccountContractCode: func(location common.AddressLocation, code []byte) error { accountCodes[location] = code return nil @@ -4103,7 +5174,7 @@ func TestCadenceOwnedAccountDeploy(t *testing.T) { OnGetSigningAccounts: func() ([]runtime.Address, error) { return []runtime.Address{runtime.Address(contractsAddress)}, nil }, - OnResolveLocation: LocationResolver, + OnResolveLocation: newLocationResolver(contractsAddress), OnUpdateAccountContractCode: func(location common.AddressLocation, code []byte) error { accountCodes[location] = code return nil @@ -4178,7 +5249,7 @@ func RunEVMScript( OnGetSigningAccounts: func() ([]runtime.Address, error) { return []runtime.Address{runtime.Address(contractsAddress)}, nil }, - OnResolveLocation: LocationResolver, + OnResolveLocation: newLocationResolver(contractsAddress), OnUpdateAccountContractCode: func(location common.AddressLocation, code []byte) error { accountCodes[location] = code return nil @@ -4384,91 +5455,72 @@ func TestEVMAccountCodeHash(t *testing.T) { func TestEVMValidateCOAOwnershipProof(t *testing.T) { t.Parallel() - contractsAddress := flow.BytesToAddress([]byte{0x1}) - proof := &types.COAOwnershipProofInContext{ - COAOwnershipProof: types.COAOwnershipProof{ - Address: types.FlowAddress(contractsAddress), - CapabilityPath: "coa", - Signatures: []types.Signature{[]byte("signature")}, - KeyIndices: []uint64{0}, - }, - SignedData: []byte("signedData"), - EVMAddress: RandomAddress(t), - } - - handler := &testContractHandler{ - deployCOA: func(_ uint64) types.Address { - return proof.EVMAddress - }, - } - transactionEnvironment := newEVMTransactionEnvironment(handler, contractsAddress) - scriptEnvironment := newEVMScriptEnvironment(handler, contractsAddress) - - rt := runtime.NewInterpreterRuntime(runtime.Config{}) - - accountCodes := map[common.Location][]byte{} - var events []cadence.Event - - runtimeInterface := &TestRuntimeInterface{ - Storage: NewTestLedger(nil, nil), - OnGetSigningAccounts: func() ([]runtime.Address, error) { - return []runtime.Address{runtime.Address(contractsAddress)}, nil - }, - OnResolveLocation: LocationResolver, - OnUpdateAccountContractCode: func(location common.AddressLocation, code []byte) error { - accountCodes[location] = code - return nil - }, - OnGetAccountContractCode: func(location common.AddressLocation) (code []byte, err error) { - code = accountCodes[location] - return code, nil - }, - OnEmitEvent: func(event cadence.Event) error { - events = append(events, event) - return nil - }, - OnDecodeArgument: func(b []byte, t cadence.Type) (cadence.Value, error) { - return json.Decode(nil, b) - }, - OnGetAccountKey: func(addr runtime.Address, index uint32) (*cadenceStdlib.AccountKey, error) { - require.Equal(t, proof.Address[:], addr[:]) - return &cadenceStdlib.AccountKey{ - PublicKey: &cadenceStdlib.PublicKey{}, - KeyIndex: index, - Weight: 100, - HashAlgo: sema.HashAlgorithmKECCAK_256, - IsRevoked: false, - }, nil - }, - OnVerifySignature: func( + validate := func( + proof *types.COAOwnershipProofInContext, + onGetAccountKey func(addr runtime.Address, index uint32) (*cadenceStdlib.AccountKey, error), + onVerifySignature func( signature []byte, tag string, sd, publicKey []byte, signatureAlgorithm runtime.SignatureAlgorithm, - hashAlgorithm runtime.HashAlgorithm) (bool, error) { - // require.Equal(t, []byte(signedData.ToGoValue()), st) - return true, nil - }, - } + hashAlgorithm runtime.HashAlgorithm) (bool, error), + ) (cadence.Value, error) { + handler := &testContractHandler{ + deployCOA: func(_ uint64) types.Address { + return proof.EVMAddress + }, + } + transactionEnvironment := newEVMTransactionEnvironment(handler, contractsAddress) + scriptEnvironment := newEVMScriptEnvironment(handler, contractsAddress) - nextTransactionLocation := NewTransactionLocationGenerator() - nextScriptLocation := NewScriptLocationGenerator() + rt := runtime.NewInterpreterRuntime(runtime.Config{}) - // Deploy contracts + accountCodes := map[common.Location][]byte{} + var events []cadence.Event - deployContracts( - t, - rt, - contractsAddress, - runtimeInterface, - transactionEnvironment, - nextTransactionLocation, - ) + runtimeInterface := &TestRuntimeInterface{ + Storage: NewTestLedger(nil, nil), + OnGetSigningAccounts: func() ([]runtime.Address, error) { + return []runtime.Address{runtime.Address(contractsAddress)}, nil + }, + OnResolveLocation: newLocationResolver(contractsAddress), + OnUpdateAccountContractCode: func(location common.AddressLocation, code []byte) error { + accountCodes[location] = code + return nil + }, + OnGetAccountContractCode: func(location common.AddressLocation) (code []byte, err error) { + code = accountCodes[location] + return code, nil + }, + OnEmitEvent: func(event cadence.Event) error { + events = append(events, event) + return nil + }, + OnDecodeArgument: func(b []byte, t cadence.Type) (cadence.Value, error) { + return json.Decode(nil, b) + }, + OnGetAccountKey: onGetAccountKey, + OnVerifySignature: onVerifySignature, + } + + nextTransactionLocation := NewTransactionLocationGenerator() + nextScriptLocation := NewScriptLocationGenerator() + + // Deploy contracts + + deployContracts( + t, + rt, + contractsAddress, + runtimeInterface, + transactionEnvironment, + nextTransactionLocation, + ) - setupTx := []byte(` + setupTx := []byte(` import EVM from 0x1 transaction { @@ -4486,55 +5538,211 @@ func TestEVMValidateCOAOwnershipProof(t *testing.T) { } }`) - err := rt.ExecuteTransaction( - runtime.Script{ - Source: setupTx, - }, - runtime.Context{ - Interface: runtimeInterface, - Environment: transactionEnvironment, - Location: nextTransactionLocation(), - }, - ) - require.NoError(t, err) + err := rt.ExecuteTransaction( + runtime.Script{ + Source: setupTx, + }, + runtime.Context{ + Interface: runtimeInterface, + Environment: transactionEnvironment, + Location: nextTransactionLocation(), + }, + ) + require.NoError(t, err) - script := []byte(` - import EVM from 0x1 + script := []byte(` + import EVM from 0x1 + + access(all) + fun main( + address: Address, + path: PublicPath, + signedData: [UInt8], + keyIndices: [UInt64], + signatures: [[UInt8]], + evmAddress: [UInt8; 20] + ): EVM.ValidationResult { + return EVM.validateCOAOwnershipProof( + address: address, + path: path, + signedData: signedData, + keyIndices: keyIndices, + signatures: signatures, + evmAddress: evmAddress + ) + } + `) - access(all) - fun main( - address: Address, - path: PublicPath, - signedData: [UInt8], - keyIndices: [UInt64], - signatures: [[UInt8]], - evmAddress: [UInt8; 20] - - ) { - EVM.validateCOAOwnershipProof( - address: address, - path: path, - signedData: signedData, - keyIndices: keyIndices, - signatures: signatures, - evmAddress: evmAddress - ) - } - `) + // Run script + result, err := rt.ExecuteScript( + runtime.Script{ + Source: script, + Arguments: EncodeArgs(proof.ToCadenceValues()), + }, + runtime.Context{ + Interface: runtimeInterface, + Environment: scriptEnvironment, + Location: nextScriptLocation(), + }, + ) - // Run script - _, err = rt.ExecuteScript( - runtime.Script{ - Source: script, - Arguments: EncodeArgs(proof.ToCadenceValues()), - }, - runtime.Context{ - Interface: runtimeInterface, - Environment: scriptEnvironment, - Location: nextScriptLocation(), - }, - ) - require.NoError(t, err) + return result, err + } + + t.Run("Single key", func(t *testing.T) { + proof := &types.COAOwnershipProofInContext{ + COAOwnershipProof: types.COAOwnershipProof{ + Address: types.FlowAddress(contractsAddress), + CapabilityPath: "coa", + Signatures: []types.Signature{[]byte("signature")}, + KeyIndices: []uint64{0}, + }, + SignedData: []byte("signedData"), + EVMAddress: RandomAddress(t), + } + + result, err := validate( + proof, + func( + addr runtime.Address, + index uint32, + ) (*cadenceStdlib.AccountKey, error) { + require.Equal(t, proof.Address[:], addr[:]) + return &cadenceStdlib.AccountKey{ + PublicKey: &cadenceStdlib.PublicKey{}, + KeyIndex: index, + Weight: 1000, + HashAlgo: sema.HashAlgorithmKECCAK_256, + IsRevoked: false, + }, nil + }, + func( + signature []byte, + tag string, + sd, + publicKey []byte, + signatureAlgorithm runtime.SignatureAlgorithm, + hashAlgorithm runtime.HashAlgorithm, + ) (bool, error) { + return true, nil + }, + ) + + require.NoError(t, err) + + isValid := result.(cadence.Struct).SearchFieldByName("isValid").(cadence.Bool) + require.True(t, bool(isValid)) + }) + + t.Run("Two keys", func(t *testing.T) { + proof := &types.COAOwnershipProofInContext{ + COAOwnershipProof: types.COAOwnershipProof{ + Address: types.FlowAddress(contractsAddress), + CapabilityPath: "coa", + Signatures: []types.Signature{[]byte("signature2"), []byte("signature0")}, + KeyIndices: []uint64{2, 0}, + }, + SignedData: []byte("signedData"), + EVMAddress: RandomAddress(t), + } + + result, err := validate( + proof, + func(addr runtime.Address, index uint32) (*cadenceStdlib.AccountKey, error) { + require.Equal(t, proof.Address[:], addr[:]) + return &cadenceStdlib.AccountKey{ + PublicKey: &cadenceStdlib.PublicKey{ + // encode the key index into the public key + PublicKey: []byte{byte(index)}, + }, + KeyIndex: index, + Weight: 1000, + HashAlgo: sema.HashAlgorithmKECCAK_256, + IsRevoked: false, + }, nil + }, + func( + signature []byte, + tag string, + sd, + publicKey []byte, + signatureAlgorithm runtime.SignatureAlgorithm, + hashAlgorithm runtime.HashAlgorithm, + ) (bool, error) { + if bytes.Equal(signature, []byte("signature2")) { + require.Equal(t, byte(2), publicKey[0]) + return true, nil + } else if bytes.Equal(signature, []byte("signature0")) { + require.Equal(t, byte(0), publicKey[0]) + return true, nil + } else { + return false, nil + } + }, + ) + + require.NoError(t, err) + + isValid := result.(cadence.Struct).SearchFieldByName("isValid").(cadence.Bool) + require.True(t, bool(isValid)) + }) + + t.Run("Two keys insufficient weight", func(t *testing.T) { + proof := &types.COAOwnershipProofInContext{ + COAOwnershipProof: types.COAOwnershipProof{ + Address: types.FlowAddress(contractsAddress), + CapabilityPath: "coa", + Signatures: []types.Signature{[]byte("signature2"), []byte("signature0")}, + KeyIndices: []uint64{2, 0}, + }, + SignedData: []byte("signedData"), + EVMAddress: RandomAddress(t), + } + + result, err := validate( + proof, + func(addr runtime.Address, index uint32) (*cadenceStdlib.AccountKey, error) { + require.Equal(t, proof.Address[:], addr[:]) + return &cadenceStdlib.AccountKey{ + PublicKey: &cadenceStdlib.PublicKey{ + // encode the key index into the public key + PublicKey: []byte{byte(index)}, + }, + KeyIndex: index, + Weight: 499, + HashAlgo: sema.HashAlgorithmKECCAK_256, + IsRevoked: false, + }, nil + }, + func( + signature []byte, + tag string, + sd, + publicKey []byte, + signatureAlgorithm runtime.SignatureAlgorithm, + hashAlgorithm runtime.HashAlgorithm, + ) (bool, error) { + if bytes.Equal(signature, []byte("signature2")) { + require.Equal(t, byte(2), publicKey[0]) + return true, nil + } else if bytes.Equal(signature, []byte("signature0")) { + require.Equal(t, byte(0), publicKey[0]) + return true, nil + } else { + return false, nil + } + }, + ) + + require.NoError(t, err) + + isValid := result.(cadence.Struct).SearchFieldByName("isValid").(cadence.Bool) + require.False(t, bool(isValid)) + message := result.(cadence.Struct). + SearchFieldByName("problem").(cadence.Optional). + Value.(cadence.String).String() + require.Equal(t, "\"the given signatures are not valid or provide enough weight\"", message) + }) } func TestInternalEVMAccess(t *testing.T) { @@ -4564,7 +5772,7 @@ func TestInternalEVMAccess(t *testing.T) { OnGetSigningAccounts: func() ([]runtime.Address, error) { return []runtime.Address{runtime.Address(contractsAddress)}, nil }, - OnResolveLocation: LocationResolver, + OnResolveLocation: newLocationResolver(contractsAddress), OnUpdateAccountContractCode: func(location common.AddressLocation, code []byte) error { accountCodes[location] = code return nil diff --git a/fvm/evm/stdlib/type.go b/fvm/evm/stdlib/type.go index deca9a9e6b4..5ad44909bb4 100644 --- a/fvm/evm/stdlib/type.go +++ b/fvm/evm/stdlib/type.go @@ -4,9 +4,9 @@ import ( "fmt" "github.com/onflow/cadence" + "github.com/onflow/cadence/common" "github.com/onflow/cadence/runtime" - "github.com/onflow/cadence/runtime/common" - "github.com/onflow/cadence/runtime/sema" + "github.com/onflow/cadence/sema" coreContracts "github.com/onflow/flow-core-contracts/lib/go/contracts" "github.com/onflow/flow-go/fvm/systemcontracts" @@ -32,8 +32,11 @@ func newContractType(chainID flow.ChainID) *sema.CompositeType { templatesEnv := contracts.AsTemplateEnv() + cryptoContractLocation := contracts.Crypto.Location() + runtimeInterface := &checkingInterface{ - SystemContractCodes: map[common.AddressLocation][]byte{ + cryptoContractAddress: cryptoContractLocation.Address, + SystemContractCodes: map[common.Location][]byte{ contracts.ViewResolver.Location(): coreContracts.ViewResolver(), contracts.Burner.Location(): coreContracts.Burner(), contracts.FungibleToken.Location(): coreContracts.FungibleToken(templatesEnv), @@ -41,6 +44,7 @@ func newContractType(chainID flow.ChainID) *sema.CompositeType { contracts.MetadataViews.Location(): coreContracts.MetadataViews(templatesEnv), contracts.FlowToken.Location(): coreContracts.FlowToken(templatesEnv), contracts.FungibleTokenMetadataViews.Location(): coreContracts.FungibleTokenMetadataViews(templatesEnv), + cryptoContractLocation: coreContracts.Crypto(), }, } diff --git a/fvm/evm/stdlib/type_test.go b/fvm/evm/stdlib/type_test.go index de45b03a768..743b6a0a596 100644 --- a/fvm/evm/stdlib/type_test.go +++ b/fvm/evm/stdlib/type_test.go @@ -3,7 +3,7 @@ package stdlib_test import ( "testing" - "github.com/onflow/cadence/runtime/sema" + "github.com/onflow/cadence/sema" "github.com/stretchr/testify/require" "github.com/onflow/flow-go/fvm/evm/stdlib" diff --git a/fvm/evm/testutils/backend.go b/fvm/evm/testutils/backend.go index 0be5599d093..7e0f05cb201 100644 --- a/fvm/evm/testutils/backend.go +++ b/fvm/evm/testutils/backend.go @@ -6,15 +6,15 @@ import ( "fmt" "testing" - "github.com/onflow/cadence/runtime/stdlib" + "github.com/onflow/cadence/stdlib" "github.com/rs/zerolog" otelTrace "go.opentelemetry.io/otel/trace" "github.com/onflow/atree" "github.com/onflow/cadence" + "github.com/onflow/cadence/common" "github.com/onflow/cadence/encoding/ccf" "github.com/onflow/cadence/runtime" - "github.com/onflow/cadence/runtime/common" "github.com/stretchr/testify/require" "golang.org/x/exp/maps" @@ -64,8 +64,16 @@ func fullKey(owner, key []byte) string { } func GetSimpleValueStore() *TestValueStore { - data := make(map[string][]byte) - allocator := make(map[string]uint64) + return GetSimpleValueStorePopulated( + make(map[string][]byte), + make(map[string]uint64), + ) +} + +func GetSimpleValueStorePopulated( + data map[string][]byte, + allocator map[string]uint64, +) *TestValueStore { bytesRead := 0 bytesWritten := 0 return &TestValueStore{ @@ -123,6 +131,20 @@ func GetSimpleValueStore() *TestValueStore { bytesRead = 0 bytesWritten = 0 }, + + CloneFunc: func() *TestValueStore { + // clone data + newData := make(map[string][]byte) + for k, v := range data { + newData[k] = v + } + newAllocator := make(map[string]uint64) + for k, v := range allocator { + newAllocator[k] = v + } + // clone allocator + return GetSimpleValueStorePopulated(newData, newAllocator) + }, } } @@ -230,6 +252,7 @@ type TestValueStore struct { TotalBytesWrittenFunc func() int TotalStorageItemsFunc func() int ResetStatsFunc func() + CloneFunc func() *TestValueStore } var _ environment.ValueStore = &TestValueStore{} @@ -297,6 +320,13 @@ func (vs *TestValueStore) ResetStats() { vs.ResetStatsFunc() } +func (vs *TestValueStore) Clone() *TestValueStore { + if vs.CloneFunc == nil { + panic("method not set") + } + return vs.CloneFunc() +} + type testMeter struct { meterComputation func(common.ComputationKind, uint) error hasComputationCapacity func(common.ComputationKind, uint) bool diff --git a/fvm/evm/testutils/cadence.go b/fvm/evm/testutils/cadence.go index 1f607427c22..12f4889a428 100644 --- a/fvm/evm/testutils/cadence.go +++ b/fvm/evm/testutils/cadence.go @@ -6,63 +6,9 @@ import ( "github.com/onflow/cadence" "github.com/onflow/cadence/encoding/json" - "github.com/onflow/cadence/runtime" - "github.com/onflow/cadence/runtime/ast" - "github.com/onflow/cadence/runtime/common" - "github.com/onflow/cadence/runtime/sema" "github.com/stretchr/testify/require" ) -// LocationResolver is a location Cadence runtime interface location resolver -// very similar to ContractReader.ResolveLocation, -// but it does not look up available contract names -func LocationResolver( - identifiers []ast.Identifier, - location common.Location, -) ( - result []sema.ResolvedLocation, - err error, -) { - addressLocation, isAddress := location.(common.AddressLocation) - - // if the location is not an address location, e.g. an identifier location - // (`import Crypto`), then return a single resolved location which declares - // all identifiers. - if !isAddress { - return []runtime.ResolvedLocation{ - { - Location: location, - Identifiers: identifiers, - }, - }, nil - } - - // if the location is an address, - // and no specific identifiers where requested in the import statement, - // then assume the imported identifier is the address location's identifier (the contract) - if len(identifiers) == 0 { - identifiers = []ast.Identifier{ - {Identifier: addressLocation.Name}, - } - } - - // return one resolved location per identifier. - // each resolved location is an address contract location - resolvedLocations := make([]runtime.ResolvedLocation, len(identifiers)) - for i := range resolvedLocations { - identifier := identifiers[i] - resolvedLocations[i] = runtime.ResolvedLocation{ - Location: common.AddressLocation{ - Address: addressLocation.Address, - Name: identifier.Identifier, - }, - Identifiers: []runtime.Identifier{identifier}, - } - } - - return resolvedLocations, nil -} - func EncodeArgs(argValues []cadence.Value) [][]byte { args := make([][]byte, len(argValues)) for i, arg := range argValues { diff --git a/fvm/evm/testutils/contracts/test.sol b/fvm/evm/testutils/contracts/test.sol index 5036e239dcf..d5deadd9b37 100644 --- a/fvm/evm/testutils/contracts/test.sol +++ b/fvm/evm/testutils/contracts/test.sol @@ -18,6 +18,11 @@ contract Storage { number = num; } + function checkThenStore(uint256 prev, uint256 num)public { + require(number == prev, "stored value check failed"); + number = num; + } + function storeWithLog(uint256 num) public { emit NewStore(msg.sender, num); number = num; @@ -36,6 +41,10 @@ contract Storage { return block.number; } + function checkBlockNumber(uint expected) public view { + require(expected == block.number, "block number check failed"); + } + function blockTime() public view returns (uint) { return block.timestamp; } @@ -44,6 +53,14 @@ contract Storage { return blockhash(num); } + function checkBlockHash(uint num, bytes32 expected) public view { + require(expected == blockhash(num), "hash check failed"); + } + + function checkBalance(address addr, uint expected) public view{ + require(expected == addr.balance, "balance check failed"); + } + function random() public view returns (uint256) { return block.prevrandao; } @@ -93,4 +110,4 @@ contract Storage { require(expected == output, "output doesnt match the expected value"); return output; } -} +} \ No newline at end of file diff --git a/fvm/evm/testutils/contracts/test_abi.json b/fvm/evm/testutils/contracts/test_abi.json index e693dcc01ad..aff2b6afe16 100644 --- a/fvm/evm/testutils/contracts/test_abi.json +++ b/fvm/evm/testutils/contracts/test_abi.json @@ -117,6 +117,73 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "address", + "name": "addr", + "type": "address" + }, + { + "internalType": "uint256", + "name": "expected", + "type": "uint256" + } + ], + "name": "checkBalance", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "num", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "expected", + "type": "bytes32" + } + ], + "name": "checkBlockHash", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "expected", + "type": "uint256" + } + ], + "name": "checkBlockNumber", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "prev", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "num", + "type": "uint256" + } + ], + "name": "checkThenStore", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [], "name": "customError", diff --git a/fvm/evm/testutils/contracts/test_bytes.hex b/fvm/evm/testutils/contracts/test_bytes.hex index badff4c6411..b7a74895f9f 100644 --- a/fvm/evm/testutils/contracts/test_bytes.hex +++ b/fvm/evm/testutils/contracts/test_bytes.hex @@ -1 +1 @@ -60806040526112e1806100115f395ff3fe608060405234801561000f575f80fd5b5060043610610109575f3560e01c8063828dd048116100a0578063adc879e91161006f578063adc879e9146102a3578063b2821c8f146102c1578063cbaff5f9146102df578063d0d250bd146102e9578063dda3a7bd1461030757610109565b8063828dd0481461020957806383197ef01461023957806385df51fd14610243578063911007b41461027357610109565b806357e871e7116100dc57806357e871e7146101955780635ec01e4d146101b35780636057361d146101d15780636babb224146101ed57610109565b80632e64cec11461010d57806348b151661461012b5780634cbefa6a1461014957806352e2402414610165575b5f80fd5b610115610311565b6040516101229190610a6f565b60405180910390f35b610133610319565b6040516101409190610a6f565b60405180910390f35b610163600480360381019061015e9190610ac3565b610320565b005b61017f600480360381019061017a9190610b2b565b610329565b60405161018c9190610b65565b60405180910390f35b61019d6104d6565b6040516101aa9190610a6f565b60405180910390f35b6101bb6104dd565b6040516101c89190610a6f565b60405180910390f35b6101eb60048036038101906101e69190610ac3565b6104e4565b005b61020760048036038101906102029190610ac3565b6104ed565b005b610223600480360381019061021e9190610d7c565b61053a565b6040516102309190610e0b565b60405180910390f35b6102416106e9565b005b61025d60048036038101906102589190610ac3565b610702565b60405161026a9190610e33565b60405180910390f35b61028d60048036038101906102889190610b2b565b61070c565b60405161029a9190610e33565b60405180910390f35b6102ab61086e565b6040516102b89190610a6f565b60405180910390f35b6102c9610875565b6040516102d69190610b65565b60405180910390f35b6102e76109ca565b005b6102f1610a0c565b6040516102fe9190610e5b565b60405180910390f35b61030f610a19565b005b5f8054905090565b5f42905090565b805f8190555f80fd5b5f805f6801000000000000000173ffffffffffffffffffffffffffffffffffffffff166040516024016040516020818303038152906040527f53e87d66000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516103dc9190610ec6565b5f60405180830381855afa9150503d805f8114610414576040519150601f19603f3d011682016040523d82523d5f602084013e610419565b606091505b50915091508161045e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161045590610f36565b60405180910390fd5b5f818060200190518101906104739190610f68565b90508067ffffffffffffffff168567ffffffffffffffff16146104cb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016104c290611003565b60405180910390fd5b809350505050919050565b5f43905090565b5f44905090565b805f8190555050565b803373ffffffffffffffffffffffffffffffffffffffff167f043cc306157a91d747b36aba0e235bbbc5771d75aba162f6e5540767d22673c660405160405180910390a3805f8190555050565b5f805f6801000000000000000173ffffffffffffffffffffffffffffffffffffffff1686868660405160240161057293929190611069565b6040516020818303038152906040527f5ee837e7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516105fc9190610ec6565b5f60405180830381855afa9150503d805f8114610634576040519150601f19603f3d011682016040523d82523d5f602084013e610639565b606091505b50915091508161067e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610675906110ef565b60405180910390fd5b5f818060200190518101906106939190611121565b9050801515881515146106db576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106d290611003565b60405180910390fd5b809350505050949350505050565b3373ffffffffffffffffffffffffffffffffffffffff16ff5b5f81409050919050565b5f805f6801000000000000000173ffffffffffffffffffffffffffffffffffffffff16846040516024016107409190610b65565b6040516020818303038152906040527f78a75fbe000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516107ca9190610ec6565b5f60405180830381855afa9150503d805f8114610802576040519150601f19603f3d011682016040523d82523d5f602084013e610807565b606091505b50915091508161084c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161084390610f36565b60405180910390fd5b5f818060200190518101906108619190611160565b9050809350505050919050565b5f46905090565b5f805f6801000000000000000173ffffffffffffffffffffffffffffffffffffffff166040516024016040516020818303038152906040527f705fab20000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516109289190610ec6565b5f60405180830381855afa9150503d805f8114610960576040519150601f19603f3d011682016040523d82523d5f602084013e610965565b606091505b5091509150816109aa576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016109a1906110ef565b60405180910390fd5b5f818060200190518101906109bf9190610f68565b905080935050505090565b5f610a0a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610a01906111d5565b60405180910390fd5b565b6801000000000000000181565b60056040517f9195785a000000000000000000000000000000000000000000000000000000008152600401610a4e919061127f565b60405180910390fd5b5f819050919050565b610a6981610a57565b82525050565b5f602082019050610a825f830184610a60565b92915050565b5f604051905090565b5f80fd5b5f80fd5b610aa281610a57565b8114610aac575f80fd5b50565b5f81359050610abd81610a99565b92915050565b5f60208284031215610ad857610ad7610a91565b5b5f610ae584828501610aaf565b91505092915050565b5f67ffffffffffffffff82169050919050565b610b0a81610aee565b8114610b14575f80fd5b50565b5f81359050610b2581610b01565b92915050565b5f60208284031215610b4057610b3f610a91565b5b5f610b4d84828501610b17565b91505092915050565b610b5f81610aee565b82525050565b5f602082019050610b785f830184610b56565b92915050565b5f8115159050919050565b610b9281610b7e565b8114610b9c575f80fd5b50565b5f81359050610bad81610b89565b92915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f610bdc82610bb3565b9050919050565b610bec81610bd2565b8114610bf6575f80fd5b50565b5f81359050610c0781610be3565b92915050565b5f819050919050565b610c1f81610c0d565b8114610c29575f80fd5b50565b5f81359050610c3a81610c16565b92915050565b5f80fd5b5f80fd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b610c8e82610c48565b810181811067ffffffffffffffff82111715610cad57610cac610c58565b5b80604052505050565b5f610cbf610a88565b9050610ccb8282610c85565b919050565b5f67ffffffffffffffff821115610cea57610ce9610c58565b5b610cf382610c48565b9050602081019050919050565b828183375f83830152505050565b5f610d20610d1b84610cd0565b610cb6565b905082815260208101848484011115610d3c57610d3b610c44565b5b610d47848285610d00565b509392505050565b5f82601f830112610d6357610d62610c40565b5b8135610d73848260208601610d0e565b91505092915050565b5f805f8060808587031215610d9457610d93610a91565b5b5f610da187828801610b9f565b9450506020610db287828801610bf9565b9350506040610dc387828801610c2c565b925050606085013567ffffffffffffffff811115610de457610de3610a95565b5b610df087828801610d4f565b91505092959194509250565b610e0581610b7e565b82525050565b5f602082019050610e1e5f830184610dfc565b92915050565b610e2d81610c0d565b82525050565b5f602082019050610e465f830184610e24565b92915050565b610e5581610bd2565b82525050565b5f602082019050610e6e5f830184610e4c565b92915050565b5f81519050919050565b5f81905092915050565b8281835e5f83830152505050565b5f610ea082610e74565b610eaa8185610e7e565b9350610eba818560208601610e88565b80840191505092915050565b5f610ed18284610e96565b915081905092915050565b5f82825260208201905092915050565b7f756e7375636365737366756c2063616c6c20746f2061726368200000000000005f82015250565b5f610f20601a83610edc565b9150610f2b82610eec565b602082019050919050565b5f6020820190508181035f830152610f4d81610f14565b9050919050565b5f81519050610f6281610b01565b92915050565b5f60208284031215610f7d57610f7c610a91565b5b5f610f8a84828501610f54565b91505092915050565b7f6f757470757420646f65736e74206d61746368207468652065787065637465645f8201527f2076616c75650000000000000000000000000000000000000000000000000000602082015250565b5f610fed602683610edc565b9150610ff882610f93565b604082019050919050565b5f6020820190508181035f83015261101a81610fe1565b9050919050565b5f82825260208201905092915050565b5f61103b82610e74565b6110458185611021565b9350611055818560208601610e88565b61105e81610c48565b840191505092915050565b5f60608201905061107c5f830186610e4c565b6110896020830185610e24565b818103604083015261109b8184611031565b9050949350505050565b7f756e7375636365737366756c2063616c6c20746f2061726368000000000000005f82015250565b5f6110d9601983610edc565b91506110e4826110a5565b602082019050919050565b5f6020820190508181035f830152611106816110cd565b9050919050565b5f8151905061111b81610b89565b92915050565b5f6020828403121561113657611135610a91565b5b5f6111438482850161110d565b91505092915050565b5f8151905061115a81610c16565b92915050565b5f6020828403121561117557611174610a91565b5b5f6111828482850161114c565b91505092915050565b7f417373657274204572726f72204d6573736167650000000000000000000000005f82015250565b5f6111bf601483610edc565b91506111ca8261118b565b602082019050919050565b5f6020820190508181035f8301526111ec816111b3565b9050919050565b5f819050919050565b5f819050919050565b5f61121f61121a611215846111f3565b6111fc565b610a57565b9050919050565b61122f81611205565b82525050565b7f56616c756520697320746f6f206c6f77000000000000000000000000000000005f82015250565b5f611269601083610edc565b915061127482611235565b602082019050919050565b5f6040820190506112925f830184611226565b81810360208301526112a38161125d565b90509291505056fea2646970667358221220c8f61ffdc0539e91e3d12903ea95b1869afd0ff3b8f25bcbdd2c3e2f7a3b55ca64736f6c634300081a0033 \ No newline at end of file +608060405261170d806100115f395ff3fe608060405234801561000f575f80fd5b5060043610610135575f3560e01c806383197ef0116100b6578063b2821c8f1161007a578063b2821c8f14610325578063cbaff5f914610343578063d0d250bd1461034d578063d462f09b1461036b578063d695cdca14610387578063dda3a7bd146103a357610135565b806383197ef01461028157806385df51fd1461028b578063911007b4146102bb578063a7b93d28146102eb578063adc879e91461030757610135565b806357e871e7116100fd57806357e871e7146101dd5780635ec01e4d146101fb5780636057361d146102195780636babb22414610235578063828dd0481461025157610135565b80632e64cec11461013957806348b15166146101575780634cbefa6a146101755780634d7b9bd51461019157806352e24024146101ad575b5f80fd5b6101416103ad565b60405161014e9190610c41565b60405180910390f35b61015f6103b5565b60405161016c9190610c41565b60405180910390f35b61018f600480360381019061018a9190610c95565b6103bc565b005b6101ab60048036038101906101a69190610d1a565b6103c5565b005b6101c760048036038101906101c29190610d95565b610422565b6040516101d49190610dcf565b60405180910390f35b6101e56105cf565b6040516101f29190610c41565b60405180910390f35b6102036105d6565b6040516102109190610c41565b60405180910390f35b610233600480360381019061022e9190610c95565b6105dd565b005b61024f600480360381019061024a9190610c95565b6105e6565b005b61026b60048036038101906102669190610f8c565b610633565b604051610278919061101b565b60405180910390f35b6102896107e2565b005b6102a560048036038101906102a09190610c95565b6107fb565b6040516102b29190611043565b60405180910390f35b6102d560048036038101906102d09190610d95565b610805565b6040516102e29190611043565b60405180910390f35b6103056004803603810190610300919061105c565b610967565b005b61030f6109b4565b60405161031c9190610c41565b60405180910390f35b61032d6109bb565b60405161033a9190610dcf565b60405180910390f35b61034b610b10565b005b610355610b52565b60405161036291906110a9565b60405180910390f35b610385600480360381019061038091906110c2565b610b5f565b005b6103a1600480360381019061039c9190610c95565b610ba6565b005b6103ab610beb565b005b5f8054905090565b5f42905090565b805f8190555f80fd5b8173ffffffffffffffffffffffffffffffffffffffff1631811461041e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016104159061115a565b60405180910390fd5b5050565b5f805f6801000000000000000173ffffffffffffffffffffffffffffffffffffffff166040516024016040516020818303038152906040527f53e87d66000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516104d591906111ca565b5f60405180830381855afa9150503d805f811461050d576040519150601f19603f3d011682016040523d82523d5f602084013e610512565b606091505b509150915081610557576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161054e9061122a565b60405180910390fd5b5f8180602001905181019061056c919061125c565b90508067ffffffffffffffff168567ffffffffffffffff16146105c4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105bb906112f7565b60405180910390fd5b809350505050919050565b5f43905090565b5f44905090565b805f8190555050565b803373ffffffffffffffffffffffffffffffffffffffff167f043cc306157a91d747b36aba0e235bbbc5771d75aba162f6e5540767d22673c660405160405180910390a3805f8190555050565b5f805f6801000000000000000173ffffffffffffffffffffffffffffffffffffffff1686868660405160240161066b9392919061135d565b6040516020818303038152906040527f5ee837e7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516106f591906111ca565b5f60405180830381855afa9150503d805f811461072d576040519150601f19603f3d011682016040523d82523d5f602084013e610732565b606091505b509150915081610777576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161076e906113e3565b60405180910390fd5b5f8180602001905181019061078c9190611415565b9050801515881515146107d4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107cb906112f7565b60405180910390fd5b809350505050949350505050565b3373ffffffffffffffffffffffffffffffffffffffff16ff5b5f81409050919050565b5f805f6801000000000000000173ffffffffffffffffffffffffffffffffffffffff16846040516024016108399190610dcf565b6040516020818303038152906040527f78a75fbe000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516108c391906111ca565b5f60405180830381855afa9150503d805f81146108fb576040519150601f19603f3d011682016040523d82523d5f602084013e610900565b606091505b509150915081610945576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161093c9061122a565b60405180910390fd5b5f8180602001905181019061095a9190611454565b9050809350505050919050565b815f54146109aa576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016109a1906114c9565b60405180910390fd5b805f819055505050565b5f46905090565b5f805f6801000000000000000173ffffffffffffffffffffffffffffffffffffffff166040516024016040516020818303038152906040527f705fab20000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050604051610a6e91906111ca565b5f60405180830381855afa9150503d805f8114610aa6576040519150601f19603f3d011682016040523d82523d5f602084013e610aab565b606091505b509150915081610af0576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ae7906113e3565b60405180910390fd5b5f81806020019051810190610b05919061125c565b905080935050505090565b5f610b50576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b4790611531565b60405180910390fd5b565b6801000000000000000181565b81408114610ba2576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b9990611599565b60405180910390fd5b5050565b438114610be8576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610bdf90611601565b60405180910390fd5b50565b60056040517f9195785a000000000000000000000000000000000000000000000000000000008152600401610c2091906116ab565b60405180910390fd5b5f819050919050565b610c3b81610c29565b82525050565b5f602082019050610c545f830184610c32565b92915050565b5f604051905090565b5f80fd5b5f80fd5b610c7481610c29565b8114610c7e575f80fd5b50565b5f81359050610c8f81610c6b565b92915050565b5f60208284031215610caa57610ca9610c63565b5b5f610cb784828501610c81565b91505092915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f610ce982610cc0565b9050919050565b610cf981610cdf565b8114610d03575f80fd5b50565b5f81359050610d1481610cf0565b92915050565b5f8060408385031215610d3057610d2f610c63565b5b5f610d3d85828601610d06565b9250506020610d4e85828601610c81565b9150509250929050565b5f67ffffffffffffffff82169050919050565b610d7481610d58565b8114610d7e575f80fd5b50565b5f81359050610d8f81610d6b565b92915050565b5f60208284031215610daa57610da9610c63565b5b5f610db784828501610d81565b91505092915050565b610dc981610d58565b82525050565b5f602082019050610de25f830184610dc0565b92915050565b5f8115159050919050565b610dfc81610de8565b8114610e06575f80fd5b50565b5f81359050610e1781610df3565b92915050565b5f819050919050565b610e2f81610e1d565b8114610e39575f80fd5b50565b5f81359050610e4a81610e26565b92915050565b5f80fd5b5f80fd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b610e9e82610e58565b810181811067ffffffffffffffff82111715610ebd57610ebc610e68565b5b80604052505050565b5f610ecf610c5a565b9050610edb8282610e95565b919050565b5f67ffffffffffffffff821115610efa57610ef9610e68565b5b610f0382610e58565b9050602081019050919050565b828183375f83830152505050565b5f610f30610f2b84610ee0565b610ec6565b905082815260208101848484011115610f4c57610f4b610e54565b5b610f57848285610f10565b509392505050565b5f82601f830112610f7357610f72610e50565b5b8135610f83848260208601610f1e565b91505092915050565b5f805f8060808587031215610fa457610fa3610c63565b5b5f610fb187828801610e09565b9450506020610fc287828801610d06565b9350506040610fd387828801610e3c565b925050606085013567ffffffffffffffff811115610ff457610ff3610c67565b5b61100087828801610f5f565b91505092959194509250565b61101581610de8565b82525050565b5f60208201905061102e5f83018461100c565b92915050565b61103d81610e1d565b82525050565b5f6020820190506110565f830184611034565b92915050565b5f806040838503121561107257611071610c63565b5b5f61107f85828601610c81565b925050602061109085828601610c81565b9150509250929050565b6110a381610cdf565b82525050565b5f6020820190506110bc5f83018461109a565b92915050565b5f80604083850312156110d8576110d7610c63565b5b5f6110e585828601610c81565b92505060206110f685828601610e3c565b9150509250929050565b5f82825260208201905092915050565b7f62616c616e636520636865636b206661696c65640000000000000000000000005f82015250565b5f611144601483611100565b915061114f82611110565b602082019050919050565b5f6020820190508181035f83015261117181611138565b9050919050565b5f81519050919050565b5f81905092915050565b8281835e5f83830152505050565b5f6111a482611178565b6111ae8185611182565b93506111be81856020860161118c565b80840191505092915050565b5f6111d5828461119a565b915081905092915050565b7f756e7375636365737366756c2063616c6c20746f2061726368200000000000005f82015250565b5f611214601a83611100565b915061121f826111e0565b602082019050919050565b5f6020820190508181035f83015261124181611208565b9050919050565b5f8151905061125681610d6b565b92915050565b5f6020828403121561127157611270610c63565b5b5f61127e84828501611248565b91505092915050565b7f6f757470757420646f65736e74206d61746368207468652065787065637465645f8201527f2076616c75650000000000000000000000000000000000000000000000000000602082015250565b5f6112e1602683611100565b91506112ec82611287565b604082019050919050565b5f6020820190508181035f83015261130e816112d5565b9050919050565b5f82825260208201905092915050565b5f61132f82611178565b6113398185611315565b935061134981856020860161118c565b61135281610e58565b840191505092915050565b5f6060820190506113705f83018661109a565b61137d6020830185611034565b818103604083015261138f8184611325565b9050949350505050565b7f756e7375636365737366756c2063616c6c20746f2061726368000000000000005f82015250565b5f6113cd601983611100565b91506113d882611399565b602082019050919050565b5f6020820190508181035f8301526113fa816113c1565b9050919050565b5f8151905061140f81610df3565b92915050565b5f6020828403121561142a57611429610c63565b5b5f61143784828501611401565b91505092915050565b5f8151905061144e81610e26565b92915050565b5f6020828403121561146957611468610c63565b5b5f61147684828501611440565b91505092915050565b7f73746f7265642076616c756520636865636b206661696c6564000000000000005f82015250565b5f6114b3601983611100565b91506114be8261147f565b602082019050919050565b5f6020820190508181035f8301526114e0816114a7565b9050919050565b7f417373657274204572726f72204d6573736167650000000000000000000000005f82015250565b5f61151b601483611100565b9150611526826114e7565b602082019050919050565b5f6020820190508181035f8301526115488161150f565b9050919050565b7f6861736820636865636b206661696c65640000000000000000000000000000005f82015250565b5f611583601183611100565b915061158e8261154f565b602082019050919050565b5f6020820190508181035f8301526115b081611577565b9050919050565b7f626c6f636b206e756d62657220636865636b206661696c6564000000000000005f82015250565b5f6115eb601983611100565b91506115f6826115b7565b602082019050919050565b5f6020820190508181035f830152611618816115df565b9050919050565b5f819050919050565b5f819050919050565b5f61164b6116466116418461161f565b611628565b610c29565b9050919050565b61165b81611631565b82525050565b7f56616c756520697320746f6f206c6f77000000000000000000000000000000005f82015250565b5f611695601083611100565b91506116a082611661565b602082019050919050565b5f6040820190506116be5f830184611652565b81810360208301526116cf81611689565b90509291505056fea26469706673582212200ade6e0552548fb1b9355b0e891417786eff51bed669555dec499c0a4512075c64736f6c634300081a0033 \ No newline at end of file diff --git a/fvm/evm/testutils/event.go b/fvm/evm/testutils/event.go index 3dcddc0b7cc..6614bd22ee6 100644 --- a/fvm/evm/testutils/event.go +++ b/fvm/evm/testutils/event.go @@ -4,8 +4,8 @@ import ( "testing" "github.com/onflow/cadence" + "github.com/onflow/cadence/common" "github.com/onflow/cadence/encoding/ccf" - "github.com/onflow/cadence/runtime/common" "github.com/stretchr/testify/require" "gotest.tools/assert" diff --git a/fvm/evm/testutils/handler.go b/fvm/evm/testutils/handler.go new file mode 100644 index 00000000000..27081a5c778 --- /dev/null +++ b/fvm/evm/testutils/handler.go @@ -0,0 +1,62 @@ +package testutils + +import ( + "github.com/onflow/cadence/common" + + "github.com/onflow/flow-go/fvm/evm/debug" + "github.com/onflow/flow-go/fvm/evm/emulator" + "github.com/onflow/flow-go/fvm/evm/handler" + "github.com/onflow/flow-go/fvm/evm/types" + "github.com/onflow/flow-go/fvm/systemcontracts" + "github.com/onflow/flow-go/model/flow" +) + +func SetupHandler( + chainID flow.ChainID, + backend types.Backend, + rootAddr flow.Address, +) *handler.ContractHandler { + return handler.NewContractHandler( + chainID, + rootAddr, + common.MustBytesToAddress(systemcontracts.SystemContractsForChain(chainID).FlowToken.Address.Bytes()), + rootAddr, + handler.NewBlockStore(chainID, backend, rootAddr), + handler.NewAddressAllocator(), + backend, + emulator.NewEmulator(backend, rootAddr), + debug.NopTracer, + ) +} + +type TestPrecompiledContract struct { + RequiredGasFunc func(input []byte) uint64 + RunFunc func(input []byte) ([]byte, error) + AddressFunc func() types.Address +} + +var _ types.PrecompiledContract = &TestPrecompiledContract{} + +// RequiredGas returns the contract gas use +func (pc *TestPrecompiledContract) RequiredGas(input []byte) uint64 { + if pc.RequiredGasFunc == nil { + panic("RequiredGasFunc is not set for the test precompiled contract") + } + return pc.RequiredGasFunc(input) +} + +// Run runs the precompiled contract +func (pc *TestPrecompiledContract) Run(input []byte) ([]byte, error) { + if pc.RunFunc == nil { + panic("RunFunc is not set for the test precompiled contract") + } + return pc.RunFunc(input) +} + +// Address returns the address that this contract is deployed to +func (pc *TestPrecompiledContract) Address() types.Address { + if pc.AddressFunc == nil { + panic("AddressFunc is not set for the test precompiled contract") + } + return pc.AddressFunc() +} diff --git a/fvm/evm/testutils/misc.go b/fvm/evm/testutils/misc.go index 8b7d6e86f98..82ae2de6ce3 100644 --- a/fvm/evm/testutils/misc.go +++ b/fvm/evm/testutils/misc.go @@ -104,18 +104,13 @@ func RandomResultFixture(t testing.TB) *types.Result { func AggregatedPrecompiledCallsFixture(t testing.TB) types.AggregatedPrecompiledCalls { return types.AggregatedPrecompiledCalls{ types.PrecompiledCalls{ - Address: RandomAddress(t), - RequiredGasCalls: []types.RequiredGasCall{{ - Input: RandomData(t), - Output: 2, - }}, + Address: RandomAddress(t), + RequiredGasCalls: []uint64{2}, RunCalls: []types.RunCall{ { - Input: RandomData(t), Output: RandomData(t), }, { - Input: RandomData(t), Output: []byte{}, ErrorMsg: "Some error msg", }, diff --git a/fvm/evm/testutils/offchain.go b/fvm/evm/testutils/offchain.go new file mode 100644 index 00000000000..5a5e675798a --- /dev/null +++ b/fvm/evm/testutils/offchain.go @@ -0,0 +1,37 @@ +package testutils + +import ( + "fmt" + + "github.com/onflow/flow-go/fvm/evm/offchain/storage" + "github.com/onflow/flow-go/fvm/evm/types" +) + +// TestStorageProvider constructs a new +// storage provider that only provides +// storage for an specific height +type TestStorageProvider struct { + storage types.BackendStorage + height uint64 +} + +var _ types.StorageProvider = &TestStorageProvider{} + +// NewTestStorageProvider constructs a new TestStorageProvider +func NewTestStorageProvider( + initSnapshot types.BackendStorageSnapshot, + height uint64, +) *TestStorageProvider { + return &TestStorageProvider{ + storage: storage.NewEphemeralStorage(storage.NewReadOnlyStorage(initSnapshot)), + height: height, + } +} + +// GetSnapshotAt returns the snapshot at specific block height +func (sp *TestStorageProvider) GetSnapshotAt(height uint64) (types.BackendStorageSnapshot, error) { + if height != sp.height { + return nil, fmt.Errorf("storage for the given height (%d) is not available", height) + } + return sp.storage, nil +} diff --git a/fvm/evm/types/address.go b/fvm/evm/types/address.go index 34f6468f92d..cd0aca85c35 100644 --- a/fvm/evm/types/address.go +++ b/fvm/evm/types/address.go @@ -7,7 +7,7 @@ import ( "github.com/onflow/cadence" "github.com/onflow/cadence/encoding/ccf" - "github.com/onflow/cadence/runtime/sema" + "github.com/onflow/cadence/sema" gethCommon "github.com/onflow/go-ethereum/common" "github.com/onflow/flow-go/model/flow" diff --git a/fvm/evm/types/backend.go b/fvm/evm/types/backend.go index 7f46cddecc8..985a8bc29e1 100644 --- a/fvm/evm/types/backend.go +++ b/fvm/evm/types/backend.go @@ -4,11 +4,16 @@ import ( "github.com/onflow/flow-go/fvm/environment" ) +// BackendStorage provides an interface for storage of registers +type BackendStorage interface { + environment.ValueStore +} + // Backend provides a subset of the FVM environment functionality // Any error returned by a Backend is expected to be a `FatalError` or // a `BackendError`. type Backend interface { - environment.ValueStore + BackendStorage environment.Meter environment.EventEmitter environment.BlockInfo diff --git a/fvm/evm/types/block.go b/fvm/evm/types/block.go index 458e2961bc3..f4123b71ddc 100644 --- a/fvm/evm/types/block.go +++ b/fvm/evm/types/block.go @@ -120,8 +120,23 @@ func GenesisBlock(chainID flow.ChainID) *Block { } } +// when testnet was launched the block structure +// didn't have the preRandao filed so the hash of the event +// was different from hashing the genesis block struct. +var TestNetGenesisHash = gethCommon.Hash{ + 60, 220, 118, 103, 27, 85, 73, 205, + 46, 2, 83, 105, 179, 240, 255, 14, + 55, 21, 42, 211, 55, 87, 177, 115, + 118, 144, 125, 37, 146, 116, 168, 229, +} + // GenesisBlockHash returns the genesis block hash in the EVM environment func GenesisBlockHash(chainID flow.ChainID) gethCommon.Hash { + // for the case of testnet, the block didn't initially + // had the preRandao and it was not part of the hash calculation. + if chainID == flow.Testnet { + return TestNetGenesisHash + } h, err := GenesisBlock(chainID).Hash() if err != nil { // this never happens panic(err) diff --git a/fvm/evm/types/block_test.go b/fvm/evm/types/block_test.go index 2ccb0ddcd65..a87f3eca008 100644 --- a/fvm/evm/types/block_test.go +++ b/fvm/evm/types/block_test.go @@ -17,14 +17,12 @@ func Test_GenesisBlock(t *testing.T) { testnetGenesis := GenesisBlock(flow.Testnet) require.Equal(t, testnetGenesis.Timestamp, GenesisTimestamp(flow.Testnet)) testnetGenesisHash := GenesisBlockHash(flow.Testnet) - h, err := testnetGenesis.Hash() - require.NoError(t, err) - require.Equal(t, h, testnetGenesisHash) + require.Equal(t, TestNetGenesisHash, testnetGenesisHash) mainnetGenesis := GenesisBlock(flow.Mainnet) require.Equal(t, mainnetGenesis.Timestamp, GenesisTimestamp(flow.Mainnet)) mainnetGenesisHash := GenesisBlockHash(flow.Mainnet) - h, err = mainnetGenesis.Hash() + h, err := mainnetGenesis.Hash() require.NoError(t, err) require.Equal(t, h, mainnetGenesisHash) diff --git a/fvm/evm/types/chainIDs.go b/fvm/evm/types/chainIDs.go index d979bc732a3..0414e7bef89 100644 --- a/fvm/evm/types/chainIDs.go +++ b/fvm/evm/types/chainIDs.go @@ -10,6 +10,10 @@ var ( FlowEVMPreviewNetChainID = big.NewInt(646) FlowEVMTestNetChainID = big.NewInt(545) FlowEVMMainNetChainID = big.NewInt(747) + + FlowEVMPreviewNetChainIDInUInt64 = FlowEVMPreviewNetChainID.Uint64() + FlowEVMTestNetChainIDInUInt64 = FlowEVMTestNetChainID.Uint64() + FlowEVMMainNetChainIDInUInt64 = FlowEVMMainNetChainID.Uint64() ) func EVMChainIDFromFlowChainID(flowChainID flow.ChainID) *big.Int { diff --git a/fvm/evm/types/handler.go b/fvm/evm/types/handler.go index 3b5db881316..7f8c61368a5 100644 --- a/fvm/evm/types/handler.go +++ b/fvm/evm/types/handler.go @@ -1,7 +1,7 @@ package types import ( - "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/common" gethCommon "github.com/onflow/go-ethereum/common" ) diff --git a/fvm/evm/types/offchain.go b/fvm/evm/types/offchain.go new file mode 100644 index 00000000000..5fc91e4aaea --- /dev/null +++ b/fvm/evm/types/offchain.go @@ -0,0 +1,42 @@ +package types + +import ( + "github.com/onflow/flow-go/model/flow" +) + +// BackendStorageSnapshot provides a read only view of registers +type BackendStorageSnapshot interface { + GetValue(owner []byte, key []byte) ([]byte, error) +} + +// StorageProvider provides access to storage at +// specific time point in history of the EVM chain +type StorageProvider interface { + // GetSnapshotAt returns a readonly snapshot of storage + // at specific block (start state of the block before executing transactions) + GetSnapshotAt(evmBlockHeight uint64) (BackendStorageSnapshot, error) +} + +// BlockSnapshot provides access to the block information +// at specific block height +type BlockSnapshot interface { + // BlockContext constructs and returns the block context for the block + // + // Warning! the block hash provider on this one has to return empty + // for the current block to stay compatible with how on-chain EVM + // behaves. so if we are on block 10, and we query for the block hash on block + // 10 it should return empty hash. + BlockContext() (BlockContext, error) +} + +type BlockSnapshotProvider interface { + // GetSnapshotAt returns a readonly snapshot of block given evm block height + GetSnapshotAt(evmBlockHeight uint64) (BlockSnapshot, error) +} + +// ReplayResultCollector collects results of replay a block +type ReplayResultCollector interface { + // StorageRegisterUpdates returns the set of register changes + // (only the EVM-related registers) + StorageRegisterUpdates() map[flow.RegisterID]flow.RegisterValue +} diff --git a/fvm/evm/types/precompiled.go b/fvm/evm/types/precompiled.go index ccb58976b35..1215094a045 100644 --- a/fvm/evm/types/precompiled.go +++ b/fvm/evm/types/precompiled.go @@ -2,6 +2,7 @@ package types import ( "bytes" + "fmt" gethVM "github.com/onflow/go-ethereum/core/vm" "github.com/onflow/go-ethereum/rlp" @@ -17,15 +18,8 @@ type PrecompiledContract interface { Address() Address } -// RunCall captures a call to the RequiredGas method of a precompiled contract -type RequiredGasCall struct { - Input []byte - Output uint64 -} - // RunCall captures a call to the Run method of a precompiled contract type RunCall struct { - Input []byte Output []byte ErrorMsg string } @@ -33,7 +27,7 @@ type RunCall struct { // PrecompiledCalls captures all the calls to a precompiled contract type PrecompiledCalls struct { Address Address - RequiredGasCalls []RequiredGasCall + RequiredGasCalls []uint64 RunCalls []RunCall } @@ -43,8 +37,8 @@ func (pc *PrecompiledCalls) IsEmpty() bool { } const ( - AggregatedPrecompiledCallsEncodingVersion uint8 = 1 AggregatedPrecompiledCallsEncodingByteSize int = 1 + AggregatedPrecompiledCallsEncodingVersion uint8 = 2 // current version ) // AggregatedPrecompiledCalls aggregates a list of precompiled calls @@ -85,5 +79,54 @@ func AggregatedPrecompileCallsFromEncoded(encoded []byte) (AggregatedPrecompiled if len(encoded) == 0 { return apc, nil } - return apc, rlp.DecodeBytes(encoded[AggregatedPrecompiledCallsEncodingByteSize:], &apc) + switch int(encoded[0]) { + case 1: + return decodePrecompiledCallsV1(encoded) + case 2: + return apc, rlp.DecodeBytes(encoded[AggregatedPrecompiledCallsEncodingByteSize:], &apc) + default: + return nil, fmt.Errorf("unknown type for encoded AggregatedPrecompiledCalls received %d", int(encoded[0])) + } +} + +func decodePrecompiledCallsV1(encoded []byte) (AggregatedPrecompiledCalls, error) { + legacy := make([]precompiledCallsV1, 0) + err := rlp.DecodeBytes(encoded[AggregatedPrecompiledCallsEncodingByteSize:], &legacy) + if err != nil { + return nil, err + } + apc := make([]PrecompiledCalls, len(legacy)) + for i, ap := range legacy { + reqCalls := make([]uint64, len(ap.RequiredGasCalls)) + for j, rc := range ap.RequiredGasCalls { + reqCalls[j] = rc.Output + } + runCalls := make([]RunCall, len(ap.RunCalls)) + for j, rc := range ap.RunCalls { + runCalls[j] = RunCall{ + Output: rc.Output, + ErrorMsg: rc.ErrorMsg, + } + } + apc[i] = PrecompiledCalls{ + Address: ap.Address, + RequiredGasCalls: reqCalls, + RunCalls: runCalls, + } + } + return apc, nil +} + +// legacy encoding types +type precompiledCallsV1 struct { + Address Address + RequiredGasCalls []struct { + Input []byte + Output uint64 + } + RunCalls []struct { + Input []byte + Output []byte + ErrorMsg string + } } diff --git a/fvm/evm/types/precompiled_test.go b/fvm/evm/types/precompiled_test.go index cf9246ff863..9c5febb7d1a 100644 --- a/fvm/evm/types/precompiled_test.go +++ b/fvm/evm/types/precompiled_test.go @@ -1,6 +1,7 @@ package types_test import ( + "encoding/hex" "testing" "github.com/stretchr/testify/require" @@ -10,65 +11,97 @@ import ( ) func TestPrecompiledCallsEncoding(t *testing.T) { - // empty precompiled calls - empty := types.AggregatedPrecompiledCalls{ - types.PrecompiledCalls{ - Address: testutils.RandomAddress(t), - }, - types.PrecompiledCalls{ - Address: testutils.RandomAddress(t), - }, - } + t.Run("test latest version of encoding", func(t *testing.T) { + // empty precompiled calls + empty := types.AggregatedPrecompiledCalls{ + types.PrecompiledCalls{ + Address: testutils.RandomAddress(t), + }, + types.PrecompiledCalls{ + Address: testutils.RandomAddress(t), + }, + } - encoded, err := empty.Encode() - require.NoError(t, err) - require.Empty(t, encoded) + encoded, err := empty.Encode() + require.NoError(t, err) + require.Empty(t, encoded) - apc := types.AggregatedPrecompiledCalls{ - types.PrecompiledCalls{ - Address: testutils.RandomAddress(t), - RequiredGasCalls: []types.RequiredGasCall{{ - Input: []byte{1}, - Output: 2, - }}, - RunCalls: []types.RunCall{}, - }, - } + apc := types.AggregatedPrecompiledCalls{ + types.PrecompiledCalls{ + Address: testutils.RandomAddress(t), + RequiredGasCalls: []uint64{2}, + RunCalls: []types.RunCall{}, + }, + } - encoded, err = apc.Encode() - require.NoError(t, err) - require.NotEmpty(t, encoded) + encoded, err = apc.Encode() + require.NoError(t, err) + require.NotEmpty(t, encoded) - ret, err := types.AggregatedPrecompileCallsFromEncoded(encoded) - require.NoError(t, err) - require.Equal(t, apc, ret) + ret, err := types.AggregatedPrecompileCallsFromEncoded(encoded) + require.NoError(t, err) + require.Equal(t, apc, ret) - apc = types.AggregatedPrecompiledCalls{ - types.PrecompiledCalls{ - Address: testutils.RandomAddress(t), - RequiredGasCalls: []types.RequiredGasCall{{ - Input: []byte{1}, - Output: 2, - }}, - RunCalls: []types.RunCall{ - { - Input: []byte{3, 4}, - Output: []byte{5, 6}, + apc = types.AggregatedPrecompiledCalls{ + types.PrecompiledCalls{ + Address: testutils.RandomAddress(t), + RequiredGasCalls: []uint64{2}, + RunCalls: []types.RunCall{ + { + Output: []byte{5, 6}, + }, + { + Output: []byte{}, + ErrorMsg: "Some error msg", + }, }, - { - Input: []byte{3, 4}, - Output: []byte{}, - ErrorMsg: "Some error msg", + }, + } + + encoded, err = apc.Encode() + require.NoError(t, err) + require.NotEmpty(t, encoded) + + ret, err = types.AggregatedPrecompileCallsFromEncoded(encoded) + require.NoError(t, err) + require.Equal(t, apc, ret) + + }) + + t.Run("test latest version of encoding v1", func(t *testing.T) { + encodedV1, err := hex.DecodeString("01f7f69408190002143239aaaed0d52d4a5bf218d62453ffc3c20102dcc782030482050680d3820304808e536f6d65206572726f72206d7367") + require.NoError(t, err) + expected := types.AggregatedPrecompiledCalls{ + types.PrecompiledCalls{ + Address: types.Address{0x8, 0x19, 0x0, 0x2, 0x14, 0x32, 0x39, 0xaa, 0xae, 0xd0, 0xd5, 0x2d, 0x4a, 0x5b, 0xf2, 0x18, 0xd6, 0x24, 0x53, 0xff}, + RequiredGasCalls: []uint64{2}, + RunCalls: []types.RunCall{ + { + Output: []byte{5, 6}, + }, + { + Output: []byte{}, + ErrorMsg: "Some error msg", + }, }, }, - }, - } + } - encoded, err = apc.Encode() - require.NoError(t, err) - require.NotEmpty(t, encoded) + apc, err := types.AggregatedPrecompileCallsFromEncoded(encodedV1) + require.NoError(t, err) + require.Equal(t, len(expected), len(apc)) + for i := range expected { + require.Equal(t, expected[i].Address, apc[i].Address) + require.Equal(t, len(expected[i].RequiredGasCalls), len(apc[i].RequiredGasCalls)) + for j := range expected[i].RequiredGasCalls { + require.Equal(t, expected[i].RequiredGasCalls[j], apc[i].RequiredGasCalls[j]) + } + require.Equal(t, len(expected[i].RunCalls), len(apc[i].RunCalls)) + for j := range expected[i].RunCalls { + require.Equal(t, expected[i].RunCalls[j].ErrorMsg, apc[i].RunCalls[j].ErrorMsg) + require.Equal(t, expected[i].RunCalls[j].Output, apc[i].RunCalls[j].Output) + } + } + }) - ret, err = types.AggregatedPrecompileCallsFromEncoded(encoded) - require.NoError(t, err) - require.Equal(t, apc, ret) } diff --git a/fvm/evm/types/proof.go b/fvm/evm/types/proof.go index c0d915af70e..789d557897c 100644 --- a/fvm/evm/types/proof.go +++ b/fvm/evm/types/proof.go @@ -4,9 +4,9 @@ import ( "fmt" "github.com/onflow/cadence" - "github.com/onflow/cadence/runtime/common" - "github.com/onflow/cadence/runtime/sema" - cadenceRLP "github.com/onflow/cadence/runtime/stdlib/rlp" + "github.com/onflow/cadence/common" + "github.com/onflow/cadence/sema" + cadenceRLP "github.com/onflow/cadence/stdlib/rlp" "github.com/onflow/go-ethereum/rlp" "github.com/onflow/flow-go/model/flow" diff --git a/fvm/evm/types/result.go b/fvm/evm/types/result.go index 8a4ba270dc6..2ad16a0b5df 100644 --- a/fvm/evm/types/result.go +++ b/fvm/evm/types/result.go @@ -1,9 +1,9 @@ package types import ( - "github.com/onflow/go-ethereum/common" gethCommon "github.com/onflow/go-ethereum/common" gethTypes "github.com/onflow/go-ethereum/core/types" + "github.com/onflow/go-ethereum/rlp" ) // InvalidTransactionGasCost is a gas cost we charge when @@ -17,6 +17,9 @@ import ( // user for the validation fee. const InvalidTransactionGasCost = 1_000 +// ChecksumLength captures number of bytes a checksum uses +const ChecksumLength = 4 + // Status captures the status of an interaction to the emulator type Status uint8 @@ -90,6 +93,8 @@ type Result struct { // PrecompiledCalls captures an encoded list of calls to the precompile // during the execution of transaction PrecompiledCalls []byte + // StateChangeCommitment captures a commitment over the state change (delta) + StateChangeCommitment []byte } // Invalid returns true if transaction has been rejected @@ -125,6 +130,59 @@ func (res *Result) VMErrorString() string { return "" } +// ErrorMsg returns the error message, if any VM or Validation error +// both error would never happen at the same time +// but if it happens the priority is by validation error +func (res *Result) ErrorMsg() string { + errorMsg := "" + if res.VMError != nil { + errorMsg = res.VMError.Error() + } + if res.ValidationError != nil { + errorMsg = res.ValidationError.Error() + } + return errorMsg +} + +// RLPEncodedLogs returns the rlp encoding of the logs +func (res *Result) RLPEncodedLogs() ([]byte, error) { + var encodedLogs []byte + var err error + if len(res.Logs) > 0 { + encodedLogs, err = rlp.EncodeToBytes(res.Logs) + if err != nil { + return encodedLogs, err + } + } + return encodedLogs, nil +} + +// DeployedContractAddressString returns an string of the deployed address +// it returns an empty string if the deployed address is nil +func (res *Result) DeployedContractAddressString() string { + deployedAddress := "" + if res.DeployedContractAddress != nil { + deployedAddress = res.DeployedContractAddress.String() + } + return deployedAddress +} + +// StateChangeChecksum constructs a checksum +// based on the state change commitment on the result +func (res *Result) StateChangeChecksum() [ChecksumLength]byte { + return SliceToChecksumLength(res.StateChangeCommitment) +} + +// SliceToChecksumLength cuts the first 4 bytes of the input and convert it into checksum +func SliceToChecksumLength(input []byte) [ChecksumLength]byte { + // the first 4 bytes of StateChangeCommitment is used as checksum + var checksum [ChecksumLength]byte + if len(input) >= ChecksumLength { + copy(checksum[:ChecksumLength], input[:ChecksumLength]) + } + return checksum +} + // Receipt constructs an EVM-style receipt // can be used by json-rpc and other integration to be returned. // @@ -229,9 +287,9 @@ func (res *Result) ResultSummary() *ResultSummary { // used by the LightReceipt type LightLog struct { // address of the contract that generated the event - Address common.Address + Address gethCommon.Address // list of topics provided by the contract. - Topics []common.Hash + Topics []gethCommon.Hash // supplied by the contract, usually ABI-encoded Data []byte } diff --git a/fvm/evm/types/state.go b/fvm/evm/types/state.go index edff68251ef..acdaaf2c9ed 100644 --- a/fvm/evm/types/state.go +++ b/fvm/evm/types/state.go @@ -2,21 +2,24 @@ package types import ( "github.com/holiman/uint256" + "github.com/onflow/crypto/hash" gethCommon "github.com/onflow/go-ethereum/common" gethTypes "github.com/onflow/go-ethereum/core/types" gethVM "github.com/onflow/go-ethereum/core/vm" + "github.com/onflow/go-ethereum/rlp" ) // StateDB acts as the main interface to the EVM runtime type StateDB interface { gethVM.StateDB - // Commit commits the changes + // Commit commits the changes and + // returns a commitment over changes // setting `finalize` flag // calls a subsequent call to Finalize - // defering finalization and calling it once at the end + // deferring finalization and calling it once at the end // improves efficiency of batch operations. - Commit(finalize bool) error + Commit(finalize bool) (hash.Hash, error) // Finalize flushes all the changes // to the permanent storage @@ -166,3 +169,24 @@ type SlotAddress struct { Address gethCommon.Address Key gethCommon.Hash } + +// SlotEntry captures an address to a storage slot and the value stored in it +type SlotEntry struct { + Address gethCommon.Address + Key gethCommon.Hash + Value gethCommon.Hash +} + +// Encoded returns the encoded content of the slot entry +func (se *SlotEntry) Encode() ([]byte, error) { + return rlp.EncodeToBytes(se) +} + +// SlotEntryFromEncoded constructs an slot entry from the encoded data +func SlotEntryFromEncoded(encoded []byte) (*SlotEntry, error) { + if len(encoded) == 0 { + return nil, nil + } + se := &SlotEntry{} + return se, rlp.DecodeBytes(encoded, se) +} diff --git a/fvm/executionParameters.go b/fvm/executionParameters.go index 0173210545b..db88f42ef5f 100644 --- a/fvm/executionParameters.go +++ b/fvm/executionParameters.go @@ -5,8 +5,15 @@ import ( "fmt" "math" + "github.com/coreos/go-semver/semver" + "github.com/rs/zerolog" + + "github.com/onflow/flow-go/model/convert" + + "github.com/onflow/flow-go/fvm/storage/snapshot" + "github.com/onflow/cadence" - "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/common" "github.com/onflow/flow-go/fvm/blueprints" "github.com/onflow/flow-go/fvm/environment" @@ -50,74 +57,82 @@ func getBasicMeterParameters( return params } -// getBodyMeterParameters returns the set of meter parameters used for -// transaction/script body execution. -func getBodyMeterParameters( +// getExecutionParameters returns the set of meter parameters used for +// transaction/script body execution and the minimum required version as defined by the +// NodeVersionBeacon contract. +func getExecutionParameters( + log zerolog.Logger, ctx Context, proc Procedure, txnState storage.TransactionPreparer, -) ( - meter.MeterParameters, - error, -) { - procParams := getBasicMeterParameters(ctx, proc) +) (state.ExecutionParameters, *snapshot.ExecutionSnapshot, error) { + meterParams := getBasicMeterParameters(ctx, proc) - overrides, err := txnState.GetMeterParamOverrides( + executionParams, executionParamsStateRead, err := txnState.GetStateExecutionParameters( txnState, - NewMeterParamOverridesComputer(ctx, txnState)) + NewExecutionParametersComputer(log, ctx, txnState)) if err != nil { - return procParams, err + return state.ExecutionParameters{ + MeterParameters: meterParams, + ExecutionVersion: semver.Version{}, + }, nil, err } - if overrides.ComputationWeights != nil { - procParams = procParams.WithComputationWeights( - overrides.ComputationWeights) + if executionParams.ComputationWeights != nil { + meterParams = meterParams.WithComputationWeights( + executionParams.ComputationWeights) } - if overrides.MemoryWeights != nil { - procParams = procParams.WithMemoryWeights(overrides.MemoryWeights) + if executionParams.MemoryWeights != nil { + meterParams = meterParams.WithMemoryWeights(executionParams.MemoryWeights) } - if overrides.MemoryLimit != nil { - procParams = procParams.WithMemoryLimit(*overrides.MemoryLimit) + if executionParams.MemoryLimit != nil { + meterParams = meterParams.WithMemoryLimit(*executionParams.MemoryLimit) } // NOTE: The memory limit (and interaction limit) may be overridden by the // environment. We need to ignore the override in that case. if proc.ShouldDisableMemoryAndInteractionLimits(ctx) { - procParams = procParams.WithMemoryLimit(math.MaxUint64). + meterParams = meterParams.WithMemoryLimit(math.MaxUint64). WithStorageInteractionLimit(math.MaxUint64) } - return procParams, nil + return state.ExecutionParameters{ + MeterParameters: meterParams, + ExecutionVersion: executionParams.ExecutionVersion, + }, executionParamsStateRead, nil } -type MeterParamOverridesComputer struct { +type ExecutionParametersComputer struct { + log zerolog.Logger ctx Context txnState storage.TransactionPreparer } -func NewMeterParamOverridesComputer( +func NewExecutionParametersComputer( + log zerolog.Logger, ctx Context, txnState storage.TransactionPreparer, -) MeterParamOverridesComputer { - return MeterParamOverridesComputer{ +) ExecutionParametersComputer { + return ExecutionParametersComputer{ + log: log, ctx: ctx, txnState: txnState, } } -func (computer MeterParamOverridesComputer) Compute( +func (computer ExecutionParametersComputer) Compute( _ state.NestedTransactionPreparer, _ struct{}, ) ( - derived.MeterParamOverrides, + derived.StateExecutionParameters, error, ) { - var overrides derived.MeterParamOverrides + var overrides derived.StateExecutionParameters var err error computer.txnState.RunWithAllLimitsDisabled(func() { - overrides, err = computer.getMeterParamOverrides() + overrides, err = computer.getExecutionParameters() }) if err != nil { @@ -129,8 +144,8 @@ func (computer MeterParamOverridesComputer) Compute( return overrides, nil } -func (computer MeterParamOverridesComputer) getMeterParamOverrides() ( - derived.MeterParamOverrides, +func (computer ExecutionParametersComputer) getExecutionParameters() ( + derived.StateExecutionParameters, error, ) { // Check that the service account exists because all the settings are @@ -144,7 +159,7 @@ func (computer MeterParamOverridesComputer) getMeterParamOverrides() ( computer.ctx.EnvironmentParams, computer.txnState) - overrides := derived.MeterParamOverrides{} + overrides := derived.StateExecutionParameters{} // set the property if no error, but if the error is a fatal error then // return it @@ -201,6 +216,15 @@ func (computer MeterParamOverridesComputer) getMeterParamOverrides() ( return overrides, err } + executionVersion, err := GetMinimumRequiredExecutionVersion(computer.log, computer.ctx, env) + err = setIfOk( + "execution version", + err, + func() { overrides.ExecutionVersion = executionVersion }) + if err != nil { + return overrides, err + } + return overrides, nil } @@ -333,3 +357,40 @@ func GetExecutionMemoryLimit( return uint64(memoryLimitRaw), nil } + +func GetMinimumRequiredExecutionVersion( + log zerolog.Logger, + ctx Context, + env environment.Environment, +) (semver.Version, error) { + if !ctx.ReadVersionFromNodeVersionBeacon { + return semver.Version{}, nil + } + + // the current version boundary defines a block height and a minimum required version that is required past that block height. + value, err := env.GetCurrentVersionBoundary() + + if err != nil { + return semver.Version{}, fmt.Errorf("could not get current version boundary: %w", err) + } + + boundary, err := convert.VersionBoundary(value) + + if err != nil { + return semver.Version{}, fmt.Errorf("could not parse current version boundary: %w", err) + } + + semVer, err := semver.NewVersion(boundary.Version) + if err != nil { + // This could be problematic, if the version is not a valid semver version. The NodeVersionBeacon should prevent + // this, but it could have bugs. + // Erroring here gives us no way to recover as no transactions would work anymore, + // instead return the version as 0.0.0 and log the error, allowing us to recover. + // this would mean that any if-statements that were relying on a higher version would fail, + // but that is preferable to all transactions halting. + log.Error().Err(err).Msg("could not parse version boundary. Version boundary as defined in the NodeVersionBeacon contract is not a valid semver version!") + return semver.Version{}, nil + } + + return *semVer, nil +} diff --git a/fvm/executionParameters_test.go b/fvm/executionParameters_test.go index 7775d010f2f..da40c12c281 100644 --- a/fvm/executionParameters_test.go +++ b/fvm/executionParameters_test.go @@ -8,8 +8,8 @@ import ( "github.com/stretchr/testify/require" "github.com/onflow/cadence" + "github.com/onflow/cadence/common" "github.com/onflow/cadence/runtime" - "github.com/onflow/cadence/runtime/common" "github.com/onflow/flow-go/fvm" "github.com/onflow/flow-go/fvm/blueprints" diff --git a/fvm/fvm.go b/fvm/fvm.go index b3671e678ec..ae92d08d945 100644 --- a/fvm/fvm.go +++ b/fvm/fvm.go @@ -5,7 +5,7 @@ import ( "fmt" "github.com/onflow/cadence" - "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/common" "github.com/onflow/flow-go/fvm/environment" "github.com/onflow/flow-go/fvm/errors" diff --git a/fvm/fvm_bench_test.go b/fvm/fvm_bench_test.go index aa1a06b6a6d..566cf81653a 100644 --- a/fvm/fvm_bench_test.go +++ b/fvm/fvm_bench_test.go @@ -18,12 +18,12 @@ import ( "github.com/onflow/cadence/encoding/ccf" jsoncdc "github.com/onflow/cadence/encoding/json" "github.com/onflow/cadence/runtime" - "github.com/onflow/cadence/runtime/stdlib" + "github.com/onflow/cadence/stdlib" "github.com/rs/zerolog" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - flow2 "github.com/onflow/flow-go-sdk" + flowsdk "github.com/onflow/flow-go-sdk" "github.com/onflow/flow-go-sdk/templates" "github.com/onflow/flow-go/engine/execution" @@ -315,13 +315,13 @@ func (b *BasicBlockExecutor) SetupAccounts(tb testing.TB, privateKeys []flow.Acc serviceAddress := b.Chain(tb).ServiceAddress() for _, privateKey := range privateKeys { - accountKey := flow2.NewAccountKey(). + accountKey := flowsdk.NewAccountKey(). FromPrivateKey(privateKey.PrivateKey). SetWeight(fvm.AccountKeyWeightThreshold). SetHashAlgo(privateKey.HashAlgo). SetSigAlgo(privateKey.SignAlgo) - sdkTX, err := templates.CreateAccount([]*flow2.AccountKey{accountKey}, []templates.Contract{}, flow2.BytesToAddress(serviceAddress.Bytes())) + sdkTX, err := templates.CreateAccount([]*flowsdk.AccountKey{accountKey}, []templates.Contract{}, flowsdk.BytesToAddress(serviceAddress.Bytes())) require.NoError(tb, err) txBody := flow.NewTransactionBody(). diff --git a/fvm/fvm_blockcontext_test.go b/fvm/fvm_blockcontext_test.go index 61f64fb59a7..037d25f4994 100644 --- a/fvm/fvm_blockcontext_test.go +++ b/fvm/fvm_blockcontext_test.go @@ -13,7 +13,7 @@ import ( "github.com/onflow/cadence/encoding/ccf" jsoncdc "github.com/onflow/cadence/encoding/json" "github.com/onflow/cadence/runtime" - "github.com/onflow/cadence/runtime/stdlib" + "github.com/onflow/cadence/stdlib" "github.com/onflow/crypto" "github.com/onflow/crypto/hash" "github.com/stretchr/testify/mock" diff --git a/fvm/fvm_fuzz_test.go b/fvm/fvm_fuzz_test.go index cedf371817a..97114a8b3e6 100644 --- a/fvm/fvm_fuzz_test.go +++ b/fvm/fvm_fuzz_test.go @@ -5,7 +5,7 @@ import ( "math" "testing" - "github.com/onflow/cadence/runtime/stdlib" + "github.com/onflow/cadence/stdlib" "github.com/stretchr/testify/require" "github.com/onflow/cadence" diff --git a/fvm/fvm_test.go b/fvm/fvm_test.go index b48fedebe22..b186d0e1c14 100644 --- a/fvm/fvm_test.go +++ b/fvm/fvm_test.go @@ -1,26 +1,31 @@ package fvm_test import ( + "context" "crypto/rand" "encoding/hex" "fmt" "math" "strings" "testing" + "time" - stdlib2 "github.com/onflow/cadence/runtime/stdlib" - - envMock "github.com/onflow/flow-go/fvm/environment/mock" - "github.com/onflow/flow-go/fvm/evm/events" - + "github.com/coreos/go-semver/semver" "github.com/onflow/cadence" + "github.com/onflow/cadence/common" "github.com/onflow/cadence/encoding/ccf" jsoncdc "github.com/onflow/cadence/encoding/json" + cadenceErrors "github.com/onflow/cadence/errors" + "github.com/onflow/cadence/interpreter" "github.com/onflow/cadence/runtime" - "github.com/onflow/cadence/runtime/common" - cadenceErrors "github.com/onflow/cadence/runtime/errors" - "github.com/onflow/cadence/runtime/tests/utils" + "github.com/onflow/cadence/sema" + cadenceStdlib "github.com/onflow/cadence/stdlib" + "github.com/onflow/cadence/test_utils/runtime_utils" "github.com/onflow/crypto" + "github.com/onflow/flow-core-contracts/lib/go/templates" + flowsdk "github.com/onflow/flow-go-sdk" + "github.com/onflow/flow-go-sdk/test" + "github.com/rs/zerolog" "github.com/stretchr/testify/assert" mockery "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -30,15 +35,20 @@ import ( "github.com/onflow/flow-go/fvm" fvmCrypto "github.com/onflow/flow-go/fvm/crypto" "github.com/onflow/flow-go/fvm/environment" + envMock "github.com/onflow/flow-go/fvm/environment/mock" "github.com/onflow/flow-go/fvm/errors" + "github.com/onflow/flow-go/fvm/evm/events" "github.com/onflow/flow-go/fvm/evm/stdlib" "github.com/onflow/flow-go/fvm/evm/types" "github.com/onflow/flow-go/fvm/meter" reusableRuntime "github.com/onflow/flow-go/fvm/runtime" + "github.com/onflow/flow-go/fvm/storage" "github.com/onflow/flow-go/fvm/storage/snapshot" "github.com/onflow/flow-go/fvm/storage/snapshot/mock" + "github.com/onflow/flow-go/fvm/storage/state" "github.com/onflow/flow-go/fvm/storage/testutils" "github.com/onflow/flow-go/fvm/systemcontracts" + "github.com/onflow/flow-go/fvm/tracing" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/utils/unittest" ) @@ -967,7 +977,7 @@ func TestTransactionFeeDeduction(t *testing.T) { address := flow.ConvertAddress( cadence.SearchFieldByName( data.(cadence.Event), - stdlib2.AccountEventAddressParameter.Identifier, + cadenceStdlib.AccountEventAddressParameter.Identifier, ).(cadence.Address), ) @@ -1524,17 +1534,17 @@ func TestSettingExecutionWeights(t *testing.T) { SetScript([]byte(fmt.Sprintf(` import FungibleToken from 0x%s import FlowToken from 0x%s - + transaction() { let sentVault: @{FungibleToken.Vault} - + prepare(signer: auth(BorrowValue) &Account) { let vaultRef = signer.storage.borrow(from: /storage/flowTokenVault) ?? panic("Could not borrow reference to the owner's Vault!") - + self.sentVault <- vaultRef.withdraw(amount: 5.0) } - + execute { let recipient1 = getAccount(%s) let recipient2 = getAccount(%s) @@ -1552,7 +1562,7 @@ func TestSettingExecutionWeights(t *testing.T) { ?? panic("Could not borrow receiver reference to the recipient's Vault") let receiverRef5 = recipient5.capabilities.borrow<&{FungibleToken.Receiver}>(/public/flowTokenReceiver) ?? panic("Could not borrow receiver reference to the recipient's Vault") - + receiverRef1.deposit(from: <-self.sentVault.withdraw(amount: 1.0)) receiverRef2.deposit(from: <-self.sentVault.withdraw(amount: 1.0)) receiverRef3.deposit(from: <-self.sentVault.withdraw(amount: 1.0)) @@ -2328,7 +2338,7 @@ func TestInteractionLimit(t *testing.T) { address = flow.ConvertAddress( cadence.SearchFieldByName( data.(cadence.Event), - stdlib2.AccountEventAddressParameter.Identifier, + cadenceStdlib.AccountEventAddressParameter.Identifier, ).(cadence.Address), ) @@ -2400,16 +2410,6 @@ func TestAttachments(t *testing.T) { newVMTest(). withBootstrapProcedureOptions(). - withContextOptions( - fvm.WithReusableCadenceRuntimePool( - reusableRuntime.NewReusableCadenceRuntimePool( - 1, - runtime.Config{ - AttachmentsEnabled: true, - }, - ), - ), - ). run( func( t *testing.T, @@ -2606,25 +2606,25 @@ func TestStorageIterationWithBrokenValues(t *testing.T) { } // Deploy `A` - runTransaction(utils.DeploymentTransaction( + runTransaction(runtime_utils.DeploymentTransaction( "A", []byte(contractA), )) // Deploy `B` - runTransaction(utils.DeploymentTransaction( + runTransaction(runtime_utils.DeploymentTransaction( "B", []byte(contractB), )) // Deploy `C` - runTransaction(utils.DeploymentTransaction( + runTransaction(runtime_utils.DeploymentTransaction( "C", []byte(contractC), )) // Deploy `D` - runTransaction(utils.DeploymentTransaction( + runTransaction(runtime_utils.DeploymentTransaction( "D", []byte(contractD), )) @@ -2666,7 +2666,7 @@ func TestStorageIterationWithBrokenValues(t *testing.T) { ))) // Update `A`, such that `B`, `C` and `D` are now broken. - runTransaction(utils.UpdateTransaction( + runTransaction(runtime_utils.UpdateTransaction( "A", []byte(updatedContractA), )) @@ -2912,7 +2912,7 @@ func TestEVM(t *testing.T) { sc := systemcontracts.SystemContractsForChain(chain.ChainID()) script := fvm.Script([]byte(fmt.Sprintf(` import EVM from %s - + access(all) fun main() { let bal = EVM.Balance(attoflow: 1000000000000000000) let acc <- EVM.createCadenceOwnedAccount() @@ -2975,7 +2975,7 @@ func TestEVM(t *testing.T) { script := fvm.Script([]byte(fmt.Sprintf(` import EVM from %s - + access(all) fun main() { destroy <- EVM.createCadenceOwnedAccount() @@ -3007,7 +3007,7 @@ func TestEVM(t *testing.T) { txBody := flow.NewTransactionBody(). SetScript([]byte(fmt.Sprintf(` import FungibleToken from %s - import FlowToken from %s + import FlowToken from %s import EVM from %s transaction() { @@ -3025,7 +3025,7 @@ func TestEVM(t *testing.T) { acc.deposit(from: <- amount) destroy acc - // commit blocks + // commit blocks evmHeartbeat.heartbeat() } }`, @@ -3086,3 +3086,504 @@ func TestEVM(t *testing.T) { }), ) } + +func TestAccountCapabilitiesGetEntitledRejection(t *testing.T) { + + // Note: This cannot be tested anymore using a transaction, + // because publish method also aborts when trying to publish an entitled capability. + // Therefore, test the functionality of the `ValidateAccountCapabilitiesGet` function. + + t.Run("entitled capability", func(t *testing.T) { + + env := environment.NewScriptEnv( + context.TODO(), + tracing.NewMockTracerSpan(), + environment.DefaultEnvironmentParams(), + nil, + ) + + valid, err := env.ValidateAccountCapabilitiesGet( + nil, + interpreter.EmptyLocationRange, + interpreter.AddressValue(common.ZeroAddress), + interpreter.NewUnmeteredPathValue(common.PathDomainPublic, "dummy_value"), + sema.NewReferenceType( + nil, + sema.NewEntitlementSetAccess( + []*sema.EntitlementType{ + sema.MutateType, + }, + sema.Conjunction, + ), + sema.IntType, + ), + nil, + ) + assert.NoError(t, err) + assert.False(t, valid) + }) + + t.Run("non-entitled capability", func(t *testing.T) { + + env := environment.NewScriptEnv( + context.TODO(), + tracing.NewMockTracerSpan(), + environment.DefaultEnvironmentParams(), + nil, + ) + + valid, err := env.ValidateAccountCapabilitiesGet( + nil, + interpreter.EmptyLocationRange, + interpreter.AddressValue(common.ZeroAddress), + interpreter.NewUnmeteredPathValue(common.PathDomainPublic, "dummy_value"), + sema.NewReferenceType( + nil, + sema.UnauthorizedAccess, + sema.IntType, + ), + nil, + ) + assert.NoError(t, err) + assert.True(t, valid) + }) +} + +func TestAccountCapabilitiesPublishEntitledRejection(t *testing.T) { + + t.Run("entitled capability", newVMTest(). + run(func( + t *testing.T, + vm fvm.VM, + chain flow.Chain, + ctx fvm.Context, + snapshotTree snapshot.SnapshotTree, + ) { + + serviceAddress := chain.ServiceAddress() + txBody := flow.NewTransactionBody(). + SetScript([]byte(` + transaction { + prepare(signer: auth(Capabilities, Storage) &Account) { + signer.storage.save(42, to: /storage/number) + let cap = signer.capabilities.storage.issue(/storage/number) + signer.capabilities.publish(cap, at: /public/number) + } + } + `)). + AddAuthorizer(serviceAddress). + SetProposalKey(serviceAddress, 0, 0). + SetPayer(serviceAddress) + + err := testutil.SignTransactionAsServiceAccount(txBody, 0, chain) + require.NoError(t, err) + + _, output, err := vm.Run( + ctx, + fvm.Transaction(txBody, 0), + snapshotTree) + + require.NoError(t, err) + require.ErrorAs(t, output.Err, &interpreter.EntitledCapabilityPublishingError{}) + }), + ) + + t.Run("non entitled capability", newVMTest(). + run(func( + t *testing.T, + vm fvm.VM, + chain flow.Chain, + ctx fvm.Context, + snapshotTree snapshot.SnapshotTree, + ) { + + serviceAddress := chain.ServiceAddress() + txBody := flow.NewTransactionBody(). + SetScript([]byte(` + transaction { + prepare(signer: auth(Capabilities, Storage) &Account) { + signer.storage.save(42, to: /storage/number) + let cap = signer.capabilities.storage.issue<&Int>(/storage/number) + signer.capabilities.publish(cap, at: /public/number) + } + } + `)). + AddAuthorizer(serviceAddress). + SetProposalKey(serviceAddress, 0, 0). + SetPayer(serviceAddress) + + err := testutil.SignTransactionAsServiceAccount(txBody, 0, chain) + require.NoError(t, err) + + _, output, err := vm.Run( + ctx, + fvm.Transaction(txBody, 0), + snapshotTree) + + require.NoError(t, err) + require.NoError(t, output.Err) + }), + ) +} + +func TestCrypto(t *testing.T) { + t.Parallel() + + const chainID = flow.Testnet + + test := func(t *testing.T, importDecl string) { + + chain, vm := createChainAndVm(chainID) + + ctx := fvm.NewContext( + fvm.WithChain(chain), + fvm.WithCadenceLogging(true), + ) + + script := []byte(fmt.Sprintf( + ` + %s + + access(all) + fun main( + rawPublicKeys: [String], + weights: [UFix64], + domainSeparationTag: String, + signatures: [String], + toAddress: Address, + fromAddress: Address, + amount: UFix64 + ): Bool { + let keyList = Crypto.KeyList() + + var i = 0 + for rawPublicKey in rawPublicKeys { + keyList.add( + PublicKey( + publicKey: rawPublicKey.decodeHex(), + signatureAlgorithm: SignatureAlgorithm.ECDSA_P256 + ), + hashAlgorithm: HashAlgorithm.SHA3_256, + weight: weights[i], + ) + i = i + 1 + } + + let signatureSet: [Crypto.KeyListSignature] = [] + + var j = 0 + for signature in signatures { + signatureSet.append( + Crypto.KeyListSignature( + keyIndex: j, + signature: signature.decodeHex() + ) + ) + j = j + 1 + } + + // assemble the same message in cadence + let message = toAddress.toBytes() + .concat(fromAddress.toBytes()) + .concat(amount.toBigEndianBytes()) + + return keyList.verify( + signatureSet: signatureSet, + signedData: message, + domainSeparationTag: domainSeparationTag + ) + } + `, + importDecl, + )) + + accountKeys := test.AccountKeyGenerator() + + // create the keys + keyAlice, signerAlice := accountKeys.NewWithSigner() + keyBob, signerBob := accountKeys.NewWithSigner() + + // create the message that will be signed + addresses := test.AddressGenerator() + + toAddress := cadence.Address(addresses.New()) + fromAddress := cadence.Address(addresses.New()) + + amount, err := cadence.NewUFix64("100.00") + require.NoError(t, err) + + var message []byte + message = append(message, toAddress.Bytes()...) + message = append(message, fromAddress.Bytes()...) + message = append(message, amount.ToBigEndianBytes()...) + + // sign the message with Alice and Bob + signatureAlice, err := flowsdk.SignUserMessage(signerAlice, message) + require.NoError(t, err) + + signatureBob, err := flowsdk.SignUserMessage(signerBob, message) + require.NoError(t, err) + + publicKeys := cadence.NewArray([]cadence.Value{ + cadence.String(hex.EncodeToString(keyAlice.PublicKey.Encode())), + cadence.String(hex.EncodeToString(keyBob.PublicKey.Encode())), + }) + + // each signature has half weight + weightAlice, err := cadence.NewUFix64("0.5") + require.NoError(t, err) + + weightBob, err := cadence.NewUFix64("0.5") + require.NoError(t, err) + + weights := cadence.NewArray([]cadence.Value{ + weightAlice, + weightBob, + }) + + signatures := cadence.NewArray([]cadence.Value{ + cadence.String(hex.EncodeToString(signatureAlice)), + cadence.String(hex.EncodeToString(signatureBob)), + }) + + domainSeparationTag := cadence.String("FLOW-V0.0-user") + + arguments := []cadence.Value{ + publicKeys, + weights, + domainSeparationTag, + signatures, + toAddress, + fromAddress, + amount, + } + + encodedArguments := make([][]byte, 0, len(arguments)) + for _, argument := range arguments { + encodedArguments = append(encodedArguments, jsoncdc.MustEncode(argument)) + } + + snapshotTree := testutil.RootBootstrappedLedger(vm, ctx) + + _, output, err := vm.Run( + ctx, + fvm.Script(script). + WithArguments(encodedArguments...), + snapshotTree) + require.NoError(t, err) + + require.NoError(t, output.Err) + + result := output.Value + + assert.Equal(t, + cadence.NewBool(true), + result, + ) + } + + t.Run("identifier location", func(t *testing.T) { + t.Parallel() + + test(t, "import Crypto") + }) + + t.Run("address location", func(t *testing.T) { + t.Parallel() + + sc := systemcontracts.SystemContractsForChain(chainID) + cryptoContractAddress := sc.Crypto.Address.HexWithPrefix() + + test(t, fmt.Sprintf("import Crypto from %s", cryptoContractAddress)) + }) +} + +func Test_MinimumRequiredVersion(t *testing.T) { + + chain := flow.Emulator.Chain() + sc := systemcontracts.SystemContractsForChain(chain.ChainID()) + log := zerolog.New(zerolog.NewTestWriter(t)) + + getVersion := func(ctx fvm.Context, snapshotTree snapshot.SnapshotTree) string { + blockDatabase := storage.NewBlockDatabase( + snapshotTree, + 0, + nil) + + txnState, err := blockDatabase.NewTransaction(0, state.DefaultParameters()) + require.NoError(t, err) + + executionParams, _, err := txnState.GetStateExecutionParameters( + txnState, + fvm.NewExecutionParametersComputer(log, ctx, txnState)) + require.NoError(t, err) + + // this will set the parameters to the txnState. + // this is done at the beginning of a transaction/script + txnId, err := txnState.BeginNestedTransactionWithMeterParams( + state.ExecutionParameters{ + ExecutionVersion: executionParams.ExecutionVersion, + }) + require.NoError(t, err) + + mrv := environment.NewMinimumCadenceRequiredVersion(txnState) + + v, err := mrv.MinimumRequiredVersion() + + require.NoError(t, err) + _, err = txnState.CommitNestedTransaction(txnId) + require.NoError(t, err) + + return v + } + + insertVersionBoundary := func(newVersion semver.Version, currentHeight, insertHeight uint64, ctx fvm.Context, snapshotTree snapshot.SnapshotTree, vm fvm.VM, txIndex uint32) snapshot.SnapshotTree { + setVersionBoundaryScript := templates.GenerateSetVersionBoundaryScript(sc.AsTemplateEnv()) + tx := flow.NewTransactionBody(). + SetScript(setVersionBoundaryScript). + SetProposalKey(sc.FlowServiceAccount.Address, 0, 0). + AddAuthorizer(sc.FlowServiceAccount.Address). + SetPayer(sc.FlowServiceAccount.Address) + + tx. + AddArgument(jsoncdc.MustEncode(cadence.UInt8(newVersion.Major))). + AddArgument(jsoncdc.MustEncode(cadence.UInt8(newVersion.Minor))). + AddArgument(jsoncdc.MustEncode(cadence.UInt8(newVersion.Patch))). + AddArgument(jsoncdc.MustEncode(cadence.String(newVersion.PreRelease))) + + tx.AddArgument(jsoncdc.MustEncode(cadence.UInt64(insertHeight))) + + startHeader := flow.Header{ + Height: currentHeight, + ChainID: chain.ChainID(), + Timestamp: time.Now().UTC(), + } + + blocks := new(envMock.Blocks) + ctxWithBlock := fvm.NewContextFromParent( + ctx, + fvm.WithBlockHeader(&startHeader), + fvm.WithBlocks(blocks), + ) + + executionSnapshot, output, err := vm.Run( + ctxWithBlock, + fvm.Transaction(tx, txIndex), + snapshotTree) + + require.NoError(t, err) + require.NoError(t, output.Err) + return snapshotTree.Append(executionSnapshot) + } + + runSystemTxToUpdateNodeVersionBeaconContract := func(atHeight uint64, ctx fvm.Context, snapshotTree snapshot.SnapshotTree, vm fvm.VM, txIndex uint32) snapshot.SnapshotTree { + txBody := flow.NewTransactionBody(). + SetScript([]byte(fmt.Sprintf(` + import NodeVersionBeacon from %s + + transaction { + prepare(serviceAccount: auth(BorrowValue) &Account) { + + let versionBeaconHeartbeat = serviceAccount.storage + .borrow<&NodeVersionBeacon.Heartbeat>(from: NodeVersionBeacon.HeartbeatStoragePath) + ?? panic("Couldn't borrow NodeVersionBeacon.Heartbeat Resource") + versionBeaconHeartbeat.heartbeat() + } + } + `, + sc.NodeVersionBeacon.Address.HexWithPrefix()))). + SetProposalKey(sc.FlowServiceAccount.Address, 0, 0). + AddAuthorizer(sc.FlowServiceAccount.Address). + SetPayer(sc.FlowServiceAccount.Address) + + endHeader := flow.Header{ + Height: atHeight, + ChainID: chain.ChainID(), + Timestamp: time.Now().UTC(), + } + + blocks := new(envMock.Blocks) + ctxWithBlock := fvm.NewContextFromParent(ctx, + fvm.WithBlockHeader(&endHeader), + fvm.WithBlocks(blocks), + ) + + executionSnapshot, output, err := vm.Run( + ctxWithBlock, + fvm.Transaction(txBody, txIndex), + snapshotTree) + + require.NoError(t, err) + require.NoError(t, output.Err) + + return snapshotTree.Append(executionSnapshot) + } + + t.Run("minimum required version", newVMTest(). + withContextOptions( + fvm.WithChain(chain), + fvm.WithAuthorizationChecksEnabled(false), + fvm.WithSequenceNumberCheckAndIncrementEnabled(false), + ). + run(func( + t *testing.T, + vm fvm.VM, + chain flow.Chain, + ctx fvm.Context, + snapshotTree snapshot.SnapshotTree, + ) { + // default version is empty + require.Equal(t, semver.Version{}.String(), getVersion(ctx, snapshotTree)) + + // define mapping for flow go version to cadence version + flowVersion1 := semver.Version{ + Major: 1, + Minor: 2, + Patch: 3, + PreRelease: "rc.1", + } + cadenceVersion1 := semver.Version{ + Major: 2, + Minor: 1, + Patch: 3, + PreRelease: "rc.2", + } + environment.SetFVMToCadenceVersionMappingForTestingOnly( + environment.FlowGoToCadenceVersionMapping{ + FlowGoVersion: flowVersion1, + CadenceVersion: cadenceVersion1, + }) + + h0 := uint64(100) // starting height + hv1 := uint64(2000) // version boundary height + + txIndex := uint32(0) + + // insert version boundary 1 + snapshotTree = insertVersionBoundary(flowVersion1, h0, hv1, ctx, snapshotTree, vm, txIndex) + txIndex += 1 + + // so far no change: + require.Equal(t, semver.Version{}.String(), getVersion(ctx, snapshotTree)) + + // system transaction needs to run to update the flowVersion on chain + snapshotTree = runSystemTxToUpdateNodeVersionBeaconContract(hv1-1, ctx, snapshotTree, vm, txIndex) + txIndex += 1 + + // no change: + require.Equal(t, semver.Version{}.String(), getVersion(ctx, snapshotTree)) + + // system transaction needs to run to update the flowVersion on chain + snapshotTree = runSystemTxToUpdateNodeVersionBeaconContract(hv1, ctx, snapshotTree, vm, txIndex) + txIndex += 1 + + // switch to cadence version 1 + require.Equal(t, cadenceVersion1.String(), getVersion(ctx, snapshotTree)) + + // system transaction needs to run to update the flowVersion on chain + snapshotTree = runSystemTxToUpdateNodeVersionBeaconContract(hv1+1, ctx, snapshotTree, vm, txIndex) + + // still cadence version 1 + require.Equal(t, cadenceVersion1.String(), getVersion(ctx, snapshotTree)) + })) +} diff --git a/fvm/meter/computation_meter.go b/fvm/meter/computation_meter.go index 4966650d748..d6f7fe55331 100644 --- a/fvm/meter/computation_meter.go +++ b/fvm/meter/computation_meter.go @@ -3,7 +3,7 @@ package meter import ( "math" - "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/common" "github.com/onflow/flow-go/fvm/errors" ) @@ -116,10 +116,28 @@ func (m *ComputationMeter) ComputationAvailable( if !ok { return true } + potentialComputationUsage := m.computationUsed + w*uint64(intensity) return potentialComputationUsage <= m.params.computationLimit } +// ComputationRemaining returns the remaining computation (intensity) left in the transaction for the given type +func (m *ComputationMeter) ComputationRemaining(kind common.ComputationKind) uint { + w, ok := m.params.computationWeights[kind] + // if not found return has capacity + // given the behaviour of MeterComputation is ignoring intensities without a set weight + if !ok { + return math.MaxUint + } + + remainingComputationUsage := m.params.computationLimit - m.computationUsed + if remainingComputationUsage <= 0 { + return 0 + } + + return uint(remainingComputationUsage / w) +} + // ComputationIntensities returns all the measured computational intensities func (m *ComputationMeter) ComputationIntensities() MeteredComputationIntensities { return m.computationIntensities diff --git a/fvm/meter/memory_meter.go b/fvm/meter/memory_meter.go index 4ed54a3cc89..c9f2ca35d4a 100644 --- a/fvm/meter/memory_meter.go +++ b/fvm/meter/memory_meter.go @@ -3,7 +3,7 @@ package meter import ( "math" - "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/common" "github.com/onflow/flow-go/fvm/errors" ) diff --git a/fvm/meter/meter_test.go b/fvm/meter/meter_test.go index 4234966c260..f691b03b6d0 100644 --- a/fvm/meter/meter_test.go +++ b/fvm/meter/meter_test.go @@ -8,7 +8,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/common" "github.com/onflow/flow-go/fvm/errors" "github.com/onflow/flow-go/fvm/meter" @@ -139,6 +139,27 @@ func TestWeightedComputationMetering(t *testing.T) { require.True(t, m.ComputationAvailable(1, 10)) }) + t.Run("check computation remaining", func(t *testing.T) { + m := meter.NewMeter( + meter.DefaultParameters(). + WithComputationLimit(10). + WithComputationWeights( + map[common.ComputationKind]uint64{0: 1 << meter.MeterExecutionInternalPrecisionBytes}), + ) + + remaining := m.ComputationRemaining(0) + require.Equal(t, uint(10), remaining) + + err := m.MeterComputation(0, 1) + require.NoError(t, err) + require.Equal(t, uint64(1), m.TotalComputationUsed()) + + require.Equal(t, uint(9), m.ComputationRemaining(0)) + + // test a type without a weight (default zero) + require.Equal(t, uint(math.MaxUint), m.ComputationRemaining(1)) + }) + t.Run("merge meters", func(t *testing.T) { compKind := common.ComputationKind(0) m := meter.NewMeter( diff --git a/fvm/runtime/reusable_cadence_runtime.go b/fvm/runtime/reusable_cadence_runtime.go index 0248e55a7df..138156897b1 100644 --- a/fvm/runtime/reusable_cadence_runtime.go +++ b/fvm/runtime/reusable_cadence_runtime.go @@ -2,11 +2,11 @@ package runtime import ( "github.com/onflow/cadence" + "github.com/onflow/cadence/common" + "github.com/onflow/cadence/interpreter" "github.com/onflow/cadence/runtime" - "github.com/onflow/cadence/runtime/common" - "github.com/onflow/cadence/runtime/interpreter" - "github.com/onflow/cadence/runtime/sema" - "github.com/onflow/cadence/runtime/stdlib" + "github.com/onflow/cadence/sema" + "github.com/onflow/cadence/stdlib" "github.com/onflow/flow-go/fvm/errors" ) diff --git a/fvm/runtime/testutil/runtime.go b/fvm/runtime/testutil/runtime.go index 885964e6e6f..94d2304bc54 100644 --- a/fvm/runtime/testutil/runtime.go +++ b/fvm/runtime/testutil/runtime.go @@ -2,10 +2,10 @@ package testutil import ( "github.com/onflow/cadence" + "github.com/onflow/cadence/common" + "github.com/onflow/cadence/interpreter" "github.com/onflow/cadence/runtime" - "github.com/onflow/cadence/runtime/common" - "github.com/onflow/cadence/runtime/interpreter" - "github.com/onflow/cadence/runtime/sema" + "github.com/onflow/cadence/sema" ) var _ runtime.Runtime = &TestInterpreterRuntime{} diff --git a/fvm/runtime/wrapped_cadence_runtime.go b/fvm/runtime/wrapped_cadence_runtime.go index 8b53b0f31b0..b57512509d4 100644 --- a/fvm/runtime/wrapped_cadence_runtime.go +++ b/fvm/runtime/wrapped_cadence_runtime.go @@ -2,10 +2,10 @@ package runtime import ( "github.com/onflow/cadence" + "github.com/onflow/cadence/common" + "github.com/onflow/cadence/interpreter" "github.com/onflow/cadence/runtime" - "github.com/onflow/cadence/runtime/common" - "github.com/onflow/cadence/runtime/interpreter" - "github.com/onflow/cadence/runtime/sema" + "github.com/onflow/cadence/sema" "github.com/onflow/flow-go/fvm/errors" ) diff --git a/fvm/script.go b/fvm/script.go index 1b5d247b640..4d8a86323b8 100644 --- a/fvm/script.go +++ b/fvm/script.go @@ -5,8 +5,8 @@ import ( "fmt" "github.com/hashicorp/go-multierror" + "github.com/onflow/cadence/common" "github.com/onflow/cadence/runtime" - "github.com/onflow/cadence/runtime/common" "github.com/onflow/flow-go/fvm/environment" "github.com/onflow/flow-go/fvm/errors" @@ -173,7 +173,8 @@ func (executor *scriptExecutor) Execute() error { } func (executor *scriptExecutor) execute() error { - meterParams, err := getBodyMeterParameters( + executionParams, _, err := getExecutionParameters( + executor.env.Logger(), executor.ctx, executor.proc, executor.txnState) @@ -182,7 +183,7 @@ func (executor *scriptExecutor) execute() error { } txnId, err := executor.txnState.BeginNestedTransactionWithMeterParams( - meterParams) + executionParams) if err != nil { return err } diff --git a/fvm/storage/derived/dependencies.go b/fvm/storage/derived/dependencies.go index c5ba3b85e29..65ece9dc173 100644 --- a/fvm/storage/derived/dependencies.go +++ b/fvm/storage/derived/dependencies.go @@ -1,7 +1,7 @@ package derived import ( - "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/common" ) // ProgramDependencies are the locations of programs that a program depends on. diff --git a/fvm/storage/derived/dependencies_test.go b/fvm/storage/derived/dependencies_test.go index 90bb1e09482..e94eb742395 100644 --- a/fvm/storage/derived/dependencies_test.go +++ b/fvm/storage/derived/dependencies_test.go @@ -3,7 +3,7 @@ package derived_test import ( "testing" - "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/common" "github.com/stretchr/testify/require" "github.com/onflow/flow-go/fvm/storage/derived" diff --git a/fvm/storage/derived/derived_block_data.go b/fvm/storage/derived/derived_block_data.go index 69a8b0990d0..14deca315b7 100644 --- a/fvm/storage/derived/derived_block_data.go +++ b/fvm/storage/derived/derived_block_data.go @@ -3,10 +3,11 @@ package derived import ( "fmt" - "github.com/onflow/cadence/runtime/common" - "github.com/onflow/cadence/runtime/interpreter" + "github.com/onflow/cadence/common" + "github.com/onflow/cadence/interpreter" "github.com/onflow/flow-go/fvm/storage/logical" + "github.com/onflow/flow-go/fvm/storage/snapshot" "github.com/onflow/flow-go/fvm/storage/state" ) @@ -21,11 +22,13 @@ type DerivedTransactionPreparer interface { ) GetProgram(location common.AddressLocation) (*Program, bool) - GetMeterParamOverrides( + // GetStateExecutionParameters returns parameters needed for execution from the state. + GetStateExecutionParameters( txnState state.NestedTransactionPreparer, - getMeterParamOverrides ValueComputer[struct{}, MeterParamOverrides], + getMeterParamOverrides ValueComputer[struct{}, StateExecutionParameters], ) ( - MeterParamOverrides, + StateExecutionParameters, + *snapshot.ExecutionSnapshot, error, ) @@ -43,7 +46,7 @@ type Program struct { type DerivedBlockData struct { programs *DerivedDataTable[common.AddressLocation, *Program] - meterParamOverrides *DerivedDataTable[struct{}, MeterParamOverrides] + meterParamOverrides *DerivedDataTable[struct{}, StateExecutionParameters] } // DerivedTransactionData is the derived data scratch space for a single @@ -56,7 +59,7 @@ type DerivedTransactionData struct { // There's only a single entry in this table. For simplicity, we'll use // struct{} as the entry's key. - meterParamOverrides *TableTransaction[struct{}, MeterParamOverrides] + executionParameters *TableTransaction[struct{}, StateExecutionParameters] } func NewEmptyDerivedBlockData( @@ -69,7 +72,7 @@ func NewEmptyDerivedBlockData( ](initialSnapshotTime), meterParamOverrides: NewEmptyTable[ struct{}, - MeterParamOverrides, + StateExecutionParameters, ](initialSnapshotTime), } } @@ -84,14 +87,14 @@ func (block *DerivedBlockData) NewChildDerivedBlockData() *DerivedBlockData { func (block *DerivedBlockData) NewSnapshotReadDerivedTransactionData() *DerivedTransactionData { return &DerivedTransactionData{ programs: block.programs.NewSnapshotReadTableTransaction(), - meterParamOverrides: block.meterParamOverrides.NewSnapshotReadTableTransaction(), + executionParameters: block.meterParamOverrides.NewSnapshotReadTableTransaction(), } } func (block *DerivedBlockData) NewCachingSnapshotReadDerivedTransactionData() *DerivedTransactionData { return &DerivedTransactionData{ programs: block.programs.NewCachingSnapshotReadTableTransaction(), - meterParamOverrides: block.meterParamOverrides.NewCachingSnapshotReadTableTransaction(), + executionParameters: block.meterParamOverrides.NewCachingSnapshotReadTableTransaction(), } } @@ -118,7 +121,7 @@ func (block *DerivedBlockData) NewDerivedTransactionData( return &DerivedTransactionData{ programs: txnPrograms, - meterParamOverrides: txnMeterParamOverrides, + executionParameters: txnMeterParamOverrides, }, nil } @@ -175,18 +178,19 @@ func (transaction *DerivedTransactionData) AddInvalidator( } transaction.programs.AddInvalidator(invalidator.ProgramInvalidator()) - transaction.meterParamOverrides.AddInvalidator( - invalidator.MeterParamOverridesInvalidator()) + transaction.executionParameters.AddInvalidator( + invalidator.ExecutionParametersInvalidator()) } -func (transaction *DerivedTransactionData) GetMeterParamOverrides( +func (transaction *DerivedTransactionData) GetStateExecutionParameters( txnState state.NestedTransactionPreparer, - getMeterParamOverrides ValueComputer[struct{}, MeterParamOverrides], + getMeterParamOverrides ValueComputer[struct{}, StateExecutionParameters], ) ( - MeterParamOverrides, + StateExecutionParameters, + *snapshot.ExecutionSnapshot, error, ) { - return transaction.meterParamOverrides.GetOrCompute( + return transaction.executionParameters.GetWithStateOrCompute( txnState, struct{}{}, getMeterParamOverrides) @@ -198,7 +202,7 @@ func (transaction *DerivedTransactionData) Validate() error { return fmt.Errorf("programs validate failed: %w", err) } - err = transaction.meterParamOverrides.Validate() + err = transaction.executionParameters.Validate() if err != nil { return fmt.Errorf("meter param overrides validate failed: %w", err) } @@ -212,7 +216,7 @@ func (transaction *DerivedTransactionData) Commit() error { return fmt.Errorf("programs commit failed: %w", err) } - err = transaction.meterParamOverrides.Commit() + err = transaction.executionParameters.Commit() if err != nil { return fmt.Errorf("meter param overrides commit failed: %w", err) } diff --git a/fvm/storage/derived/derived_chain_data_test.go b/fvm/storage/derived/derived_chain_data_test.go index 0c79af2f603..5bd303f5785 100644 --- a/fvm/storage/derived/derived_chain_data_test.go +++ b/fvm/storage/derived/derived_chain_data_test.go @@ -3,8 +3,8 @@ package derived import ( "testing" - "github.com/onflow/cadence/runtime/common" - "github.com/onflow/cadence/runtime/interpreter" + "github.com/onflow/cadence/common" + "github.com/onflow/cadence/interpreter" "github.com/stretchr/testify/require" diff --git a/fvm/storage/derived/invalidator.go b/fvm/storage/derived/invalidator.go index 63e3ee290d5..9467c169bdc 100644 --- a/fvm/storage/derived/invalidator.go +++ b/fvm/storage/derived/invalidator.go @@ -1,7 +1,8 @@ package derived import ( - "github.com/onflow/cadence/runtime/common" + "github.com/coreos/go-semver/semver" + "github.com/onflow/cadence/common" "github.com/onflow/flow-go/fvm/meter" ) @@ -12,17 +13,23 @@ type MeterParamOverrides struct { MemoryLimit *uint64 // nil indicates no override } +// StateExecutionParameters are parameters needed for execution defined in the execution state. +type StateExecutionParameters struct { + MeterParamOverrides + ExecutionVersion semver.Version +} + type ProgramInvalidator TableInvalidator[ common.AddressLocation, *Program, ] -type MeterParamOverridesInvalidator TableInvalidator[ +type ExecutionParametersInvalidator TableInvalidator[ struct{}, - MeterParamOverrides, + StateExecutionParameters, ] type TransactionInvalidator interface { ProgramInvalidator() ProgramInvalidator - MeterParamOverridesInvalidator() MeterParamOverridesInvalidator + ExecutionParametersInvalidator() ExecutionParametersInvalidator } diff --git a/fvm/storage/derived/table.go b/fvm/storage/derived/table.go index 6698f7a6db1..2c9a70631ea 100644 --- a/fvm/storage/derived/table.go +++ b/fvm/storage/derived/table.go @@ -466,6 +466,27 @@ func (txn *TableTransaction[TKey, TVal]) GetOrCompute( ) ( TVal, error, +) { + val, _, err := txn.GetWithStateOrCompute(txnState, key, computer) + return val, err +} + +// GetWithStateOrCompute returns the key's value and the execution snapshot used to +// compute it. If a pre-computed value is available, +// then the pre-computed value is returned and the cached state is replayed on +// txnState. Otherwise, the value is computed using valFunc; both the value +// and the states used to compute the value are captured. +// +// Note: valFunc must be an idempotent function and it must not modify +// txnState's values. +func (txn *TableTransaction[TKey, TVal]) GetWithStateOrCompute( + txnState state.NestedTransactionPreparer, + key TKey, + computer ValueComputer[TKey, TVal], +) ( + TVal, + *snapshot.ExecutionSnapshot, + error, ) { var defaultVal TVal @@ -473,17 +494,17 @@ func (txn *TableTransaction[TKey, TVal]) GetOrCompute( if ok { err := txnState.AttachAndCommitNestedTransaction(state) if err != nil { - return defaultVal, fmt.Errorf( + return defaultVal, nil, fmt.Errorf( "failed to replay cached state: %w", err) } - return val, nil + return val, state, nil } nestedTxId, err := txnState.BeginNestedTransaction() if err != nil { - return defaultVal, fmt.Errorf("failed to start nested txn: %w", err) + return defaultVal, nil, fmt.Errorf("failed to start nested txn: %w", err) } val, err = computer.Compute(txnState, key) @@ -497,12 +518,12 @@ func (txn *TableTransaction[TKey, TVal]) GetOrCompute( } if err != nil { - return defaultVal, fmt.Errorf("failed to derive value: %w", err) + return defaultVal, nil, fmt.Errorf("failed to derive value: %w", err) } txn.set(key, val, committedState) - return val, nil + return val, committedState, nil } func (txn *TableTransaction[TKey, TVal]) AddInvalidator( diff --git a/fvm/storage/state/execution_state.go b/fvm/storage/state/execution_state.go index 909b91b374f..12968e4b0a6 100644 --- a/fvm/storage/state/execution_state.go +++ b/fvm/storage/state/execution_state.go @@ -2,8 +2,11 @@ package state import ( "fmt" + "math" - "github.com/onflow/cadence/runtime/common" + "github.com/coreos/go-semver/semver" + + "github.com/onflow/cadence/common" "github.com/onflow/crypto/hash" "github.com/onflow/flow-go/fvm/errors" @@ -27,7 +30,8 @@ type ExecutionState struct { finalized bool *spockState - meter *meter.Meter + meter *meter.Meter + executionVersion semver.Version // NOTE: parent and child state shares the same limits controller *limitsController @@ -40,6 +44,11 @@ type StateParameters struct { maxValueSizeAllowed uint64 } +type ExecutionParameters struct { + meter.MeterParameters + ExecutionVersion semver.Version +} + func DefaultParameters() StateParameters { return StateParameters{ MeterParameters: meter.DefaultParameters(), @@ -129,19 +138,20 @@ func NewExecutionStateWithSpockStateHasher( // NewChildWithMeterParams generates a new child state using the provide meter // parameters. func (state *ExecutionState) NewChildWithMeterParams( - params meter.MeterParameters, + params ExecutionParameters, ) *ExecutionState { return &ExecutionState{ finalized: false, spockState: state.spockState.NewChild(), - meter: meter.NewMeter(params), + meter: meter.NewMeter(params.MeterParameters), + executionVersion: params.ExecutionVersion, limitsController: state.limitsController, } } // NewChild generates a new child state using the parent's meter parameters. func (state *ExecutionState) NewChild() *ExecutionState { - return state.NewChildWithMeterParams(state.meter.MeterParameters) + return state.NewChildWithMeterParams(state.ExecutionParameters()) } // InteractionUsed returns the amount of ledger interaction (total ledger byte read + total ledger byte written) @@ -235,6 +245,20 @@ func (state *ExecutionState) ComputationAvailable(kind common.ComputationKind, i return true } +// ComputationRemaining returns the available computation capacity without metering +func (state *ExecutionState) ComputationRemaining(kind common.ComputationKind) uint { + if state.finalized { + // if state is finalized return 0 + return 0 + } + + if state.enforceLimits { + return state.meter.ComputationRemaining(kind) + } + + return math.MaxUint +} + // TotalComputationUsed returns total computation used func (state *ExecutionState) TotalComputationUsed() uint64 { return state.meter.TotalComputationUsed() @@ -337,6 +361,13 @@ func (state *ExecutionState) checkSize( return nil } +func (state *ExecutionState) ExecutionParameters() ExecutionParameters { + return ExecutionParameters{ + MeterParameters: state.meter.MeterParameters, + ExecutionVersion: state.executionVersion, + } +} + func (state *ExecutionState) readSetSize() int { return state.spockState.readSetSize() } diff --git a/fvm/storage/state/transaction_state.go b/fvm/storage/state/transaction_state.go index 2a75c632bbc..1b00bf78973 100644 --- a/fvm/storage/state/transaction_state.go +++ b/fvm/storage/state/transaction_state.go @@ -3,7 +3,7 @@ package state import ( "fmt" - "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/common" "github.com/onflow/flow-go/fvm/meter" "github.com/onflow/flow-go/fvm/storage/snapshot" @@ -22,6 +22,7 @@ func (id NestedTransactionId) StateForTestingOnly() *ExecutionState { type Meter interface { MeterComputation(kind common.ComputationKind, intensity uint) error ComputationAvailable(kind common.ComputationKind, intensity uint) bool + ComputationRemaining(kind common.ComputationKind) uint ComputationIntensities() meter.MeteredComputationIntensities TotalComputationLimit() uint TotalComputationUsed() uint64 @@ -44,6 +45,9 @@ type Meter interface { type NestedTransactionPreparer interface { Meter + // ExecutionParameters returns the execution parameters + ExecutionParameters() ExecutionParameters + // NumNestedTransactions returns the number of uncommitted nested // transactions. Note that the main transaction is not considered a // nested transaction. @@ -83,7 +87,7 @@ type NestedTransactionPreparer interface { // the provided meter parameters. This returns error if the current nested // transaction is program restricted. BeginNestedTransactionWithMeterParams( - params meter.MeterParameters, + params ExecutionParameters, ) ( NestedTransactionId, error, @@ -199,6 +203,10 @@ func (txnState *transactionState) current() nestedTransactionStackFrame { return txnState.nestedTransactions[txnState.NumNestedTransactions()] } +func (txnState *transactionState) ExecutionParameters() ExecutionParameters { + return txnState.current().ExecutionParameters() +} + func (txnState *transactionState) NumNestedTransactions() int { return len(txnState.nestedTransactions) - 1 } @@ -266,7 +274,7 @@ func (txnState *transactionState) BeginNestedTransaction() ( } func (txnState *transactionState) BeginNestedTransactionWithMeterParams( - params meter.MeterParameters, + params ExecutionParameters, ) ( NestedTransactionId, error, @@ -451,6 +459,10 @@ func (txnState *transactionState) ComputationAvailable( return txnState.current().ComputationAvailable(kind, intensity) } +func (txnState *transactionState) ComputationRemaining(kind common.ComputationKind) uint { + return txnState.current().ComputationRemaining(kind) +} + func (txnState *transactionState) MeterMemory( kind common.MemoryKind, intensity uint, diff --git a/fvm/storage/state/transaction_state_test.go b/fvm/storage/state/transaction_state_test.go index 87144f14b0f..f8b18b5d186 100644 --- a/fvm/storage/state/transaction_state_test.go +++ b/fvm/storage/state/transaction_state_test.go @@ -4,7 +4,7 @@ import ( "math" "testing" - "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/common" "github.com/stretchr/testify/require" "github.com/onflow/flow-go/fvm/meter" @@ -104,7 +104,9 @@ func TestUnrestrictedNestedTransactionDifferentMeterParams(t *testing.T) { require.Equal(t, uint(math.MaxUint), mainState.TotalMemoryLimit()) id1, err := txn.BeginNestedTransactionWithMeterParams( - meter.DefaultParameters().WithMemoryLimit(1)) + state.ExecutionParameters{ + MeterParameters: meter.DefaultParameters().WithMemoryLimit(1), + }) require.NoError(t, err) nestedState1 := id1.StateForTestingOnly() @@ -112,7 +114,9 @@ func TestUnrestrictedNestedTransactionDifferentMeterParams(t *testing.T) { require.Equal(t, uint(1), nestedState1.TotalMemoryLimit()) id2, err := txn.BeginNestedTransactionWithMeterParams( - meter.DefaultParameters().WithMemoryLimit(2)) + state.ExecutionParameters{ + MeterParameters: meter.DefaultParameters().WithMemoryLimit(2), + }) require.NoError(t, err) nestedState2 := id2.StateForTestingOnly() diff --git a/fvm/systemcontracts/system_contracts.go b/fvm/systemcontracts/system_contracts.go index 7fd321d88bc..ad9f66c4a65 100644 --- a/fvm/systemcontracts/system_contracts.go +++ b/fvm/systemcontracts/system_contracts.go @@ -16,7 +16,7 @@ package systemcontracts import ( "fmt" - "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/common" "github.com/onflow/flow-core-contracts/lib/go/templates" "github.com/onflow/flow-go/model/flow" @@ -43,6 +43,7 @@ const ( ContractNameViewResolver = "ViewResolver" ContractNameEVM = "EVM" ContractNameBurner = "Burner" + ContractNameCrypto = "Crypto" // AccountNameEVMStorage is not a contract, but a special account that is used to store EVM state AccountNameEVMStorage = "EVMStorageAccount" @@ -64,6 +65,7 @@ const ( ContractStorageFeesFunction_calculateAccountCapacity = "calculateAccountCapacity" ContractStorageFeesFunction_getAccountsCapacityForTransactionStorageCheck = "getAccountsCapacityForTransactionStorageCheck" ContractStorageFeesFunction_defaultTokenAvailableBalance = "defaultTokenAvailableBalance" + ContractVersionBeacon_getCurrentVersionBoundary = "getCurrentVersionBoundary" // These are the account indexes of system contracts as deployed by the default bootstrapping. // On long-running networks some of these contracts might have been deployed after bootstrapping, @@ -171,6 +173,7 @@ type SystemContracts struct { // Utility contracts Burner SystemContract + Crypto SystemContract } // AsTemplateEnv returns a template environment with all system contracts filled in. @@ -198,6 +201,7 @@ func (c SystemContracts) AsTemplateEnv() templates.Environment { ViewResolverAddress: c.ViewResolver.Address.Hex(), BurnerAddress: c.Burner.Address.Hex(), + CryptoAddress: c.Crypto.Address.Hex(), } } @@ -228,6 +232,7 @@ func (c SystemContracts) All() []SystemContract { // EVMStorage is not included here, since it is not a contract c.Burner, + c.Crypto, } } @@ -331,6 +336,15 @@ func init() { } } + burnerAddressFunc := func(chain flow.ChainID) flow.Address { + switch chain { + case flow.Mainnet, flow.Testnet: + return nthAddressFunc(FungibleTokenAccountIndex)(chain) + default: + return serviceAddressFunc(chain) + } + } + contractAddressFunc = map[string]func(id flow.ChainID) flow.Address{ ContractNameIDTableStaking: epochAddressFunc, ContractNameEpoch: epochAddressFunc, @@ -355,7 +369,8 @@ func init() { ContractNameEVM: serviceAddressFunc, AccountNameEVMStorage: evmStorageEVMFunc, - ContractNameBurner: serviceAddressFunc, + ContractNameBurner: burnerAddressFunc, + ContractNameCrypto: serviceAddressFunc, } getSystemContractsForChain := func(chainID flow.ChainID) *SystemContracts { @@ -411,6 +426,7 @@ func init() { EVMStorage: addressOfAccount(AccountNameEVMStorage), Burner: addressOfContract(ContractNameBurner), + Crypto: addressOfContract(ContractNameCrypto), } return contracts diff --git a/fvm/transactionInvoker.go b/fvm/transactionInvoker.go index 20a68727c81..6edf2ec5405 100644 --- a/fvm/transactionInvoker.go +++ b/fvm/transactionInvoker.go @@ -4,8 +4,8 @@ import ( "fmt" "strconv" + "github.com/onflow/cadence/common" "github.com/onflow/cadence/runtime" - "github.com/onflow/cadence/runtime/common" "github.com/rs/zerolog" "go.opentelemetry.io/otel/attribute" otelTrace "go.opentelemetry.io/otel/trace" @@ -34,6 +34,8 @@ type TransactionExecutorParams struct { // Note: This is disabled only by tests TransactionBodyExecutionEnabled bool + + ReadVersionFromNodeVersionBeacon bool } func DefaultTransactionExecutorParams() TransactionExecutorParams { @@ -42,6 +44,7 @@ func DefaultTransactionExecutorParams() TransactionExecutorParams { SequenceNumberCheckAndIncrementEnabled: true, AccountKeyWeightThreshold: AccountKeyWeightThreshold, TransactionBodyExecutionEnabled: true, + ReadVersionFromNodeVersionBeacon: true, } } @@ -65,6 +68,11 @@ type transactionExecutor struct { startedTransactionBodyExecution bool nestedTxnId state.NestedTransactionId + // the state reads needed to compute the metering parameters + // this is used to invalidate the metering parameters if a transaction + // writes to any of those registers + executionStateRead *snapshot.ExecutionSnapshot + cadenceRuntime *reusableRuntime.ReusableCadenceRuntime txnBodyExecutor runtime.Executor @@ -194,17 +202,27 @@ func (executor *transactionExecutor) preprocessTransactionBody() error { return err } } - - meterParams, err := getBodyMeterParameters( + // get meter parameters + executionParameters, executionStateRead, err := getExecutionParameters( + executor.env.Logger(), executor.ctx, executor.proc, executor.txnState) if err != nil { - return fmt.Errorf("error gettng meter parameters: %w", err) + return fmt.Errorf("error getting execution parameters: %w", err) } + if len(executionStateRead.WriteSet) != 0 { + // this should never happen + // and indicates an implementation error + panic("getting execution parameters should not write to registers") + } + + // we need to save the execution state read for invalidation purposes + executor.executionStateRead = executionStateRead + txnId, err := executor.txnState.BeginNestedTransactionWithMeterParams( - meterParams) + executionParameters) if err != nil { return err } @@ -387,8 +405,9 @@ func (executor *transactionExecutor) normalExecution() ( invalidator = environment.NewDerivedDataInvalidator( contractUpdates, - executor.ctx.Chain.ServiceAddress(), - bodySnapshot) + bodySnapshot, + executor.executionStateRead, + ) // Check if all account storage limits are ok // diff --git a/fvm/transactionInvoker_test.go b/fvm/transactionInvoker_test.go index 44198592454..89d218b26b0 100644 --- a/fvm/transactionInvoker_test.go +++ b/fvm/transactionInvoker_test.go @@ -5,10 +5,10 @@ import ( "testing" "github.com/onflow/cadence" + "github.com/onflow/cadence/common" + "github.com/onflow/cadence/interpreter" "github.com/onflow/cadence/runtime" - "github.com/onflow/cadence/runtime/common" - "github.com/onflow/cadence/runtime/interpreter" - "github.com/onflow/cadence/runtime/sema" + "github.com/onflow/cadence/sema" "github.com/rs/zerolog" "github.com/stretchr/testify/require" diff --git a/fvm/transactionStorageLimiter.go b/fvm/transactionStorageLimiter.go index 261942bf8b0..37ebf12ddee 100644 --- a/fvm/transactionStorageLimiter.go +++ b/fvm/transactionStorageLimiter.go @@ -5,7 +5,7 @@ import ( "fmt" "github.com/onflow/cadence" - "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/common" "golang.org/x/exp/slices" "github.com/onflow/flow-go/fvm/environment" diff --git a/go.mod b/go.mod index e557998ca60..eacd5e0ded0 100644 --- a/go.mod +++ b/go.mod @@ -47,14 +47,14 @@ require ( github.com/multiformats/go-multiaddr v0.12.2 github.com/multiformats/go-multiaddr-dns v0.3.1 github.com/multiformats/go-multihash v0.2.3 - github.com/onflow/atree v0.8.0-rc.6 - github.com/onflow/cadence v1.0.0-preview.52 + github.com/onflow/atree v0.8.0 + github.com/onflow/cadence v1.2.2 github.com/onflow/crypto v0.25.2 github.com/onflow/flow v0.3.4 - github.com/onflow/flow-core-contracts/lib/go/contracts v1.3.1 - github.com/onflow/flow-core-contracts/lib/go/templates v1.3.1 - github.com/onflow/flow-go-sdk v1.0.0-preview.55 - github.com/onflow/flow/protobuf/go/flow v0.4.6 + github.com/onflow/flow-core-contracts/lib/go/contracts v1.4.0 + github.com/onflow/flow-core-contracts/lib/go/templates v1.4.0 + github.com/onflow/flow-go-sdk v1.2.3 + github.com/onflow/flow/protobuf/go/flow v0.4.7 github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 github.com/pierrec/lz4 v2.6.1+incompatible github.com/pkg/errors v0.9.1 @@ -77,11 +77,11 @@ require ( go.opentelemetry.io/otel/trace v1.24.0 go.uber.org/atomic v1.11.0 go.uber.org/multierr v1.11.0 - golang.org/x/crypto v0.26.0 + golang.org/x/crypto v0.28.0 golang.org/x/exp v0.0.0-20240119083558-1b970713d09a golang.org/x/sync v0.8.0 - golang.org/x/sys v0.23.0 - golang.org/x/text v0.17.0 + golang.org/x/sys v0.26.0 + golang.org/x/text v0.19.0 golang.org/x/time v0.5.0 golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d google.golang.org/api v0.162.0 @@ -98,7 +98,6 @@ require ( github.com/coreos/go-semver v0.3.0 github.com/docker/go-units v0.5.0 github.com/dustin/go-humanize v1.0.1 - github.com/glebarez/go-sqlite v1.22.0 github.com/go-playground/validator/v10 v10.14.1 github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb github.com/gorilla/websocket v1.5.0 @@ -108,7 +107,6 @@ require ( github.com/ipfs/boxo v0.17.1-0.20240131173518-89bceff34bf1 github.com/mitchellh/mapstructure v1.5.0 github.com/onflow/go-ethereum v1.14.7 - github.com/onflow/nft-storefront/lib/go/contracts v1.0.0 github.com/onflow/wal v1.0.2 github.com/slok/go-http-metrics v0.10.0 github.com/sony/gobreaker v0.5.0 @@ -256,10 +254,10 @@ require ( github.com/multiformats/go-multistream v0.5.0 // indirect github.com/multiformats/go-varint v0.0.7 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect - github.com/onflow/flow-ft/lib/go/contracts v1.0.0 // indirect - github.com/onflow/flow-ft/lib/go/templates v1.0.0 // indirect - github.com/onflow/flow-nft/lib/go/contracts v1.2.1 // indirect - github.com/onflow/flow-nft/lib/go/templates v1.2.0 // indirect + github.com/onflow/flow-ft/lib/go/contracts v1.0.1 // indirect + github.com/onflow/flow-ft/lib/go/templates v1.0.1 // indirect + github.com/onflow/flow-nft/lib/go/contracts v1.2.2 // indirect + github.com/onflow/flow-nft/lib/go/templates v1.2.1 // indirect github.com/onflow/sdks v0.6.0-preview.1 // indirect github.com/onsi/ginkgo v1.16.5 // indirect github.com/onsi/ginkgo/v2 v2.13.2 // indirect @@ -278,7 +276,6 @@ require ( github.com/quic-go/quic-go v0.40.1 // indirect github.com/quic-go/webtransport-go v0.6.0 // indirect github.com/raulk/go-watchdog v1.3.0 // indirect - github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/rivo/uniseg v0.4.4 // indirect github.com/rogpeppe/go-internal v1.10.0 // indirect github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect @@ -314,17 +311,13 @@ require ( golang.org/x/mod v0.17.0 // indirect golang.org/x/net v0.25.0 // indirect golang.org/x/oauth2 v0.17.0 // indirect - golang.org/x/term v0.23.0 // indirect + golang.org/x/term v0.25.0 // indirect gonum.org/v1/gonum v0.14.0 // indirect google.golang.org/appengine v1.6.8 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect lukechampine.com/blake3 v1.3.0 // indirect - modernc.org/libc v1.37.6 // indirect - modernc.org/mathutil v1.6.0 // indirect - modernc.org/memory v1.7.2 // indirect - modernc.org/sqlite v1.28.0 // indirect nhooyr.io/websocket v1.8.7 // indirect rsc.io/tmplfunc v0.0.3 // indirect ) diff --git a/go.sum b/go.sum index e3cd8146665..15ec48eea52 100644 --- a/go.sum +++ b/go.sum @@ -1,17 +1,14 @@ -cloud.google.com/go v0.0.0-20170206221025-ce650573d812/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.31.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.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.43.0/go.mod h1:BOSR3VbTLkk6FDC/TcffxP4NF/FFBGA5ku+jvKOP7pg= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.51.0/go.mod h1:hWtGJ6gnXH+KgDv+V0zFGDvpi07n3z8ZNj3T1RW0Gcw= 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= @@ -33,789 +30,35 @@ cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+Y cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= -cloud.google.com/go v0.100.1/go.mod h1:fs4QogzfH5n2pBXBP9vRiU+eCny7lD2vmFZy79Iuw1U= cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= -cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= -cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU= -cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA= -cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM= -cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I= -cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY= -cloud.google.com/go v0.110.2/go.mod h1:k04UEeEtb6ZBRTv3dZz4CeJC3jKGxyhl0sAiVVquxiw= -cloud.google.com/go v0.110.4/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI= -cloud.google.com/go v0.110.6/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI= -cloud.google.com/go v0.110.7/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI= -cloud.google.com/go v0.110.8/go.mod h1:Iz8AkXJf1qmxC3Oxoep8R1T36w8B92yU29PcBhHO5fk= cloud.google.com/go v0.112.0 h1:tpFCD7hpHFlQ8yPwT3x+QeXqc2T6+n6T+hmABHfDUSM= cloud.google.com/go v0.112.0/go.mod h1:3jEEVwZ/MHU4djK5t5RHuKOA/GbLddgTdVubX1qnPD4= -cloud.google.com/go/accessapproval v1.4.0/go.mod h1:zybIuC3KpDOvotz59lFe5qxRZx6C75OtwbisN56xYB4= -cloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw= -cloud.google.com/go/accessapproval v1.6.0/go.mod h1:R0EiYnwV5fsRFiKZkPHr6mwyk2wxUJ30nL4j2pcFY2E= -cloud.google.com/go/accessapproval v1.7.1/go.mod h1:JYczztsHRMK7NTXb6Xw+dwbs/WnOJxbo/2mTI+Kgg68= -cloud.google.com/go/accessapproval v1.7.2/go.mod h1:/gShiq9/kK/h8T/eEn1BTzalDvk0mZxJlhfw0p+Xuc0= -cloud.google.com/go/accesscontextmanager v1.3.0/go.mod h1:TgCBehyr5gNMz7ZaH9xubp+CE8dkrszb4oK9CWyvD4o= -cloud.google.com/go/accesscontextmanager v1.4.0/go.mod h1:/Kjh7BBu/Gh83sv+K60vN9QE5NJcd80sU33vIe2IFPE= -cloud.google.com/go/accesscontextmanager v1.6.0/go.mod h1:8XCvZWfYw3K/ji0iVnp+6pu7huxoQTLmxAbVjbloTtM= -cloud.google.com/go/accesscontextmanager v1.7.0/go.mod h1:CEGLewx8dwa33aDAZQujl7Dx+uYhS0eay198wB/VumQ= -cloud.google.com/go/accesscontextmanager v1.8.0/go.mod h1:uI+AI/r1oyWK99NN8cQ3UK76AMelMzgZCvJfsi2c+ps= -cloud.google.com/go/accesscontextmanager v1.8.1/go.mod h1:JFJHfvuaTC+++1iL1coPiG1eu5D24db2wXCDWDjIrxo= -cloud.google.com/go/accesscontextmanager v1.8.2/go.mod h1:E6/SCRM30elQJ2PKtFMs2YhfJpZSNcJyejhuzoId4Zk= -cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw= -cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY= -cloud.google.com/go/aiplatform v1.27.0/go.mod h1:Bvxqtl40l0WImSb04d0hXFU7gDOiq9jQmorivIiWcKg= -cloud.google.com/go/aiplatform v1.35.0/go.mod h1:7MFT/vCaOyZT/4IIFfxH4ErVg/4ku6lKv3w0+tFTgXQ= -cloud.google.com/go/aiplatform v1.36.1/go.mod h1:WTm12vJRPARNvJ+v6P52RDHCNe4AhvjcIZ/9/RRHy/k= -cloud.google.com/go/aiplatform v1.37.0/go.mod h1:IU2Cv29Lv9oCn/9LkFiiuKfwrRTq+QQMbW+hPCxJGZw= -cloud.google.com/go/aiplatform v1.45.0/go.mod h1:Iu2Q7sC7QGhXUeOhAj/oCK9a+ULz1O4AotZiqjQ8MYA= -cloud.google.com/go/aiplatform v1.48.0/go.mod h1:Iu2Q7sC7QGhXUeOhAj/oCK9a+ULz1O4AotZiqjQ8MYA= -cloud.google.com/go/aiplatform v1.50.0/go.mod h1:IRc2b8XAMTa9ZmfJV1BCCQbieWWvDnP1A8znyz5N7y4= -cloud.google.com/go/aiplatform v1.51.0/go.mod h1:IRc2b8XAMTa9ZmfJV1BCCQbieWWvDnP1A8znyz5N7y4= -cloud.google.com/go/aiplatform v1.51.1/go.mod h1:kY3nIMAVQOK2XDqDPHaOuD9e+FdMA6OOpfBjsvaFSOo= -cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI= -cloud.google.com/go/analytics v0.12.0/go.mod h1:gkfj9h6XRf9+TS4bmuhPEShsh3hH8PAZzm/41OOhQd4= -cloud.google.com/go/analytics v0.17.0/go.mod h1:WXFa3WSym4IZ+JiKmavYdJwGG/CvpqiqczmL59bTD9M= -cloud.google.com/go/analytics v0.18.0/go.mod h1:ZkeHGQlcIPkw0R/GW+boWHhCOR43xz9RN/jn7WcqfIE= -cloud.google.com/go/analytics v0.19.0/go.mod h1:k8liqf5/HCnOUkbawNtrWWc+UAzyDlW89doe8TtoDsE= -cloud.google.com/go/analytics v0.21.2/go.mod h1:U8dcUtmDmjrmUTnnnRnI4m6zKn/yaA5N9RlEkYFHpQo= -cloud.google.com/go/analytics v0.21.3/go.mod h1:U8dcUtmDmjrmUTnnnRnI4m6zKn/yaA5N9RlEkYFHpQo= -cloud.google.com/go/analytics v0.21.4/go.mod h1:zZgNCxLCy8b2rKKVfC1YkC2vTrpfZmeRCySM3aUbskA= -cloud.google.com/go/apigateway v1.3.0/go.mod h1:89Z8Bhpmxu6AmUxuVRg/ECRGReEdiP3vQtk4Z1J9rJk= -cloud.google.com/go/apigateway v1.4.0/go.mod h1:pHVY9MKGaH9PQ3pJ4YLzoj6U5FUDeDFBllIz7WmzJoc= -cloud.google.com/go/apigateway v1.5.0/go.mod h1:GpnZR3Q4rR7LVu5951qfXPJCHquZt02jf7xQx7kpqN8= -cloud.google.com/go/apigateway v1.6.1/go.mod h1:ufAS3wpbRjqfZrzpvLC2oh0MFlpRJm2E/ts25yyqmXA= -cloud.google.com/go/apigateway v1.6.2/go.mod h1:CwMC90nnZElorCW63P2pAYm25AtQrHfuOkbRSHj0bT8= -cloud.google.com/go/apigeeconnect v1.3.0/go.mod h1:G/AwXFAKo0gIXkPTVfZDd2qA1TxBXJ3MgMRBQkIi9jc= -cloud.google.com/go/apigeeconnect v1.4.0/go.mod h1:kV4NwOKqjvt2JYR0AoIWo2QGfoRtn/pkS3QlHp0Ni04= -cloud.google.com/go/apigeeconnect v1.5.0/go.mod h1:KFaCqvBRU6idyhSNyn3vlHXc8VMDJdRmwDF6JyFRqZ8= -cloud.google.com/go/apigeeconnect v1.6.1/go.mod h1:C4awq7x0JpLtrlQCr8AzVIzAaYgngRqWf9S5Uhg+wWs= -cloud.google.com/go/apigeeconnect v1.6.2/go.mod h1:s6O0CgXT9RgAxlq3DLXvG8riw8PYYbU/v25jqP3Dy18= -cloud.google.com/go/apigeeregistry v0.4.0/go.mod h1:EUG4PGcsZvxOXAdyEghIdXwAEi/4MEaoqLMLDMIwKXY= -cloud.google.com/go/apigeeregistry v0.5.0/go.mod h1:YR5+s0BVNZfVOUkMa5pAR2xGd0A473vA5M7j247o1wM= -cloud.google.com/go/apigeeregistry v0.6.0/go.mod h1:BFNzW7yQVLZ3yj0TKcwzb8n25CFBri51GVGOEUcgQsc= -cloud.google.com/go/apigeeregistry v0.7.1/go.mod h1:1XgyjZye4Mqtw7T9TsY4NW10U7BojBvG4RMD+vRDrIw= -cloud.google.com/go/apigeeregistry v0.7.2/go.mod h1:9CA2B2+TGsPKtfi3F7/1ncCCsL62NXBRfM6iPoGSM+8= -cloud.google.com/go/apikeys v0.4.0/go.mod h1:XATS/yqZbaBK0HOssf+ALHp8jAlNHUgyfprvNcBIszU= -cloud.google.com/go/apikeys v0.5.0/go.mod h1:5aQfwY4D+ewMMWScd3hm2en3hCj+BROlyrt3ytS7KLI= -cloud.google.com/go/apikeys v0.6.0/go.mod h1:kbpXu5upyiAlGkKrJgQl8A0rKNNJ7dQ377pdroRSSi8= -cloud.google.com/go/appengine v1.4.0/go.mod h1:CS2NhuBuDXM9f+qscZ6V86m1MIIqPj3WC/UoEuR1Sno= -cloud.google.com/go/appengine v1.5.0/go.mod h1:TfasSozdkFI0zeoxW3PTBLiNqRmzraodCWatWI9Dmak= -cloud.google.com/go/appengine v1.6.0/go.mod h1:hg6i0J/BD2cKmDJbaFSYHFyZkgBEfQrDg/X0V5fJn84= -cloud.google.com/go/appengine v1.7.0/go.mod h1:eZqpbHFCqRGa2aCdope7eC0SWLV1j0neb/QnMJVWx6A= -cloud.google.com/go/appengine v1.7.1/go.mod h1:IHLToyb/3fKutRysUlFO0BPt5j7RiQ45nrzEJmKTo6E= -cloud.google.com/go/appengine v1.8.1/go.mod h1:6NJXGLVhZCN9aQ/AEDvmfzKEfoYBlfB80/BHiKVputY= -cloud.google.com/go/appengine v1.8.2/go.mod h1:WMeJV9oZ51pvclqFN2PqHoGnys7rK0rz6s3Mp6yMvDo= -cloud.google.com/go/area120 v0.5.0/go.mod h1:DE/n4mp+iqVyvxHN41Vf1CR602GiHQjFPusMFW6bGR4= -cloud.google.com/go/area120 v0.6.0/go.mod h1:39yFJqWVgm0UZqWTOdqkLhjoC7uFfgXRC8g/ZegeAh0= -cloud.google.com/go/area120 v0.7.0/go.mod h1:a3+8EUD1SX5RUcCs3MY5YasiO1z6yLiNLRiFrykbynY= -cloud.google.com/go/area120 v0.7.1/go.mod h1:j84i4E1RboTWjKtZVWXPqvK5VHQFJRF2c1Nm69pWm9k= -cloud.google.com/go/area120 v0.8.1/go.mod h1:BVfZpGpB7KFVNxPiQBuHkX6Ed0rS51xIgmGyjrAfzsg= -cloud.google.com/go/area120 v0.8.2/go.mod h1:a5qfo+x77SRLXnCynFWPUZhnZGeSgvQ+Y0v1kSItkh4= -cloud.google.com/go/artifactregistry v1.6.0/go.mod h1:IYt0oBPSAGYj/kprzsBjZ/4LnG/zOcHyFHjWPCi6SAQ= -cloud.google.com/go/artifactregistry v1.7.0/go.mod h1:mqTOFOnGZx8EtSqK/ZWcsm/4U8B77rbcLP6ruDU2Ixk= -cloud.google.com/go/artifactregistry v1.8.0/go.mod h1:w3GQXkJX8hiKN0v+at4b0qotwijQbYUqF2GWkZzAhC0= -cloud.google.com/go/artifactregistry v1.9.0/go.mod h1:2K2RqvA2CYvAeARHRkLDhMDJ3OXy26h3XW+3/Jh2uYc= -cloud.google.com/go/artifactregistry v1.11.1/go.mod h1:lLYghw+Itq9SONbCa1YWBoWs1nOucMH0pwXN1rOBZFI= -cloud.google.com/go/artifactregistry v1.11.2/go.mod h1:nLZns771ZGAwVLzTX/7Al6R9ehma4WUEhZGWV6CeQNQ= -cloud.google.com/go/artifactregistry v1.12.0/go.mod h1:o6P3MIvtzTOnmvGagO9v/rOjjA0HmhJ+/6KAXrmYDCI= -cloud.google.com/go/artifactregistry v1.13.0/go.mod h1:uy/LNfoOIivepGhooAUpL1i30Hgee3Cu0l4VTWHUC08= -cloud.google.com/go/artifactregistry v1.14.1/go.mod h1:nxVdG19jTaSTu7yA7+VbWL346r3rIdkZ142BSQqhn5E= -cloud.google.com/go/artifactregistry v1.14.2/go.mod h1:Xk+QbsKEb0ElmyeMfdHAey41B+qBq3q5R5f5xD4XT3U= -cloud.google.com/go/artifactregistry v1.14.3/go.mod h1:A2/E9GXnsyXl7GUvQ/2CjHA+mVRoWAXC0brg2os+kNI= -cloud.google.com/go/asset v1.5.0/go.mod h1:5mfs8UvcM5wHhqtSv8J1CtxxaQq3AdBxxQi2jGW/K4o= -cloud.google.com/go/asset v1.7.0/go.mod h1:YbENsRK4+xTiL+Ofoj5Ckf+O17kJtgp3Y3nn4uzZz5s= -cloud.google.com/go/asset v1.8.0/go.mod h1:mUNGKhiqIdbr8X7KNayoYvyc4HbbFO9URsjbytpUaW0= -cloud.google.com/go/asset v1.9.0/go.mod h1:83MOE6jEJBMqFKadM9NLRcs80Gdw76qGuHn8m3h8oHQ= -cloud.google.com/go/asset v1.10.0/go.mod h1:pLz7uokL80qKhzKr4xXGvBQXnzHn5evJAEAtZiIb0wY= -cloud.google.com/go/asset v1.11.1/go.mod h1:fSwLhbRvC9p9CXQHJ3BgFeQNM4c9x10lqlrdEUYXlJo= -cloud.google.com/go/asset v1.12.0/go.mod h1:h9/sFOa4eDIyKmH6QMpm4eUK3pDojWnUhTgJlk762Hg= -cloud.google.com/go/asset v1.13.0/go.mod h1:WQAMyYek/b7NBpYq/K4KJWcRqzoalEsxz/t/dTk4THw= -cloud.google.com/go/asset v1.14.1/go.mod h1:4bEJ3dnHCqWCDbWJ/6Vn7GVI9LerSi7Rfdi03hd+WTQ= -cloud.google.com/go/asset v1.15.0/go.mod h1:tpKafV6mEut3+vN9ScGvCHXHj7FALFVta+okxFECHcg= -cloud.google.com/go/asset v1.15.1/go.mod h1:yX/amTvFWRpp5rcFq6XbCxzKT8RJUam1UoboE179jU4= -cloud.google.com/go/assuredworkloads v1.5.0/go.mod h1:n8HOZ6pff6re5KYfBXcFvSViQjDwxFkAkmUFffJRbbY= -cloud.google.com/go/assuredworkloads v1.6.0/go.mod h1:yo2YOk37Yc89Rsd5QMVECvjaMKymF9OP+QXWlKXUkXw= -cloud.google.com/go/assuredworkloads v1.7.0/go.mod h1:z/736/oNmtGAyU47reJgGN+KVoYoxeLBoj4XkKYscNI= -cloud.google.com/go/assuredworkloads v1.8.0/go.mod h1:AsX2cqyNCOvEQC8RMPnoc0yEarXQk6WEKkxYfL6kGIo= -cloud.google.com/go/assuredworkloads v1.9.0/go.mod h1:kFuI1P78bplYtT77Tb1hi0FMxM0vVpRC7VVoJC3ZoT0= -cloud.google.com/go/assuredworkloads v1.10.0/go.mod h1:kwdUQuXcedVdsIaKgKTp9t0UJkE5+PAVNhdQm4ZVq2E= -cloud.google.com/go/assuredworkloads v1.11.1/go.mod h1:+F04I52Pgn5nmPG36CWFtxmav6+7Q+c5QyJoL18Lry0= -cloud.google.com/go/assuredworkloads v1.11.2/go.mod h1:O1dfr+oZJMlE6mw0Bp0P1KZSlj5SghMBvTpZqIcUAW4= -cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0= -cloud.google.com/go/automl v1.6.0/go.mod h1:ugf8a6Fx+zP0D59WLhqgTDsQI9w07o64uf/Is3Nh5p8= -cloud.google.com/go/automl v1.7.0/go.mod h1:RL9MYCCsJEOmt0Wf3z9uzG0a7adTT1fe+aObgSpkCt8= -cloud.google.com/go/automl v1.8.0/go.mod h1:xWx7G/aPEe/NP+qzYXktoBSDfjO+vnKMGgsApGJJquM= -cloud.google.com/go/automl v1.12.0/go.mod h1:tWDcHDp86aMIuHmyvjuKeeHEGq76lD7ZqfGLN6B0NuU= -cloud.google.com/go/automl v1.13.1/go.mod h1:1aowgAHWYZU27MybSCFiukPO7xnyawv7pt3zK4bheQE= -cloud.google.com/go/automl v1.13.2/go.mod h1:gNY/fUmDEN40sP8amAX3MaXkxcqPIn7F1UIIPZpy4Mg= -cloud.google.com/go/baremetalsolution v0.3.0/go.mod h1:XOrocE+pvK1xFfleEnShBlNAXf+j5blPPxrhjKgnIFc= -cloud.google.com/go/baremetalsolution v0.4.0/go.mod h1:BymplhAadOO/eBa7KewQ0Ppg4A4Wplbn+PsFKRLo0uI= -cloud.google.com/go/baremetalsolution v0.5.0/go.mod h1:dXGxEkmR9BMwxhzBhV0AioD0ULBmuLZI8CdwalUxuss= -cloud.google.com/go/baremetalsolution v1.1.1/go.mod h1:D1AV6xwOksJMV4OSlWHtWuFNZZYujJknMAP4Qa27QIA= -cloud.google.com/go/baremetalsolution v1.2.0/go.mod h1:68wi9AwPYkEWIUT4SvSGS9UJwKzNpshjHsH4lzk8iOw= -cloud.google.com/go/baremetalsolution v1.2.1/go.mod h1:3qKpKIw12RPXStwQXcbhfxVj1dqQGEvcmA+SX/mUR88= -cloud.google.com/go/batch v0.3.0/go.mod h1:TR18ZoAekj1GuirsUsR1ZTKN3FC/4UDnScjT8NXImFE= -cloud.google.com/go/batch v0.4.0/go.mod h1:WZkHnP43R/QCGQsZ+0JyG4i79ranE2u8xvjq/9+STPE= -cloud.google.com/go/batch v0.7.0/go.mod h1:vLZN95s6teRUqRQ4s3RLDsH8PvboqBK+rn1oevL159g= -cloud.google.com/go/batch v1.3.1/go.mod h1:VguXeQKXIYaeeIYbuozUmBR13AfL4SJP7IltNPS+A4A= -cloud.google.com/go/batch v1.4.1/go.mod h1:KdBmDD61K0ovcxoRHGrN6GmOBWeAOyCgKD0Mugx4Fkk= -cloud.google.com/go/batch v1.5.0/go.mod h1:KdBmDD61K0ovcxoRHGrN6GmOBWeAOyCgKD0Mugx4Fkk= -cloud.google.com/go/batch v1.5.1/go.mod h1:RpBuIYLkQu8+CWDk3dFD/t/jOCGuUpkpX+Y0n1Xccs8= -cloud.google.com/go/beyondcorp v0.2.0/go.mod h1:TB7Bd+EEtcw9PCPQhCJtJGjk/7TC6ckmnSFS+xwTfm4= -cloud.google.com/go/beyondcorp v0.3.0/go.mod h1:E5U5lcrcXMsCuoDNyGrpyTm/hn7ne941Jz2vmksAxW8= -cloud.google.com/go/beyondcorp v0.4.0/go.mod h1:3ApA0mbhHx6YImmuubf5pyW8srKnCEPON32/5hj+RmM= -cloud.google.com/go/beyondcorp v0.5.0/go.mod h1:uFqj9X+dSfrheVp7ssLTaRHd2EHqSL4QZmH4e8WXGGU= -cloud.google.com/go/beyondcorp v0.6.1/go.mod h1:YhxDWw946SCbmcWo3fAhw3V4XZMSpQ/VYfcKGAEU8/4= -cloud.google.com/go/beyondcorp v1.0.0/go.mod h1:YhxDWw946SCbmcWo3fAhw3V4XZMSpQ/VYfcKGAEU8/4= -cloud.google.com/go/beyondcorp v1.0.1/go.mod h1:zl/rWWAFVeV+kx+X2Javly7o1EIQThU4WlkynffL/lk= 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/bigquery v1.42.0/go.mod h1:8dRTJxhtG+vwBKzE5OseQn/hiydoQN3EedCaOdYmxRA= -cloud.google.com/go/bigquery v1.43.0/go.mod h1:ZMQcXHsl+xmU1z36G2jNGZmKp9zNY5BUua5wDgmNCfw= -cloud.google.com/go/bigquery v1.44.0/go.mod h1:0Y33VqXTEsbamHJvJHdFmtqHvMIY28aK1+dFsvaChGc= -cloud.google.com/go/bigquery v1.47.0/go.mod h1:sA9XOgy0A8vQK9+MWhEQTY6Tix87M/ZurWFIxmF9I/E= -cloud.google.com/go/bigquery v1.48.0/go.mod h1:QAwSz+ipNgfL5jxiaK7weyOhzdoAy1zFm0Nf1fysJac= -cloud.google.com/go/bigquery v1.49.0/go.mod h1:Sv8hMmTFFYBlt/ftw2uN6dFdQPzBlREY9yBh7Oy7/4Q= -cloud.google.com/go/bigquery v1.50.0/go.mod h1:YrleYEh2pSEbgTBZYMJ5SuSr0ML3ypjRB1zgf7pvQLU= -cloud.google.com/go/bigquery v1.52.0/go.mod h1:3b/iXjRQGU4nKa87cXeg6/gogLjO8C6PmuM8i5Bi/u4= -cloud.google.com/go/bigquery v1.53.0/go.mod h1:3b/iXjRQGU4nKa87cXeg6/gogLjO8C6PmuM8i5Bi/u4= -cloud.google.com/go/bigquery v1.55.0/go.mod h1:9Y5I3PN9kQWuid6183JFhOGOW3GcirA5LpsKCUn+2ec= -cloud.google.com/go/bigquery v1.56.0/go.mod h1:KDcsploXTEY7XT3fDQzMUZlpQLHzE4itubHrnmhUrZA= -cloud.google.com/go/bigtable v1.2.0/go.mod h1:JcVAOl45lrTmQfLj7T6TxyMzIN/3FGGcFm+2xVAli2o= -cloud.google.com/go/billing v1.4.0/go.mod h1:g9IdKBEFlItS8bTtlrZdVLWSSdSyFUZKXNS02zKMOZY= -cloud.google.com/go/billing v1.5.0/go.mod h1:mztb1tBc3QekhjSgmpf/CV4LzWXLzCArwpLmP2Gm88s= -cloud.google.com/go/billing v1.6.0/go.mod h1:WoXzguj+BeHXPbKfNWkqVtDdzORazmCjraY+vrxcyvI= -cloud.google.com/go/billing v1.7.0/go.mod h1:q457N3Hbj9lYwwRbnlD7vUpyjq6u5U1RAOArInEiD5Y= -cloud.google.com/go/billing v1.12.0/go.mod h1:yKrZio/eu+okO/2McZEbch17O5CB5NpZhhXG6Z766ss= -cloud.google.com/go/billing v1.13.0/go.mod h1:7kB2W9Xf98hP9Sr12KfECgfGclsH3CQR0R08tnRlRbc= -cloud.google.com/go/billing v1.16.0/go.mod h1:y8vx09JSSJG02k5QxbycNRrN7FGZB6F3CAcgum7jvGA= -cloud.google.com/go/billing v1.17.0/go.mod h1:Z9+vZXEq+HwH7bhJkyI4OQcR6TSbeMrjlpEjO2vzY64= -cloud.google.com/go/billing v1.17.1/go.mod h1:Z9+vZXEq+HwH7bhJkyI4OQcR6TSbeMrjlpEjO2vzY64= -cloud.google.com/go/billing v1.17.2/go.mod h1:u/AdV/3wr3xoRBk5xvUzYMS1IawOAPwQMuHgHMdljDg= -cloud.google.com/go/binaryauthorization v1.1.0/go.mod h1:xwnoWu3Y84jbuHa0zd526MJYmtnVXn0syOjaJgy4+dM= -cloud.google.com/go/binaryauthorization v1.2.0/go.mod h1:86WKkJHtRcv5ViNABtYMhhNWRrD1Vpi//uKEy7aYEfI= -cloud.google.com/go/binaryauthorization v1.3.0/go.mod h1:lRZbKgjDIIQvzYQS1p99A7/U1JqvqeZg0wiI5tp6tg0= -cloud.google.com/go/binaryauthorization v1.4.0/go.mod h1:tsSPQrBd77VLplV70GUhBf/Zm3FsKmgSqgm4UmiDItk= -cloud.google.com/go/binaryauthorization v1.5.0/go.mod h1:OSe4OU1nN/VswXKRBmciKpo9LulY41gch5c68htf3/Q= -cloud.google.com/go/binaryauthorization v1.6.1/go.mod h1:TKt4pa8xhowwffiBmbrbcxijJRZED4zrqnwZ1lKH51U= -cloud.google.com/go/binaryauthorization v1.7.0/go.mod h1:Zn+S6QqTMn6odcMU1zDZCJxPjU2tZPV1oDl45lWY154= -cloud.google.com/go/binaryauthorization v1.7.1/go.mod h1:GTAyfRWYgcbsP3NJogpV3yeunbUIjx2T9xVeYovtURE= -cloud.google.com/go/certificatemanager v1.3.0/go.mod h1:n6twGDvcUBFu9uBgt4eYvvf3sQ6My8jADcOVwHmzadg= -cloud.google.com/go/certificatemanager v1.4.0/go.mod h1:vowpercVFyqs8ABSmrdV+GiFf2H/ch3KyudYQEMM590= -cloud.google.com/go/certificatemanager v1.6.0/go.mod h1:3Hh64rCKjRAX8dXgRAyOcY5vQ/fE1sh8o+Mdd6KPgY8= -cloud.google.com/go/certificatemanager v1.7.1/go.mod h1:iW8J3nG6SaRYImIa+wXQ0g8IgoofDFRp5UMzaNk1UqI= -cloud.google.com/go/certificatemanager v1.7.2/go.mod h1:15SYTDQMd00kdoW0+XY5d9e+JbOPjp24AvF48D8BbcQ= -cloud.google.com/go/channel v1.8.0/go.mod h1:W5SwCXDJsq/rg3tn3oG0LOxpAo6IMxNa09ngphpSlnk= -cloud.google.com/go/channel v1.9.0/go.mod h1:jcu05W0my9Vx4mt3/rEHpfxc9eKi9XwsdDL8yBMbKUk= -cloud.google.com/go/channel v1.11.0/go.mod h1:IdtI0uWGqhEeatSB62VOoJ8FSUhJ9/+iGkJVqp74CGE= -cloud.google.com/go/channel v1.12.0/go.mod h1:VkxCGKASi4Cq7TbXxlaBezonAYpp1GCnKMY6tnMQnLU= -cloud.google.com/go/channel v1.16.0/go.mod h1:eN/q1PFSl5gyu0dYdmxNXscY/4Fi7ABmeHCJNf/oHmc= -cloud.google.com/go/channel v1.17.0/go.mod h1:RpbhJsGi/lXWAUM1eF4IbQGbsfVlg2o8Iiy2/YLfVT0= -cloud.google.com/go/channel v1.17.1/go.mod h1:xqfzcOZAcP4b/hUDH0GkGg1Sd5to6di1HOJn/pi5uBQ= -cloud.google.com/go/cloudbuild v1.3.0/go.mod h1:WequR4ULxlqvMsjDEEEFnOG5ZSRSgWOywXYDb1vPE6U= -cloud.google.com/go/cloudbuild v1.4.0/go.mod h1:5Qwa40LHiOXmz3386FrjrYM93rM/hdRr7b53sySrTqA= -cloud.google.com/go/cloudbuild v1.6.0/go.mod h1:UIbc/w9QCbH12xX+ezUsgblrWv+Cv4Tw83GiSMHOn9M= -cloud.google.com/go/cloudbuild v1.7.0/go.mod h1:zb5tWh2XI6lR9zQmsm1VRA+7OCuve5d8S+zJUul8KTg= -cloud.google.com/go/cloudbuild v1.9.0/go.mod h1:qK1d7s4QlO0VwfYn5YuClDGg2hfmLZEb4wQGAbIgL1s= -cloud.google.com/go/cloudbuild v1.10.1/go.mod h1:lyJg7v97SUIPq4RC2sGsz/9tNczhyv2AjML/ci4ulzU= -cloud.google.com/go/cloudbuild v1.13.0/go.mod h1:lyJg7v97SUIPq4RC2sGsz/9tNczhyv2AjML/ci4ulzU= -cloud.google.com/go/cloudbuild v1.14.0/go.mod h1:lyJg7v97SUIPq4RC2sGsz/9tNczhyv2AjML/ci4ulzU= -cloud.google.com/go/cloudbuild v1.14.1/go.mod h1:K7wGc/3zfvmYWOWwYTgF/d/UVJhS4pu+HAy7PL7mCsU= -cloud.google.com/go/clouddms v1.3.0/go.mod h1:oK6XsCDdW4Ib3jCCBugx+gVjevp2TMXFtgxvPSee3OM= -cloud.google.com/go/clouddms v1.4.0/go.mod h1:Eh7sUGCC+aKry14O1NRljhjyrr0NFC0G2cjwX0cByRk= -cloud.google.com/go/clouddms v1.5.0/go.mod h1:QSxQnhikCLUw13iAbffF2CZxAER3xDGNHjsTAkQJcQA= -cloud.google.com/go/clouddms v1.6.1/go.mod h1:Ygo1vL52Ov4TBZQquhz5fiw2CQ58gvu+PlS6PVXCpZI= -cloud.google.com/go/clouddms v1.7.0/go.mod h1:MW1dC6SOtI/tPNCciTsXtsGNEM0i0OccykPvv3hiYeM= -cloud.google.com/go/clouddms v1.7.1/go.mod h1:o4SR8U95+P7gZ/TX+YbJxehOCsM+fe6/brlrFquiszk= -cloud.google.com/go/cloudtasks v1.5.0/go.mod h1:fD92REy1x5woxkKEkLdvavGnPJGEn8Uic9nWuLzqCpY= -cloud.google.com/go/cloudtasks v1.6.0/go.mod h1:C6Io+sxuke9/KNRkbQpihnW93SWDU3uXt92nu85HkYI= -cloud.google.com/go/cloudtasks v1.7.0/go.mod h1:ImsfdYWwlWNJbdgPIIGJWC+gemEGTBK/SunNQQNCAb4= -cloud.google.com/go/cloudtasks v1.8.0/go.mod h1:gQXUIwCSOI4yPVK7DgTVFiiP0ZW/eQkydWzwVMdHxrI= -cloud.google.com/go/cloudtasks v1.9.0/go.mod h1:w+EyLsVkLWHcOaqNEyvcKAsWp9p29dL6uL9Nst1cI7Y= -cloud.google.com/go/cloudtasks v1.10.0/go.mod h1:NDSoTLkZ3+vExFEWu2UJV1arUyzVDAiZtdWcsUyNwBs= -cloud.google.com/go/cloudtasks v1.11.1/go.mod h1:a9udmnou9KO2iulGscKR0qBYjreuX8oHwpmFsKspEvM= -cloud.google.com/go/cloudtasks v1.12.1/go.mod h1:a9udmnou9KO2iulGscKR0qBYjreuX8oHwpmFsKspEvM= -cloud.google.com/go/cloudtasks v1.12.2/go.mod h1:A7nYkjNlW2gUoROg1kvJrQGhJP/38UaWwsnuBDOBVUk= cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= -cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= -cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU= -cloud.google.com/go/compute v1.12.0/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= -cloud.google.com/go/compute v1.12.1/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= -cloud.google.com/go/compute v1.13.0/go.mod h1:5aPTS0cUNMIc1CE546K+Th6weJUNQErARyZtRXDJ8GE= -cloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvjxega5vAdo= -cloud.google.com/go/compute v1.15.1/go.mod h1:bjjoF/NtFUrkD/urWfdHaKuOPDR5nWIs63rR+SXhcpA= -cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs= -cloud.google.com/go/compute v1.19.0/go.mod h1:rikpw2y+UMidAe9tISo04EHNOIf42RLYF/q8Bs93scU= -cloud.google.com/go/compute v1.19.1/go.mod h1:6ylj3a05WF8leseCdIf77NK0g1ey+nj5IKd5/kvShxE= -cloud.google.com/go/compute v1.19.3/go.mod h1:qxvISKp/gYnXkSAD1ppcSOveRAmzxicEv/JlizULFrI= -cloud.google.com/go/compute v1.20.1/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= -cloud.google.com/go/compute v1.21.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= -cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= -cloud.google.com/go/compute v1.23.1/go.mod h1:CqB3xpmPKKt3OJpW2ndFIXnA9A4xAy/F3Xp1ixncW78= cloud.google.com/go/compute v1.24.0 h1:phWcR2eWzRJaL/kOiJwfFsPs4BaKq1j6vnpZrc1YlVg= cloud.google.com/go/compute v1.24.0/go.mod h1:kw1/T+h/+tK2LJK0wiPPx1intgdAM3j/g3hFDlscY40= -cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU= -cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= -cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= -cloud.google.com/go/contactcenterinsights v1.3.0/go.mod h1:Eu2oemoePuEFc/xKFPjbTuPSj0fYJcPls9TFlPNnHHY= -cloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck= -cloud.google.com/go/contactcenterinsights v1.6.0/go.mod h1:IIDlT6CLcDoyv79kDv8iWxMSTZhLxSCofVV5W6YFM/w= -cloud.google.com/go/contactcenterinsights v1.9.1/go.mod h1:bsg/R7zGLYMVxFFzfh9ooLTruLRCG9fnzhH9KznHhbM= -cloud.google.com/go/contactcenterinsights v1.10.0/go.mod h1:bsg/R7zGLYMVxFFzfh9ooLTruLRCG9fnzhH9KznHhbM= -cloud.google.com/go/contactcenterinsights v1.11.0/go.mod h1:hutBdImE4XNZ1NV4vbPJKSFOnQruhC5Lj9bZqWMTKiU= -cloud.google.com/go/contactcenterinsights v1.11.1/go.mod h1:FeNP3Kg8iteKM80lMwSk3zZZKVxr+PGnAId6soKuXwE= -cloud.google.com/go/container v1.6.0/go.mod h1:Xazp7GjJSeUYo688S+6J5V+n/t+G5sKBTFkKNudGRxg= -cloud.google.com/go/container v1.7.0/go.mod h1:Dp5AHtmothHGX3DwwIHPgq45Y8KmNsgN3amoYfxVkLo= -cloud.google.com/go/container v1.13.1/go.mod h1:6wgbMPeQRw9rSnKBCAJXnds3Pzj03C4JHamr8asWKy4= -cloud.google.com/go/container v1.14.0/go.mod h1:3AoJMPhHfLDxLvrlVWaK57IXzaPnLaZq63WX59aQBfM= -cloud.google.com/go/container v1.15.0/go.mod h1:ft+9S0WGjAyjDggg5S06DXj+fHJICWg8L7isCQe9pQA= -cloud.google.com/go/container v1.22.1/go.mod h1:lTNExE2R7f+DLbAN+rJiKTisauFCaoDq6NURZ83eVH4= -cloud.google.com/go/container v1.24.0/go.mod h1:lTNExE2R7f+DLbAN+rJiKTisauFCaoDq6NURZ83eVH4= -cloud.google.com/go/container v1.26.0/go.mod h1:YJCmRet6+6jnYYRS000T6k0D0xUXQgBSaJ7VwI8FBj4= -cloud.google.com/go/container v1.26.1/go.mod h1:5smONjPRUxeEpDG7bMKWfDL4sauswqEtnBK1/KKpR04= -cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I= -cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4= -cloud.google.com/go/containeranalysis v0.7.0/go.mod h1:9aUL+/vZ55P2CXfuZjS4UjQ9AgXoSw8Ts6lemfmxBxI= -cloud.google.com/go/containeranalysis v0.9.0/go.mod h1:orbOANbwk5Ejoom+s+DUCTTJ7IBdBQJDcSylAx/on9s= -cloud.google.com/go/containeranalysis v0.10.1/go.mod h1:Ya2jiILITMY68ZLPaogjmOMNkwsDrWBSTyBubGXO7j0= -cloud.google.com/go/containeranalysis v0.11.0/go.mod h1:4n2e99ZwpGxpNcz+YsFT1dfOHPQFGcAC8FN2M2/ne/U= -cloud.google.com/go/containeranalysis v0.11.1/go.mod h1:rYlUOM7nem1OJMKwE1SadufX0JP3wnXj844EtZAwWLY= -cloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSiyOzm8kpLq0a0= -cloud.google.com/go/datacatalog v1.5.0/go.mod h1:M7GPLNQeLfWqeIm3iuiruhPzkt65+Bx8dAKvScX8jvs= -cloud.google.com/go/datacatalog v1.6.0/go.mod h1:+aEyF8JKg+uXcIdAmmaMUmZ3q1b/lKLtXCmXdnc0lbc= -cloud.google.com/go/datacatalog v1.7.0/go.mod h1:9mEl4AuDYWw81UGc41HonIHH7/sn52H0/tc8f8ZbZIE= -cloud.google.com/go/datacatalog v1.8.0/go.mod h1:KYuoVOv9BM8EYz/4eMFxrr4DUKhGIOXxZoKYF5wdISM= -cloud.google.com/go/datacatalog v1.8.1/go.mod h1:RJ58z4rMp3gvETA465Vg+ag8BGgBdnRPEMMSTr5Uv+M= -cloud.google.com/go/datacatalog v1.12.0/go.mod h1:CWae8rFkfp6LzLumKOnmVh4+Zle4A3NXLzVJ1d1mRm0= -cloud.google.com/go/datacatalog v1.13.0/go.mod h1:E4Rj9a5ZtAxcQJlEBTLgMTphfP11/lNaAshpoBgemX8= -cloud.google.com/go/datacatalog v1.14.0/go.mod h1:h0PrGtlihoutNMp/uvwhawLQ9+c63Kz65UFqh49Yo+E= -cloud.google.com/go/datacatalog v1.14.1/go.mod h1:d2CevwTG4yedZilwe+v3E3ZBDRMobQfSG/a6cCCN5R4= -cloud.google.com/go/datacatalog v1.16.0/go.mod h1:d2CevwTG4yedZilwe+v3E3ZBDRMobQfSG/a6cCCN5R4= -cloud.google.com/go/datacatalog v1.17.1/go.mod h1:nCSYFHgtxh2MiEktWIz71s/X+7ds/UT9kp0PC7waCzE= -cloud.google.com/go/datacatalog v1.18.0/go.mod h1:nCSYFHgtxh2MiEktWIz71s/X+7ds/UT9kp0PC7waCzE= -cloud.google.com/go/datacatalog v1.18.1/go.mod h1:TzAWaz+ON1tkNr4MOcak8EBHX7wIRX/gZKM+yTVsv+A= -cloud.google.com/go/dataflow v0.6.0/go.mod h1:9QwV89cGoxjjSR9/r7eFDqqjtvbKxAK2BaYU6PVk9UM= -cloud.google.com/go/dataflow v0.7.0/go.mod h1:PX526vb4ijFMesO1o202EaUmouZKBpjHsTlCtB4parQ= -cloud.google.com/go/dataflow v0.8.0/go.mod h1:Rcf5YgTKPtQyYz8bLYhFoIV/vP39eL7fWNcSOyFfLJE= -cloud.google.com/go/dataflow v0.9.1/go.mod h1:Wp7s32QjYuQDWqJPFFlnBKhkAtiFpMTdg00qGbnIHVw= -cloud.google.com/go/dataflow v0.9.2/go.mod h1:vBfdBZ/ejlTaYIGB3zB4T08UshH70vbtZeMD+urnUSo= -cloud.google.com/go/dataform v0.3.0/go.mod h1:cj8uNliRlHpa6L3yVhDOBrUXH+BPAO1+KFMQQNSThKo= -cloud.google.com/go/dataform v0.4.0/go.mod h1:fwV6Y4Ty2yIFL89huYlEkwUPtS7YZinZbzzj5S9FzCE= -cloud.google.com/go/dataform v0.5.0/go.mod h1:GFUYRe8IBa2hcomWplodVmUx/iTL0FrsauObOM3Ipr0= -cloud.google.com/go/dataform v0.6.0/go.mod h1:QPflImQy33e29VuapFdf19oPbE4aYTJxr31OAPV+ulA= -cloud.google.com/go/dataform v0.7.0/go.mod h1:7NulqnVozfHvWUBpMDfKMUESr+85aJsC/2O0o3jWPDE= -cloud.google.com/go/dataform v0.8.1/go.mod h1:3BhPSiw8xmppbgzeBbmDvmSWlwouuJkXsXsb8UBih9M= -cloud.google.com/go/dataform v0.8.2/go.mod h1:X9RIqDs6NbGPLR80tnYoPNiO1w0wenKTb8PxxlhTMKM= -cloud.google.com/go/datafusion v1.4.0/go.mod h1:1Zb6VN+W6ALo85cXnM1IKiPw+yQMKMhB9TsTSRDo/38= -cloud.google.com/go/datafusion v1.5.0/go.mod h1:Kz+l1FGHB0J+4XF2fud96WMmRiq/wj8N9u007vyXZ2w= -cloud.google.com/go/datafusion v1.6.0/go.mod h1:WBsMF8F1RhSXvVM8rCV3AeyWVxcC2xY6vith3iw3S+8= -cloud.google.com/go/datafusion v1.7.1/go.mod h1:KpoTBbFmoToDExJUso/fcCiguGDk7MEzOWXUsJo0wsI= -cloud.google.com/go/datafusion v1.7.2/go.mod h1:62K2NEC6DRlpNmI43WHMWf9Vg/YvN6QVi8EVwifElI0= -cloud.google.com/go/datalabeling v0.5.0/go.mod h1:TGcJ0G2NzcsXSE/97yWjIZO0bXj0KbVlINXMG9ud42I= -cloud.google.com/go/datalabeling v0.6.0/go.mod h1:WqdISuk/+WIGeMkpw/1q7bK/tFEZxsrFJOJdY2bXvTQ= -cloud.google.com/go/datalabeling v0.7.0/go.mod h1:WPQb1y08RJbmpM3ww0CSUAGweL0SxByuW2E+FU+wXcM= -cloud.google.com/go/datalabeling v0.8.1/go.mod h1:XS62LBSVPbYR54GfYQsPXZjTW8UxCK2fkDciSrpRFdY= -cloud.google.com/go/datalabeling v0.8.2/go.mod h1:cyDvGHuJWu9U/cLDA7d8sb9a0tWLEletStu2sTmg3BE= -cloud.google.com/go/dataplex v1.3.0/go.mod h1:hQuRtDg+fCiFgC8j0zV222HvzFQdRd+SVX8gdmFcZzA= -cloud.google.com/go/dataplex v1.4.0/go.mod h1:X51GfLXEMVJ6UN47ESVqvlsRplbLhcsAt0kZCCKsU0A= -cloud.google.com/go/dataplex v1.5.2/go.mod h1:cVMgQHsmfRoI5KFYq4JtIBEUbYwc3c7tXmIDhRmNNVQ= -cloud.google.com/go/dataplex v1.6.0/go.mod h1:bMsomC/aEJOSpHXdFKFGQ1b0TDPIeL28nJObeO1ppRs= -cloud.google.com/go/dataplex v1.8.1/go.mod h1:7TyrDT6BCdI8/38Uvp0/ZxBslOslP2X2MPDucliyvSE= -cloud.google.com/go/dataplex v1.9.0/go.mod h1:7TyrDT6BCdI8/38Uvp0/ZxBslOslP2X2MPDucliyvSE= -cloud.google.com/go/dataplex v1.9.1/go.mod h1:7TyrDT6BCdI8/38Uvp0/ZxBslOslP2X2MPDucliyvSE= -cloud.google.com/go/dataplex v1.10.1/go.mod h1:1MzmBv8FvjYfc7vDdxhnLFNskikkB+3vl475/XdCDhs= -cloud.google.com/go/dataproc v1.7.0/go.mod h1:CKAlMjII9H90RXaMpSxQ8EU6dQx6iAYNPcYPOkSbi8s= -cloud.google.com/go/dataproc v1.8.0/go.mod h1:5OW+zNAH0pMpw14JVrPONsxMQYMBqJuzORhIBfBn9uI= -cloud.google.com/go/dataproc v1.12.0/go.mod h1:zrF3aX0uV3ikkMz6z4uBbIKyhRITnxvr4i3IjKsKrw4= -cloud.google.com/go/dataproc/v2 v2.0.1/go.mod h1:7Ez3KRHdFGcfY7GcevBbvozX+zyWGcwLJvvAMwCaoZ4= -cloud.google.com/go/dataproc/v2 v2.2.0/go.mod h1:lZR7AQtwZPvmINx5J87DSOOpTfof9LVZju6/Qo4lmcY= -cloud.google.com/go/dataproc/v2 v2.2.1/go.mod h1:QdAJLaBjh+l4PVlVZcmrmhGccosY/omC1qwfQ61Zv/o= -cloud.google.com/go/dataqna v0.5.0/go.mod h1:90Hyk596ft3zUQ8NkFfvICSIfHFh1Bc7C4cK3vbhkeo= -cloud.google.com/go/dataqna v0.6.0/go.mod h1:1lqNpM7rqNLVgWBJyk5NF6Uen2PHym0jtVJonplVsDA= -cloud.google.com/go/dataqna v0.7.0/go.mod h1:Lx9OcIIeqCrw1a6KdO3/5KMP1wAmTc0slZWwP12Qq3c= -cloud.google.com/go/dataqna v0.8.1/go.mod h1:zxZM0Bl6liMePWsHA8RMGAfmTG34vJMapbHAxQ5+WA8= -cloud.google.com/go/dataqna v0.8.2/go.mod h1:KNEqgx8TTmUipnQsScOoDpq/VlXVptUqVMZnt30WAPs= 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/datastore v1.10.0/go.mod h1:PC5UzAmDEkAmkfaknstTYbNpgE49HAgW2J1gcgUfmdM= -cloud.google.com/go/datastore v1.11.0/go.mod h1:TvGxBIHCS50u8jzG+AW/ppf87v1of8nwzFNgEZU1D3c= -cloud.google.com/go/datastore v1.12.0/go.mod h1:KjdB88W897MRITkvWWJrg2OUtrR5XVj1EoLgSp6/N70= -cloud.google.com/go/datastore v1.12.1/go.mod h1:KjdB88W897MRITkvWWJrg2OUtrR5XVj1EoLgSp6/N70= -cloud.google.com/go/datastore v1.13.0/go.mod h1:KjdB88W897MRITkvWWJrg2OUtrR5XVj1EoLgSp6/N70= -cloud.google.com/go/datastore v1.14.0/go.mod h1:GAeStMBIt9bPS7jMJA85kgkpsMkvseWWXiaHya9Jes8= -cloud.google.com/go/datastore v1.15.0/go.mod h1:GAeStMBIt9bPS7jMJA85kgkpsMkvseWWXiaHya9Jes8= -cloud.google.com/go/datastream v1.2.0/go.mod h1:i/uTP8/fZwgATHS/XFu0TcNUhuA0twZxxQ3EyCUQMwo= -cloud.google.com/go/datastream v1.3.0/go.mod h1:cqlOX8xlyYF/uxhiKn6Hbv6WjwPPuI9W2M9SAXwaLLQ= -cloud.google.com/go/datastream v1.4.0/go.mod h1:h9dpzScPhDTs5noEMQVWP8Wx8AFBRyS0s8KWPx/9r0g= -cloud.google.com/go/datastream v1.5.0/go.mod h1:6TZMMNPwjUqZHBKPQ1wwXpb0d5VDVPl2/XoS5yi88q4= -cloud.google.com/go/datastream v1.6.0/go.mod h1:6LQSuswqLa7S4rPAOZFVjHIG3wJIjZcZrw8JDEDJuIs= -cloud.google.com/go/datastream v1.7.0/go.mod h1:uxVRMm2elUSPuh65IbZpzJNMbuzkcvu5CjMqVIUHrww= -cloud.google.com/go/datastream v1.9.1/go.mod h1:hqnmr8kdUBmrnk65k5wNRoHSCYksvpdZIcZIEl8h43Q= -cloud.google.com/go/datastream v1.10.0/go.mod h1:hqnmr8kdUBmrnk65k5wNRoHSCYksvpdZIcZIEl8h43Q= -cloud.google.com/go/datastream v1.10.1/go.mod h1:7ngSYwnw95YFyTd5tOGBxHlOZiL+OtpjheqU7t2/s/c= -cloud.google.com/go/deploy v1.4.0/go.mod h1:5Xghikd4VrmMLNaF6FiRFDlHb59VM59YoDQnOUdsH/c= -cloud.google.com/go/deploy v1.5.0/go.mod h1:ffgdD0B89tToyW/U/D2eL0jN2+IEV/3EMuXHA0l4r+s= -cloud.google.com/go/deploy v1.6.0/go.mod h1:f9PTHehG/DjCom3QH0cntOVRm93uGBDt2vKzAPwpXQI= -cloud.google.com/go/deploy v1.8.0/go.mod h1:z3myEJnA/2wnB4sgjqdMfgxCA0EqC3RBTNcVPs93mtQ= -cloud.google.com/go/deploy v1.11.0/go.mod h1:tKuSUV5pXbn67KiubiUNUejqLs4f5cxxiCNCeyl0F2g= -cloud.google.com/go/deploy v1.13.0/go.mod h1:tKuSUV5pXbn67KiubiUNUejqLs4f5cxxiCNCeyl0F2g= -cloud.google.com/go/deploy v1.13.1/go.mod h1:8jeadyLkH9qu9xgO3hVWw8jVr29N1mnW42gRJT8GY6g= -cloud.google.com/go/dialogflow v1.15.0/go.mod h1:HbHDWs33WOGJgn6rfzBW1Kv807BE3O1+xGbn59zZWI4= -cloud.google.com/go/dialogflow v1.16.1/go.mod h1:po6LlzGfK+smoSmTBnbkIZY2w8ffjz/RcGSS+sh1el0= -cloud.google.com/go/dialogflow v1.17.0/go.mod h1:YNP09C/kXA1aZdBgC/VtXX74G/TKn7XVCcVumTflA+8= -cloud.google.com/go/dialogflow v1.18.0/go.mod h1:trO7Zu5YdyEuR+BhSNOqJezyFQ3aUzz0njv7sMx/iek= -cloud.google.com/go/dialogflow v1.19.0/go.mod h1:JVmlG1TwykZDtxtTXujec4tQ+D8SBFMoosgy+6Gn0s0= -cloud.google.com/go/dialogflow v1.29.0/go.mod h1:b+2bzMe+k1s9V+F2jbJwpHPzrnIyHihAdRFMtn2WXuM= -cloud.google.com/go/dialogflow v1.31.0/go.mod h1:cuoUccuL1Z+HADhyIA7dci3N5zUssgpBJmCzI6fNRB4= -cloud.google.com/go/dialogflow v1.32.0/go.mod h1:jG9TRJl8CKrDhMEcvfcfFkkpp8ZhgPz3sBGmAUYJ2qE= -cloud.google.com/go/dialogflow v1.38.0/go.mod h1:L7jnH+JL2mtmdChzAIcXQHXMvQkE3U4hTaNltEuxXn4= -cloud.google.com/go/dialogflow v1.40.0/go.mod h1:L7jnH+JL2mtmdChzAIcXQHXMvQkE3U4hTaNltEuxXn4= -cloud.google.com/go/dialogflow v1.43.0/go.mod h1:pDUJdi4elL0MFmt1REMvFkdsUTYSHq+rTCS8wg0S3+M= -cloud.google.com/go/dialogflow v1.44.0/go.mod h1:pDUJdi4elL0MFmt1REMvFkdsUTYSHq+rTCS8wg0S3+M= -cloud.google.com/go/dialogflow v1.44.1/go.mod h1:n/h+/N2ouKOO+rbe/ZnI186xImpqvCVj2DdsWS/0EAk= -cloud.google.com/go/dlp v1.6.0/go.mod h1:9eyB2xIhpU0sVwUixfBubDoRwP+GjeUoxxeueZmqvmM= -cloud.google.com/go/dlp v1.7.0/go.mod h1:68ak9vCiMBjbasxeVD17hVPxDEck+ExiHavX8kiHG+Q= -cloud.google.com/go/dlp v1.9.0/go.mod h1:qdgmqgTyReTz5/YNSSuueR8pl7hO0o9bQ39ZhtgkWp4= -cloud.google.com/go/dlp v1.10.1/go.mod h1:IM8BWz1iJd8njcNcG0+Kyd9OPnqnRNkDV8j42VT5KOI= -cloud.google.com/go/dlp v1.10.2/go.mod h1:ZbdKIhcnyhILgccwVDzkwqybthh7+MplGC3kZVZsIOQ= -cloud.google.com/go/documentai v1.7.0/go.mod h1:lJvftZB5NRiFSX4moiye1SMxHx0Bc3x1+p9e/RfXYiU= -cloud.google.com/go/documentai v1.8.0/go.mod h1:xGHNEB7CtsnySCNrCFdCyyMz44RhFEEX2Q7UD0c5IhU= -cloud.google.com/go/documentai v1.9.0/go.mod h1:FS5485S8R00U10GhgBC0aNGrJxBP8ZVpEeJ7PQDZd6k= -cloud.google.com/go/documentai v1.10.0/go.mod h1:vod47hKQIPeCfN2QS/jULIvQTugbmdc0ZvxxfQY1bg4= -cloud.google.com/go/documentai v1.16.0/go.mod h1:o0o0DLTEZ+YnJZ+J4wNfTxmDVyrkzFvttBXXtYRMHkM= -cloud.google.com/go/documentai v1.18.0/go.mod h1:F6CK6iUH8J81FehpskRmhLq/3VlwQvb7TvwOceQ2tbs= -cloud.google.com/go/documentai v1.20.0/go.mod h1:yJkInoMcK0qNAEdRnqY/D5asy73tnPe88I1YTZT+a8E= -cloud.google.com/go/documentai v1.22.0/go.mod h1:yJkInoMcK0qNAEdRnqY/D5asy73tnPe88I1YTZT+a8E= -cloud.google.com/go/documentai v1.22.1/go.mod h1:LKs22aDHbJv7ufXuPypzRO7rG3ALLJxzdCXDPutw4Qc= -cloud.google.com/go/documentai v1.23.0/go.mod h1:LKs22aDHbJv7ufXuPypzRO7rG3ALLJxzdCXDPutw4Qc= -cloud.google.com/go/documentai v1.23.2/go.mod h1:Q/wcRT+qnuXOpjAkvOV4A+IeQl04q2/ReT7SSbytLSo= -cloud.google.com/go/domains v0.6.0/go.mod h1:T9Rz3GasrpYk6mEGHh4rymIhjlnIuB4ofT1wTxDeT4Y= -cloud.google.com/go/domains v0.7.0/go.mod h1:PtZeqS1xjnXuRPKE/88Iru/LdfoRyEHYA9nFQf4UKpg= -cloud.google.com/go/domains v0.8.0/go.mod h1:M9i3MMDzGFXsydri9/vW+EWz9sWb4I6WyHqdlAk0idE= -cloud.google.com/go/domains v0.9.1/go.mod h1:aOp1c0MbejQQ2Pjf1iJvnVyT+z6R6s8pX66KaCSDYfE= -cloud.google.com/go/domains v0.9.2/go.mod h1:3YvXGYzZG1Temjbk7EyGCuGGiXHJwVNmwIf+E/cUp5I= -cloud.google.com/go/edgecontainer v0.1.0/go.mod h1:WgkZ9tp10bFxqO8BLPqv2LlfmQF1X8lZqwW4r1BTajk= -cloud.google.com/go/edgecontainer v0.2.0/go.mod h1:RTmLijy+lGpQ7BXuTDa4C4ssxyXT34NIuHIgKuP4s5w= -cloud.google.com/go/edgecontainer v0.3.0/go.mod h1:FLDpP4nykgwwIfcLt6zInhprzw0lEi2P1fjO6Ie0qbc= -cloud.google.com/go/edgecontainer v1.0.0/go.mod h1:cttArqZpBB2q58W/upSG++ooo6EsblxDIolxa3jSjbY= -cloud.google.com/go/edgecontainer v1.1.1/go.mod h1:O5bYcS//7MELQZs3+7mabRqoWQhXCzenBu0R8bz2rwk= -cloud.google.com/go/edgecontainer v1.1.2/go.mod h1:wQRjIzqxEs9e9wrtle4hQPSR1Y51kqN75dgF7UllZZ4= -cloud.google.com/go/errorreporting v0.3.0/go.mod h1:xsP2yaAp+OAW4OIm60An2bbLpqIhKXdWR/tawvl7QzU= -cloud.google.com/go/essentialcontacts v1.3.0/go.mod h1:r+OnHa5jfj90qIfZDO/VztSFqbQan7HV75p8sA+mdGI= -cloud.google.com/go/essentialcontacts v1.4.0/go.mod h1:8tRldvHYsmnBCHdFpvU+GL75oWiBKl80BiqlFh9tp+8= -cloud.google.com/go/essentialcontacts v1.5.0/go.mod h1:ay29Z4zODTuwliK7SnX8E86aUF2CTzdNtvv42niCX0M= -cloud.google.com/go/essentialcontacts v1.6.2/go.mod h1:T2tB6tX+TRak7i88Fb2N9Ok3PvY3UNbUsMag9/BARh4= -cloud.google.com/go/essentialcontacts v1.6.3/go.mod h1:yiPCD7f2TkP82oJEFXFTou8Jl8L6LBRPeBEkTaO0Ggo= -cloud.google.com/go/eventarc v1.7.0/go.mod h1:6ctpF3zTnaQCxUjHUdcfgcA1A2T309+omHZth7gDfmc= -cloud.google.com/go/eventarc v1.8.0/go.mod h1:imbzxkyAU4ubfsaKYdQg04WS1NvncblHEup4kvF+4gw= -cloud.google.com/go/eventarc v1.10.0/go.mod h1:u3R35tmZ9HvswGRBnF48IlYgYeBcPUCjkr4BTdem2Kw= -cloud.google.com/go/eventarc v1.11.0/go.mod h1:PyUjsUKPWoRBCHeOxZd/lbOOjahV41icXyUY5kSTvVY= -cloud.google.com/go/eventarc v1.12.1/go.mod h1:mAFCW6lukH5+IZjkvrEss+jmt2kOdYlN8aMx3sRJiAI= -cloud.google.com/go/eventarc v1.13.0/go.mod h1:mAFCW6lukH5+IZjkvrEss+jmt2kOdYlN8aMx3sRJiAI= -cloud.google.com/go/eventarc v1.13.1/go.mod h1:EqBxmGHFrruIara4FUQ3RHlgfCn7yo1HYsu2Hpt/C3Y= -cloud.google.com/go/filestore v1.3.0/go.mod h1:+qbvHGvXU1HaKX2nD0WEPo92TP/8AQuCVEBXNY9z0+w= -cloud.google.com/go/filestore v1.4.0/go.mod h1:PaG5oDfo9r224f8OYXURtAsY+Fbyq/bLYoINEK8XQAI= -cloud.google.com/go/filestore v1.5.0/go.mod h1:FqBXDWBp4YLHqRnVGveOkHDf8svj9r5+mUDLupOWEDs= -cloud.google.com/go/filestore v1.6.0/go.mod h1:di5unNuss/qfZTw2U9nhFqo8/ZDSc466dre85Kydllg= -cloud.google.com/go/filestore v1.7.1/go.mod h1:y10jsorq40JJnjR/lQ8AfFbbcGlw3g+Dp8oN7i7FjV4= -cloud.google.com/go/filestore v1.7.2/go.mod h1:TYOlyJs25f/omgj+vY7/tIG/E7BX369triSPzE4LdgE= -cloud.google.com/go/firestore v1.9.0/go.mod h1:HMkjKHNTtRyZNiMzu7YAsLr9K3X2udY2AMwDaMEQiiE= -cloud.google.com/go/firestore v1.11.0/go.mod h1:b38dKhgzlmNNGTNZZwe7ZRFEuRab1Hay3/DBsIGKKy4= -cloud.google.com/go/firestore v1.12.0/go.mod h1:b38dKhgzlmNNGTNZZwe7ZRFEuRab1Hay3/DBsIGKKy4= -cloud.google.com/go/firestore v1.13.0/go.mod h1:QojqqOh8IntInDUSTAh0c8ZsPYAr68Ma8c5DWOy8xb8= -cloud.google.com/go/functions v1.6.0/go.mod h1:3H1UA3qiIPRWD7PeZKLvHZ9SaQhR26XIJcC0A5GbvAk= -cloud.google.com/go/functions v1.7.0/go.mod h1:+d+QBcWM+RsrgZfV9xo6KfA1GlzJfxcfZcRPEhDDfzg= -cloud.google.com/go/functions v1.8.0/go.mod h1:RTZ4/HsQjIqIYP9a9YPbU+QFoQsAlYgrwOXJWHn1POY= -cloud.google.com/go/functions v1.9.0/go.mod h1:Y+Dz8yGguzO3PpIjhLTbnqV1CWmgQ5UwtlpzoyquQ08= -cloud.google.com/go/functions v1.10.0/go.mod h1:0D3hEOe3DbEvCXtYOZHQZmD+SzYsi1YbI7dGvHfldXw= -cloud.google.com/go/functions v1.12.0/go.mod h1:AXWGrF3e2C/5ehvwYo/GH6O5s09tOPksiKhz+hH8WkA= -cloud.google.com/go/functions v1.13.0/go.mod h1:EU4O007sQm6Ef/PwRsI8N2umygGqPBS/IZQKBQBcJ3c= -cloud.google.com/go/functions v1.15.1/go.mod h1:P5yNWUTkyU+LvW/S9O6V+V423VZooALQlqoXdoPz5AE= -cloud.google.com/go/functions v1.15.2/go.mod h1:CHAjtcR6OU4XF2HuiVeriEdELNcnvRZSk1Q8RMqy4lE= -cloud.google.com/go/gaming v1.5.0/go.mod h1:ol7rGcxP/qHTRQE/RO4bxkXq+Fix0j6D4LFPzYTIrDM= -cloud.google.com/go/gaming v1.6.0/go.mod h1:YMU1GEvA39Qt3zWGyAVA9bpYz/yAhTvaQ1t2sK4KPUA= -cloud.google.com/go/gaming v1.7.0/go.mod h1:LrB8U7MHdGgFG851iHAfqUdLcKBdQ55hzXy9xBJz0+w= -cloud.google.com/go/gaming v1.8.0/go.mod h1:xAqjS8b7jAVW0KFYeRUxngo9My3f33kFmua++Pi+ggM= -cloud.google.com/go/gaming v1.9.0/go.mod h1:Fc7kEmCObylSWLO334NcO+O9QMDyz+TKC4v1D7X+Bc0= -cloud.google.com/go/gaming v1.10.1/go.mod h1:XQQvtfP8Rb9Rxnxm5wFVpAp9zCQkJi2bLIb7iHGwB3s= -cloud.google.com/go/gkebackup v0.2.0/go.mod h1:XKvv/4LfG829/B8B7xRkk8zRrOEbKtEam6yNfuQNH60= -cloud.google.com/go/gkebackup v0.3.0/go.mod h1:n/E671i1aOQvUxT541aTkCwExO/bTer2HDlj4TsBRAo= -cloud.google.com/go/gkebackup v0.4.0/go.mod h1:byAyBGUwYGEEww7xsbnUTBHIYcOPy/PgUWUtOeRm9Vg= -cloud.google.com/go/gkebackup v1.3.0/go.mod h1:vUDOu++N0U5qs4IhG1pcOnD1Mac79xWy6GoBFlWCWBU= -cloud.google.com/go/gkebackup v1.3.1/go.mod h1:vUDOu++N0U5qs4IhG1pcOnD1Mac79xWy6GoBFlWCWBU= -cloud.google.com/go/gkebackup v1.3.2/go.mod h1:OMZbXzEJloyXMC7gqdSB+EOEQ1AKcpGYvO3s1ec5ixk= -cloud.google.com/go/gkeconnect v0.5.0/go.mod h1:c5lsNAg5EwAy7fkqX/+goqFsU1Da/jQFqArp+wGNr/o= -cloud.google.com/go/gkeconnect v0.6.0/go.mod h1:Mln67KyU/sHJEBY8kFZ0xTeyPtzbq9StAVvEULYK16A= -cloud.google.com/go/gkeconnect v0.7.0/go.mod h1:SNfmVqPkaEi3bF/B3CNZOAYPYdg7sU+obZ+QTky2Myw= -cloud.google.com/go/gkeconnect v0.8.1/go.mod h1:KWiK1g9sDLZqhxB2xEuPV8V9NYzrqTUmQR9shJHpOZw= -cloud.google.com/go/gkeconnect v0.8.2/go.mod h1:6nAVhwchBJYgQCXD2pHBFQNiJNyAd/wyxljpaa6ZPrY= -cloud.google.com/go/gkehub v0.9.0/go.mod h1:WYHN6WG8w9bXU0hqNxt8rm5uxnk8IH+lPY9J2TV7BK0= -cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y977wO+hBH0= -cloud.google.com/go/gkehub v0.11.0/go.mod h1:JOWHlmN+GHyIbuWQPl47/C2RFhnFKH38jH9Ascu3n0E= -cloud.google.com/go/gkehub v0.12.0/go.mod h1:djiIwwzTTBrF5NaXCGv3mf7klpEMcST17VBTVVDcuaw= -cloud.google.com/go/gkehub v0.14.1/go.mod h1:VEXKIJZ2avzrbd7u+zeMtW00Y8ddk/4V9511C9CQGTY= -cloud.google.com/go/gkehub v0.14.2/go.mod h1:iyjYH23XzAxSdhrbmfoQdePnlMj2EWcvnR+tHdBQsCY= -cloud.google.com/go/gkemulticloud v0.3.0/go.mod h1:7orzy7O0S+5kq95e4Hpn7RysVA7dPs8W/GgfUtsPbrA= -cloud.google.com/go/gkemulticloud v0.4.0/go.mod h1:E9gxVBnseLWCk24ch+P9+B2CoDFJZTyIgLKSalC7tuI= -cloud.google.com/go/gkemulticloud v0.5.0/go.mod h1:W0JDkiyi3Tqh0TJr//y19wyb1yf8llHVto2Htf2Ja3Y= -cloud.google.com/go/gkemulticloud v0.6.1/go.mod h1:kbZ3HKyTsiwqKX7Yw56+wUGwwNZViRnxWK2DVknXWfw= -cloud.google.com/go/gkemulticloud v1.0.0/go.mod h1:kbZ3HKyTsiwqKX7Yw56+wUGwwNZViRnxWK2DVknXWfw= -cloud.google.com/go/gkemulticloud v1.0.1/go.mod h1:AcrGoin6VLKT/fwZEYuqvVominLriQBCKmbjtnbMjG8= -cloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc= -cloud.google.com/go/grafeas v0.3.0/go.mod h1:P7hgN24EyONOTMyeJH6DxG4zD7fwiYa5Q6GUgyFSOU8= -cloud.google.com/go/gsuiteaddons v1.3.0/go.mod h1:EUNK/J1lZEZO8yPtykKxLXI6JSVN2rg9bN8SXOa0bgM= -cloud.google.com/go/gsuiteaddons v1.4.0/go.mod h1:rZK5I8hht7u7HxFQcFei0+AtfS9uSushomRlg+3ua1o= -cloud.google.com/go/gsuiteaddons v1.5.0/go.mod h1:TFCClYLd64Eaa12sFVmUyG62tk4mdIsI7pAnSXRkcFo= -cloud.google.com/go/gsuiteaddons v1.6.1/go.mod h1:CodrdOqRZcLp5WOwejHWYBjZvfY0kOphkAKpF/3qdZY= -cloud.google.com/go/gsuiteaddons v1.6.2/go.mod h1:K65m9XSgs8hTF3X9nNTPi8IQueljSdYo9F+Mi+s4MyU= -cloud.google.com/go/iam v0.1.0/go.mod h1:vcUNEa0pEm0qRVpmWepWaFMIAI8/hjB9mO8rNCJtF6c= cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= -cloud.google.com/go/iam v0.5.0/go.mod h1:wPU9Vt0P4UmCux7mqtRu6jcpPAb74cP1fh50J3QpkUc= -cloud.google.com/go/iam v0.6.0/go.mod h1:+1AH33ueBne5MzYccyMHtEKqLE4/kJOibtffMHDMFMc= -cloud.google.com/go/iam v0.7.0/go.mod h1:H5Br8wRaDGNc8XP3keLc4unfUUZeyH3Sfl9XpQEYOeg= -cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE= -cloud.google.com/go/iam v0.11.0/go.mod h1:9PiLDanza5D+oWFZiH1uG+RnRCfEGKoyl6yo4cgWZGY= -cloud.google.com/go/iam v0.12.0/go.mod h1:knyHGviacl11zrtZUoDuYpDgLjvr28sLQaG0YB2GYAY= -cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0= -cloud.google.com/go/iam v1.0.1/go.mod h1:yR3tmSL8BcZB4bxByRv2jkSIahVmCtfKZwLYGBalRE8= -cloud.google.com/go/iam v1.1.0/go.mod h1:nxdHjaKfCr7fNYx/HJMM8LgiMugmveWlkatear5gVyk= -cloud.google.com/go/iam v1.1.1/go.mod h1:A5avdyVL2tCppe4unb0951eI9jreack+RJ0/d+KUZOU= -cloud.google.com/go/iam v1.1.2/go.mod h1:A5avdyVL2tCppe4unb0951eI9jreack+RJ0/d+KUZOU= -cloud.google.com/go/iam v1.1.3/go.mod h1:3khUlaBXfPKKe7huYgEpDn6FtgRyMEqbkvBxrQyY5SE= cloud.google.com/go/iam v1.1.6 h1:bEa06k05IO4f4uJonbB5iAgKTPpABy1ayxaIZV/GHVc= cloud.google.com/go/iam v1.1.6/go.mod h1:O0zxdPeGBoFdWW3HWmBxJsk0pfvNM/p/qa82rWOGTwI= -cloud.google.com/go/iap v1.4.0/go.mod h1:RGFwRJdihTINIe4wZ2iCP0zF/qu18ZwyKxrhMhygBEc= -cloud.google.com/go/iap v1.5.0/go.mod h1:UH/CGgKd4KyohZL5Pt0jSKE4m3FR51qg6FKQ/z/Ix9A= -cloud.google.com/go/iap v1.6.0/go.mod h1:NSuvI9C/j7UdjGjIde7t7HBz+QTwBcapPE07+sSRcLk= -cloud.google.com/go/iap v1.7.0/go.mod h1:beqQx56T9O1G1yNPph+spKpNibDlYIiIixiqsQXxLIo= -cloud.google.com/go/iap v1.7.1/go.mod h1:WapEwPc7ZxGt2jFGB/C/bm+hP0Y6NXzOYGjpPnmMS74= -cloud.google.com/go/iap v1.8.1/go.mod h1:sJCbeqg3mvWLqjZNsI6dfAtbbV1DL2Rl7e1mTyXYREQ= -cloud.google.com/go/iap v1.9.0/go.mod h1:01OFxd1R+NFrg78S+hoPV5PxEzv22HXaNqUUlmNHFuY= -cloud.google.com/go/iap v1.9.1/go.mod h1:SIAkY7cGMLohLSdBR25BuIxO+I4fXJiL06IBL7cy/5Q= -cloud.google.com/go/ids v1.1.0/go.mod h1:WIuwCaYVOzHIj2OhN9HAwvW+DBdmUAdcWlFxRl+KubM= -cloud.google.com/go/ids v1.2.0/go.mod h1:5WXvp4n25S0rA/mQWAg1YEEBBq6/s+7ml1RDCW1IrcY= -cloud.google.com/go/ids v1.3.0/go.mod h1:JBdTYwANikFKaDP6LtW5JAi4gubs57SVNQjemdt6xV4= -cloud.google.com/go/ids v1.4.1/go.mod h1:np41ed8YMU8zOgv53MMMoCntLTn2lF+SUzlM+O3u/jw= -cloud.google.com/go/ids v1.4.2/go.mod h1:3vw8DX6YddRu9BncxuzMyWn0g8+ooUjI2gslJ7FH3vk= -cloud.google.com/go/iot v1.3.0/go.mod h1:r7RGh2B61+B8oz0AGE+J72AhA0G7tdXItODWsaA2oLs= -cloud.google.com/go/iot v1.4.0/go.mod h1:dIDxPOn0UvNDUMD8Ger7FIaTuvMkj+aGk94RPP0iV+g= -cloud.google.com/go/iot v1.5.0/go.mod h1:mpz5259PDl3XJthEmh9+ap0affn/MqNSP4My77Qql9o= -cloud.google.com/go/iot v1.6.0/go.mod h1:IqdAsmE2cTYYNO1Fvjfzo9po179rAtJeVGUvkLN3rLE= -cloud.google.com/go/iot v1.7.1/go.mod h1:46Mgw7ev1k9KqK1ao0ayW9h0lI+3hxeanz+L1zmbbbk= -cloud.google.com/go/iot v1.7.2/go.mod h1:q+0P5zr1wRFpw7/MOgDXrG/HVA+l+cSwdObffkrpnSg= -cloud.google.com/go/kms v1.4.0/go.mod h1:fajBHndQ+6ubNw6Ss2sSd+SWvjL26RNo/dr7uxsnnOA= -cloud.google.com/go/kms v1.5.0/go.mod h1:QJS2YY0eJGBg3mnDfuaCyLauWwBJiHRboYxJ++1xJNg= -cloud.google.com/go/kms v1.6.0/go.mod h1:Jjy850yySiasBUDi6KFUwUv2n1+o7QZFyuUJg6OgjA0= -cloud.google.com/go/kms v1.8.0/go.mod h1:4xFEhYFqvW+4VMELtZyxomGSYtSQKzM178ylFW4jMAg= -cloud.google.com/go/kms v1.9.0/go.mod h1:qb1tPTgfF9RQP8e1wq4cLFErVuTJv7UsSC915J8dh3w= -cloud.google.com/go/kms v1.10.0/go.mod h1:ng3KTUtQQU9bPX3+QGLsflZIHlkbn8amFAMY63m8d24= -cloud.google.com/go/kms v1.10.1/go.mod h1:rIWk/TryCkR59GMC3YtHtXeLzd634lBbKenvyySAyYI= -cloud.google.com/go/kms v1.11.0/go.mod h1:hwdiYC0xjnWsKQQCQQmIQnS9asjYVSK6jtXm+zFqXLM= -cloud.google.com/go/kms v1.12.1/go.mod h1:c9J991h5DTl+kg7gi3MYomh12YEENGrf48ee/N/2CDM= -cloud.google.com/go/kms v1.15.0/go.mod h1:c9J991h5DTl+kg7gi3MYomh12YEENGrf48ee/N/2CDM= -cloud.google.com/go/kms v1.15.2/go.mod h1:3hopT4+7ooWRCjc2DxgnpESFxhIraaI2IpAVUEhbT/w= -cloud.google.com/go/kms v1.15.3/go.mod h1:AJdXqHxS2GlPyduM99s9iGqi2nwbviBbhV/hdmt4iOQ= -cloud.google.com/go/kms v1.15.5/go.mod h1:cU2H5jnp6G2TDpUGZyqTCoy1n16fbubHZjmVXSMtwDI= -cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic= -cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI= -cloud.google.com/go/language v1.7.0/go.mod h1:DJ6dYN/W+SQOjF8e1hLQXMF21AkH2w9wiPzPCJa2MIE= -cloud.google.com/go/language v1.8.0/go.mod h1:qYPVHf7SPoNNiCL2Dr0FfEFNil1qi3pQEyygwpgVKB8= -cloud.google.com/go/language v1.9.0/go.mod h1:Ns15WooPM5Ad/5no/0n81yUetis74g3zrbeJBE+ptUY= -cloud.google.com/go/language v1.10.1/go.mod h1:CPp94nsdVNiQEt1CNjF5WkTcisLiHPyIbMhvR8H2AW0= -cloud.google.com/go/language v1.11.0/go.mod h1:uDx+pFDdAKTY8ehpWbiXyQdz8tDSYLJbQcXsCkjYyvQ= -cloud.google.com/go/language v1.11.1/go.mod h1:Xyid9MG9WOX3utvDbpX7j3tXDmmDooMyMDqgUVpH17U= -cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8= -cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08= -cloud.google.com/go/lifesciences v0.8.0/go.mod h1:lFxiEOMqII6XggGbOnKiyZ7IBwoIqA84ClvoezaA/bo= -cloud.google.com/go/lifesciences v0.9.1/go.mod h1:hACAOd1fFbCGLr/+weUKRAJas82Y4vrL3O5326N//Wc= -cloud.google.com/go/lifesciences v0.9.2/go.mod h1:QHEOO4tDzcSAzeJg7s2qwnLM2ji8IRpQl4p6m5Z9yTA= -cloud.google.com/go/logging v1.6.1/go.mod h1:5ZO0mHHbvm8gEmeEUHrmDlTDSu5imF6MUP9OfilNXBw= -cloud.google.com/go/logging v1.7.0/go.mod h1:3xjP2CjkM3ZkO73aj4ASA5wRPGGCRrPIAeNqVNkzY8M= -cloud.google.com/go/logging v1.8.1/go.mod h1:TJjR+SimHwuC8MZ9cjByQulAMgni+RkXeI3wwctHJEI= -cloud.google.com/go/longrunning v0.1.1/go.mod h1:UUFxuDWkv22EuY93jjmDMFT5GPQKeFVJBIF6QlTqdsE= -cloud.google.com/go/longrunning v0.3.0/go.mod h1:qth9Y41RRSUE69rDcOn6DdK3HfQfsUI0YSmW3iIlLJc= -cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo= -cloud.google.com/go/longrunning v0.4.2/go.mod h1:OHrnaYyLUV6oqwh0xiS7e5sLQhP1m0QU9R+WhGDMgIQ= -cloud.google.com/go/longrunning v0.5.0/go.mod h1:0JNuqRShmscVAhIACGtskSAWtqtOoPkwP0YF1oVEchc= -cloud.google.com/go/longrunning v0.5.1/go.mod h1:spvimkwdz6SPWKEt/XBij79E9fiTkHSQl/fRUUQJYJc= -cloud.google.com/go/longrunning v0.5.2/go.mod h1:nqo6DQbNV2pXhGDbDMoN2bWz68MjZUzqv2YttZiveCs= -cloud.google.com/go/managedidentities v1.3.0/go.mod h1:UzlW3cBOiPrzucO5qWkNkh0w33KFtBJU281hacNvsdE= -cloud.google.com/go/managedidentities v1.4.0/go.mod h1:NWSBYbEMgqmbZsLIyKvxrYbtqOsxY1ZrGM+9RgDqInM= -cloud.google.com/go/managedidentities v1.5.0/go.mod h1:+dWcZ0JlUmpuxpIDfyP5pP5y0bLdRwOS4Lp7gMni/LA= -cloud.google.com/go/managedidentities v1.6.1/go.mod h1:h/irGhTN2SkZ64F43tfGPMbHnypMbu4RB3yl8YcuEak= -cloud.google.com/go/managedidentities v1.6.2/go.mod h1:5c2VG66eCa0WIq6IylRk3TBW83l161zkFvCj28X7jn8= -cloud.google.com/go/maps v0.1.0/go.mod h1:BQM97WGyfw9FWEmQMpZ5T6cpovXXSd1cGmFma94eubI= -cloud.google.com/go/maps v0.6.0/go.mod h1:o6DAMMfb+aINHz/p/jbcY+mYeXBoZoxTfdSQ8VAJaCw= -cloud.google.com/go/maps v0.7.0/go.mod h1:3GnvVl3cqeSvgMcpRlQidXsPYuDGQ8naBis7MVzpXsY= -cloud.google.com/go/maps v1.3.0/go.mod h1:6mWTUv+WhnOwAgjVsSW2QPPECmW+s3PcRyOa9vgG/5s= -cloud.google.com/go/maps v1.4.0/go.mod h1:6mWTUv+WhnOwAgjVsSW2QPPECmW+s3PcRyOa9vgG/5s= -cloud.google.com/go/maps v1.4.1/go.mod h1:BxSa0BnW1g2U2gNdbq5zikLlHUuHW0GFWh7sgML2kIY= -cloud.google.com/go/mediatranslation v0.5.0/go.mod h1:jGPUhGTybqsPQn91pNXw0xVHfuJ3leR1wj37oU3y1f4= -cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w= -cloud.google.com/go/mediatranslation v0.7.0/go.mod h1:LCnB/gZr90ONOIQLgSXagp8XUW1ODs2UmUMvcgMfI2I= -cloud.google.com/go/mediatranslation v0.8.1/go.mod h1:L/7hBdEYbYHQJhX2sldtTO5SZZ1C1vkapubj0T2aGig= -cloud.google.com/go/mediatranslation v0.8.2/go.mod h1:c9pUaDRLkgHRx3irYE5ZC8tfXGrMYwNZdmDqKMSfFp8= -cloud.google.com/go/memcache v1.4.0/go.mod h1:rTOfiGZtJX1AaFUrOgsMHX5kAzaTQ8azHiuDoTPzNsE= -cloud.google.com/go/memcache v1.5.0/go.mod h1:dk3fCK7dVo0cUU2c36jKb4VqKPS22BTkf81Xq617aWM= -cloud.google.com/go/memcache v1.6.0/go.mod h1:XS5xB0eQZdHtTuTF9Hf8eJkKtR3pVRCcvJwtm68T3rA= -cloud.google.com/go/memcache v1.7.0/go.mod h1:ywMKfjWhNtkQTxrWxCkCFkoPjLHPW6A7WOTVI8xy3LY= -cloud.google.com/go/memcache v1.9.0/go.mod h1:8oEyzXCu+zo9RzlEaEjHl4KkgjlNDaXbCQeQWlzNFJM= -cloud.google.com/go/memcache v1.10.1/go.mod h1:47YRQIarv4I3QS5+hoETgKO40InqzLP6kpNLvyXuyaA= -cloud.google.com/go/memcache v1.10.2/go.mod h1:f9ZzJHLBrmd4BkguIAa/l/Vle6uTHzHokdnzSWOdQ6A= -cloud.google.com/go/metastore v1.5.0/go.mod h1:2ZNrDcQwghfdtCwJ33nM0+GrBGlVuh8rakL3vdPY3XY= -cloud.google.com/go/metastore v1.6.0/go.mod h1:6cyQTls8CWXzk45G55x57DVQ9gWg7RiH65+YgPsNh9s= -cloud.google.com/go/metastore v1.7.0/go.mod h1:s45D0B4IlsINu87/AsWiEVYbLaIMeUSoxlKKDqBGFS8= -cloud.google.com/go/metastore v1.8.0/go.mod h1:zHiMc4ZUpBiM7twCIFQmJ9JMEkDSyZS9U12uf7wHqSI= -cloud.google.com/go/metastore v1.10.0/go.mod h1:fPEnH3g4JJAk+gMRnrAnoqyv2lpUCqJPWOodSaf45Eo= -cloud.google.com/go/metastore v1.11.1/go.mod h1:uZuSo80U3Wd4zi6C22ZZliOUJ3XeM/MlYi/z5OAOWRA= -cloud.google.com/go/metastore v1.12.0/go.mod h1:uZuSo80U3Wd4zi6C22ZZliOUJ3XeM/MlYi/z5OAOWRA= -cloud.google.com/go/metastore v1.13.0/go.mod h1:URDhpG6XLeh5K+Glq0NOt74OfrPKTwS62gEPZzb5SOk= -cloud.google.com/go/metastore v1.13.1/go.mod h1:IbF62JLxuZmhItCppcIfzBBfUFq0DIB9HPDoLgWrVOU= -cloud.google.com/go/monitoring v1.7.0/go.mod h1:HpYse6kkGo//7p6sT0wsIC6IBDET0RhIsnmlA53dvEk= -cloud.google.com/go/monitoring v1.8.0/go.mod h1:E7PtoMJ1kQXWxPjB6mv2fhC5/15jInuulFdYYtlcvT4= -cloud.google.com/go/monitoring v1.12.0/go.mod h1:yx8Jj2fZNEkL/GYZyTLS4ZtZEZN8WtDEiEqG4kLK50w= -cloud.google.com/go/monitoring v1.13.0/go.mod h1:k2yMBAB1H9JT/QETjNkgdCGD9bPF712XiLTVr+cBrpw= -cloud.google.com/go/monitoring v1.15.1/go.mod h1:lADlSAlFdbqQuwwpaImhsJXu1QSdd3ojypXrFSMr2rM= -cloud.google.com/go/monitoring v1.16.0/go.mod h1:Ptp15HgAyM1fNICAojDMoNc/wUmn67mLHQfyqbw+poY= -cloud.google.com/go/monitoring v1.16.1/go.mod h1:6HsxddR+3y9j+o/cMJH6q/KJ/CBTvM/38L/1m7bTRJ4= -cloud.google.com/go/networkconnectivity v1.4.0/go.mod h1:nOl7YL8odKyAOtzNX73/M5/mGZgqqMeryi6UPZTk/rA= -cloud.google.com/go/networkconnectivity v1.5.0/go.mod h1:3GzqJx7uhtlM3kln0+x5wyFvuVH1pIBJjhCpjzSt75o= -cloud.google.com/go/networkconnectivity v1.6.0/go.mod h1:OJOoEXW+0LAxHh89nXd64uGG+FbQoeH8DtxCHVOMlaM= -cloud.google.com/go/networkconnectivity v1.7.0/go.mod h1:RMuSbkdbPwNMQjB5HBWD5MpTBnNm39iAVpC3TmsExt8= -cloud.google.com/go/networkconnectivity v1.10.0/go.mod h1:UP4O4sWXJG13AqrTdQCD9TnLGEbtNRqjuaaA7bNjF5E= -cloud.google.com/go/networkconnectivity v1.11.0/go.mod h1:iWmDD4QF16VCDLXUqvyspJjIEtBR/4zq5hwnY2X3scM= -cloud.google.com/go/networkconnectivity v1.12.1/go.mod h1:PelxSWYM7Sh9/guf8CFhi6vIqf19Ir/sbfZRUwXh92E= -cloud.google.com/go/networkconnectivity v1.13.0/go.mod h1:SAnGPes88pl7QRLUen2HmcBSE9AowVAcdug8c0RSBFk= -cloud.google.com/go/networkconnectivity v1.14.0/go.mod h1:SAnGPes88pl7QRLUen2HmcBSE9AowVAcdug8c0RSBFk= -cloud.google.com/go/networkconnectivity v1.14.1/go.mod h1:LyGPXR742uQcDxZ/wv4EI0Vu5N6NKJ77ZYVnDe69Zug= -cloud.google.com/go/networkmanagement v1.4.0/go.mod h1:Q9mdLLRn60AsOrPc8rs8iNV6OHXaGcDdsIQe1ohekq8= -cloud.google.com/go/networkmanagement v1.5.0/go.mod h1:ZnOeZ/evzUdUsnvRt792H0uYEnHQEMaz+REhhzJRcf4= -cloud.google.com/go/networkmanagement v1.6.0/go.mod h1:5pKPqyXjB/sgtvB5xqOemumoQNB7y95Q7S+4rjSOPYY= -cloud.google.com/go/networkmanagement v1.8.0/go.mod h1:Ho/BUGmtyEqrttTgWEe7m+8vDdK74ibQc+Be0q7Fof0= -cloud.google.com/go/networkmanagement v1.9.0/go.mod h1:UTUaEU9YwbCAhhz3jEOHr+2/K/MrBk2XxOLS89LQzFw= -cloud.google.com/go/networkmanagement v1.9.1/go.mod h1:CCSYgrQQvW73EJawO2QamemYcOb57LvrDdDU51F0mcI= -cloud.google.com/go/networksecurity v0.5.0/go.mod h1:xS6fOCoqpVC5zx15Z/MqkfDwH4+m/61A3ODiDV1xmiQ= -cloud.google.com/go/networksecurity v0.6.0/go.mod h1:Q5fjhTr9WMI5mbpRYEbiexTzROf7ZbDzvzCrNl14nyU= -cloud.google.com/go/networksecurity v0.7.0/go.mod h1:mAnzoxx/8TBSyXEeESMy9OOYwo1v+gZ5eMRnsT5bC8k= -cloud.google.com/go/networksecurity v0.8.0/go.mod h1:B78DkqsxFG5zRSVuwYFRZ9Xz8IcQ5iECsNrPn74hKHU= -cloud.google.com/go/networksecurity v0.9.1/go.mod h1:MCMdxOKQ30wsBI1eI659f9kEp4wuuAueoC9AJKSPWZQ= -cloud.google.com/go/networksecurity v0.9.2/go.mod h1:jG0SeAttWzPMUILEHDUvFYdQTl8L/E/KC8iZDj85lEI= -cloud.google.com/go/notebooks v1.2.0/go.mod h1:9+wtppMfVPUeJ8fIWPOq1UnATHISkGXGqTkxeieQ6UY= -cloud.google.com/go/notebooks v1.3.0/go.mod h1:bFR5lj07DtCPC7YAAJ//vHskFBxA5JzYlH68kXVdk34= -cloud.google.com/go/notebooks v1.4.0/go.mod h1:4QPMngcwmgb6uw7Po99B2xv5ufVoIQ7nOGDyL4P8AgA= -cloud.google.com/go/notebooks v1.5.0/go.mod h1:q8mwhnP9aR8Hpfnrc5iN5IBhrXUy8S2vuYs+kBJ/gu0= -cloud.google.com/go/notebooks v1.7.0/go.mod h1:PVlaDGfJgj1fl1S3dUwhFMXFgfYGhYQt2164xOMONmE= -cloud.google.com/go/notebooks v1.8.0/go.mod h1:Lq6dYKOYOWUCTvw5t2q1gp1lAp0zxAxRycayS0iJcqQ= -cloud.google.com/go/notebooks v1.9.1/go.mod h1:zqG9/gk05JrzgBt4ghLzEepPHNwE5jgPcHZRKhlC1A8= -cloud.google.com/go/notebooks v1.10.0/go.mod h1:SOPYMZnttHxqot0SGSFSkRrwE29eqnKPBJFqgWmiK2k= -cloud.google.com/go/notebooks v1.10.1/go.mod h1:5PdJc2SgAybE76kFQCWrTfJolCOUQXF97e+gteUUA6A= -cloud.google.com/go/optimization v1.1.0/go.mod h1:5po+wfvX5AQlPznyVEZjGJTMr4+CAkJf2XSTQOOl9l4= -cloud.google.com/go/optimization v1.2.0/go.mod h1:Lr7SOHdRDENsh+WXVmQhQTrzdu9ybg0NecjHidBq6xs= -cloud.google.com/go/optimization v1.3.1/go.mod h1:IvUSefKiwd1a5p0RgHDbWCIbDFgKuEdB+fPPuP0IDLI= -cloud.google.com/go/optimization v1.4.1/go.mod h1:j64vZQP7h9bO49m2rVaTVoNM0vEBEN5eKPUPbZyXOrk= -cloud.google.com/go/optimization v1.5.0/go.mod h1:evo1OvTxeBRBu6ydPlrIRizKY/LJKo/drDMMRKqGEUU= -cloud.google.com/go/optimization v1.5.1/go.mod h1:NC0gnUD5MWVAF7XLdoYVPmYYVth93Q6BUzqAq3ZwtV8= -cloud.google.com/go/orchestration v1.3.0/go.mod h1:Sj5tq/JpWiB//X/q3Ngwdl5K7B7Y0KZ7bfv0wL6fqVA= -cloud.google.com/go/orchestration v1.4.0/go.mod h1:6W5NLFWs2TlniBphAViZEVhrXRSMgUGDfW7vrWKvsBk= -cloud.google.com/go/orchestration v1.6.0/go.mod h1:M62Bevp7pkxStDfFfTuCOaXgaaqRAga1yKyoMtEoWPQ= -cloud.google.com/go/orchestration v1.8.1/go.mod h1:4sluRF3wgbYVRqz7zJ1/EUNc90TTprliq9477fGobD8= -cloud.google.com/go/orchestration v1.8.2/go.mod h1:T1cP+6WyTmh6LSZzeUhvGf0uZVmJyTx7t8z7Vg87+A0= -cloud.google.com/go/orgpolicy v1.4.0/go.mod h1:xrSLIV4RePWmP9P3tBl8S93lTmlAxjm06NSm2UTmKvE= -cloud.google.com/go/orgpolicy v1.5.0/go.mod h1:hZEc5q3wzwXJaKrsx5+Ewg0u1LxJ51nNFlext7Tanwc= -cloud.google.com/go/orgpolicy v1.10.0/go.mod h1:w1fo8b7rRqlXlIJbVhOMPrwVljyuW5mqssvBtU18ONc= -cloud.google.com/go/orgpolicy v1.11.0/go.mod h1:2RK748+FtVvnfuynxBzdnyu7sygtoZa1za/0ZfpOs1M= -cloud.google.com/go/orgpolicy v1.11.1/go.mod h1:8+E3jQcpZJQliP+zaFfayC2Pg5bmhuLK755wKhIIUCE= -cloud.google.com/go/orgpolicy v1.11.2/go.mod h1:biRDpNwfyytYnmCRWZWxrKF22Nkz9eNVj9zyaBdpm1o= -cloud.google.com/go/osconfig v1.7.0/go.mod h1:oVHeCeZELfJP7XLxcBGTMBvRO+1nQ5tFG9VQTmYS2Fs= -cloud.google.com/go/osconfig v1.8.0/go.mod h1:EQqZLu5w5XA7eKizepumcvWx+m8mJUhEwiPqWiZeEdg= -cloud.google.com/go/osconfig v1.9.0/go.mod h1:Yx+IeIZJ3bdWmzbQU4fxNl8xsZ4amB+dygAwFPlvnNo= -cloud.google.com/go/osconfig v1.10.0/go.mod h1:uMhCzqC5I8zfD9zDEAfvgVhDS8oIjySWh+l4WK6GnWw= -cloud.google.com/go/osconfig v1.11.0/go.mod h1:aDICxrur2ogRd9zY5ytBLV89KEgT2MKB2L/n6x1ooPw= -cloud.google.com/go/osconfig v1.12.0/go.mod h1:8f/PaYzoS3JMVfdfTubkowZYGmAhUCjjwnjqWI7NVBc= -cloud.google.com/go/osconfig v1.12.1/go.mod h1:4CjBxND0gswz2gfYRCUoUzCm9zCABp91EeTtWXyz0tE= -cloud.google.com/go/osconfig v1.12.2/go.mod h1:eh9GPaMZpI6mEJEuhEjUJmaxvQ3gav+fFEJon1Y8Iw0= -cloud.google.com/go/oslogin v1.4.0/go.mod h1:YdgMXWRaElXz/lDk1Na6Fh5orF7gvmJ0FGLIs9LId4E= -cloud.google.com/go/oslogin v1.5.0/go.mod h1:D260Qj11W2qx/HVF29zBg+0fd6YCSjSqLUkY/qEenQU= -cloud.google.com/go/oslogin v1.6.0/go.mod h1:zOJ1O3+dTU8WPlGEkFSh7qeHPPSoxrcMbbK1Nm2iX70= -cloud.google.com/go/oslogin v1.7.0/go.mod h1:e04SN0xO1UNJ1M5GP0vzVBFicIe4O53FOfcixIqTyXo= -cloud.google.com/go/oslogin v1.9.0/go.mod h1:HNavntnH8nzrn8JCTT5fj18FuJLFJc4NaZJtBnQtKFs= -cloud.google.com/go/oslogin v1.10.1/go.mod h1:x692z7yAue5nE7CsSnoG0aaMbNoRJRXO4sn73R+ZqAs= -cloud.google.com/go/oslogin v1.11.0/go.mod h1:8GMTJs4X2nOAUVJiPGqIWVcDaF0eniEto3xlOxaboXE= -cloud.google.com/go/oslogin v1.11.1/go.mod h1:OhD2icArCVNUxKqtK0mcSmKL7lgr0LVlQz+v9s1ujTg= -cloud.google.com/go/phishingprotection v0.5.0/go.mod h1:Y3HZknsK9bc9dMi+oE8Bim0lczMU6hrX0UpADuMefr0= -cloud.google.com/go/phishingprotection v0.6.0/go.mod h1:9Y3LBLgy0kDTcYET8ZH3bq/7qni15yVUoAxiFxnlSUA= -cloud.google.com/go/phishingprotection v0.7.0/go.mod h1:8qJI4QKHoda/sb/7/YmMQ2omRLSLYSu9bU0EKCNI+Lk= -cloud.google.com/go/phishingprotection v0.8.1/go.mod h1:AxonW7GovcA8qdEk13NfHq9hNx5KPtfxXNeUxTDxB6I= -cloud.google.com/go/phishingprotection v0.8.2/go.mod h1:LhJ91uyVHEYKSKcMGhOa14zMMWfbEdxG032oT6ECbC8= -cloud.google.com/go/policytroubleshooter v1.3.0/go.mod h1:qy0+VwANja+kKrjlQuOzmlvscn4RNsAc0e15GGqfMxg= -cloud.google.com/go/policytroubleshooter v1.4.0/go.mod h1:DZT4BcRw3QoO8ota9xw/LKtPa8lKeCByYeKTIf/vxdE= -cloud.google.com/go/policytroubleshooter v1.5.0/go.mod h1:Rz1WfV+1oIpPdN2VvvuboLVRsB1Hclg3CKQ53j9l8vw= -cloud.google.com/go/policytroubleshooter v1.6.0/go.mod h1:zYqaPTsmfvpjm5ULxAyD/lINQxJ0DDsnWOP/GZ7xzBc= -cloud.google.com/go/policytroubleshooter v1.7.1/go.mod h1:0NaT5v3Ag1M7U5r0GfDCpUFkWd9YqpubBWsQlhanRv0= -cloud.google.com/go/policytroubleshooter v1.8.0/go.mod h1:tmn5Ir5EToWe384EuboTcVQT7nTag2+DuH3uHmKd1HU= -cloud.google.com/go/policytroubleshooter v1.9.0/go.mod h1:+E2Lga7TycpeSTj2FsH4oXxTnrbHJGRlKhVZBLGgU64= -cloud.google.com/go/policytroubleshooter v1.9.1/go.mod h1:MYI8i0bCrL8cW+VHN1PoiBTyNZTstCg2WUw2eVC4c4U= -cloud.google.com/go/privatecatalog v0.5.0/go.mod h1:XgosMUvvPyxDjAVNDYxJ7wBW8//hLDDYmnsNcMGq1K0= -cloud.google.com/go/privatecatalog v0.6.0/go.mod h1:i/fbkZR0hLN29eEWiiwue8Pb+GforiEIBnV9yrRUOKI= -cloud.google.com/go/privatecatalog v0.7.0/go.mod h1:2s5ssIFO69F5csTXcwBP7NPFTZvps26xGzvQ2PQaBYg= -cloud.google.com/go/privatecatalog v0.8.0/go.mod h1:nQ6pfaegeDAq/Q5lrfCQzQLhubPiZhSaNhIgfJlnIXs= -cloud.google.com/go/privatecatalog v0.9.1/go.mod h1:0XlDXW2unJXdf9zFz968Hp35gl/bhF4twwpXZAW50JA= -cloud.google.com/go/privatecatalog v0.9.2/go.mod h1:RMA4ATa8IXfzvjrhhK8J6H4wwcztab+oZph3c6WmtFc= cloud.google.com/go/profiler v0.3.0 h1:R6y/xAeifaUXxd2x6w+jIwKxoKl8Cv5HJvcvASTPWJo= cloud.google.com/go/profiler v0.3.0/go.mod h1:9wYk9eY4iZHsev8TQb61kh3wiOiSyz/xOYixWPzweCU= 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/pubsub v1.26.0/go.mod h1:QgBH3U/jdJy/ftjPhTkyXNj543Tin1pRYcdcPRnFIRI= -cloud.google.com/go/pubsub v1.27.1/go.mod h1:hQN39ymbV9geqBnfQq6Xf63yNhUAhv9CZhzp5O6qsW0= -cloud.google.com/go/pubsub v1.28.0/go.mod h1:vuXFpwaVoIPQMGXqRyUQigu/AX1S3IWugR9xznmcXX8= -cloud.google.com/go/pubsub v1.30.0/go.mod h1:qWi1OPS0B+b5L+Sg6Gmc9zD1Y+HaM0MdUr7LsupY1P4= -cloud.google.com/go/pubsub v1.32.0/go.mod h1:f+w71I33OMyxf9VpMVcZbnG5KSUkCOUHYpFd5U1GdRc= -cloud.google.com/go/pubsub v1.33.0/go.mod h1:f+w71I33OMyxf9VpMVcZbnG5KSUkCOUHYpFd5U1GdRc= -cloud.google.com/go/pubsublite v1.5.0/go.mod h1:xapqNQ1CuLfGi23Yda/9l4bBCKz/wC3KIJ5gKcxveZg= -cloud.google.com/go/pubsublite v1.6.0/go.mod h1:1eFCS0U11xlOuMFV/0iBqw3zP12kddMeCbj/F3FSj9k= -cloud.google.com/go/pubsublite v1.7.0/go.mod h1:8hVMwRXfDfvGm3fahVbtDbiLePT3gpoiJYJY+vxWxVM= -cloud.google.com/go/pubsublite v1.8.1/go.mod h1:fOLdU4f5xldK4RGJrBMm+J7zMWNj/k4PxwEZXy39QS0= -cloud.google.com/go/recaptchaenterprise v1.3.1/go.mod h1:OdD+q+y4XGeAlxRaMn1Y7/GveP6zmq76byL6tjPE7d4= -cloud.google.com/go/recaptchaenterprise/v2 v2.1.0/go.mod h1:w9yVqajwroDNTfGuhmOjPDN//rZGySaf6PtFVcSCa7o= -cloud.google.com/go/recaptchaenterprise/v2 v2.2.0/go.mod h1:/Zu5jisWGeERrd5HnlS3EUGb/D335f9k51B/FVil0jk= -cloud.google.com/go/recaptchaenterprise/v2 v2.3.0/go.mod h1:O9LwGCjrhGHBQET5CA7dd5NwwNQUErSgEDit1DLNTdo= -cloud.google.com/go/recaptchaenterprise/v2 v2.4.0/go.mod h1:Am3LHfOuBstrLrNCBrlI5sbwx9LBg3te2N6hGvHn2mE= -cloud.google.com/go/recaptchaenterprise/v2 v2.5.0/go.mod h1:O8LzcHXN3rz0j+LBC91jrwI3R+1ZSZEWrfL7XHgNo9U= -cloud.google.com/go/recaptchaenterprise/v2 v2.6.0/go.mod h1:RPauz9jeLtB3JVzg6nCbe12qNoaa8pXc4d/YukAmcnA= -cloud.google.com/go/recaptchaenterprise/v2 v2.7.0/go.mod h1:19wVj/fs5RtYtynAPJdDTb69oW0vNHYDBTbB4NvMD9c= -cloud.google.com/go/recaptchaenterprise/v2 v2.7.2/go.mod h1:kR0KjsJS7Jt1YSyWFkseQ756D45kaYNTlDPPaRAvDBU= -cloud.google.com/go/recaptchaenterprise/v2 v2.8.0/go.mod h1:QuE8EdU9dEnesG8/kG3XuJyNsjEqMlMzg3v3scCJ46c= -cloud.google.com/go/recaptchaenterprise/v2 v2.8.1/go.mod h1:JZYZJOeZjgSSTGP4uz7NlQ4/d1w5hGmksVgM0lbEij0= -cloud.google.com/go/recommendationengine v0.5.0/go.mod h1:E5756pJcVFeVgaQv3WNpImkFP8a+RptV6dDLGPILjvg= -cloud.google.com/go/recommendationengine v0.6.0/go.mod h1:08mq2umu9oIqc7tDy8sx+MNJdLG0fUi3vaSVbztHgJ4= -cloud.google.com/go/recommendationengine v0.7.0/go.mod h1:1reUcE3GIu6MeBz/h5xZJqNLuuVjNg1lmWMPyjatzac= -cloud.google.com/go/recommendationengine v0.8.1/go.mod h1:MrZihWwtFYWDzE6Hz5nKcNz3gLizXVIDI/o3G1DLcrE= -cloud.google.com/go/recommendationengine v0.8.2/go.mod h1:QIybYHPK58qir9CV2ix/re/M//Ty10OxjnnhWdaKS1Y= -cloud.google.com/go/recommender v1.5.0/go.mod h1:jdoeiBIVrJe9gQjwd759ecLJbxCDED4A6p+mqoqDvTg= -cloud.google.com/go/recommender v1.6.0/go.mod h1:+yETpm25mcoiECKh9DEScGzIRyDKpZ0cEhWGo+8bo+c= -cloud.google.com/go/recommender v1.7.0/go.mod h1:XLHs/W+T8olwlGOgfQenXBTbIseGclClff6lhFVe9Bs= -cloud.google.com/go/recommender v1.8.0/go.mod h1:PkjXrTT05BFKwxaUxQmtIlrtj0kph108r02ZZQ5FE70= -cloud.google.com/go/recommender v1.9.0/go.mod h1:PnSsnZY7q+VL1uax2JWkt/UegHssxjUVVCrX52CuEmQ= -cloud.google.com/go/recommender v1.10.1/go.mod h1:XFvrE4Suqn5Cq0Lf+mCP6oBHD/yRMA8XxP5sb7Q7gpA= -cloud.google.com/go/recommender v1.11.0/go.mod h1:kPiRQhPyTJ9kyXPCG6u/dlPLbYfFlkwHNRwdzPVAoII= -cloud.google.com/go/recommender v1.11.1/go.mod h1:sGwFFAyI57v2Hc5LbIj+lTwXipGu9NW015rkaEM5B18= -cloud.google.com/go/redis v1.7.0/go.mod h1:V3x5Jq1jzUcg+UNsRvdmsfuFnit1cfe3Z/PGyq/lm4Y= -cloud.google.com/go/redis v1.8.0/go.mod h1:Fm2szCDavWzBk2cDKxrkmWBqoCiL1+Ctwq7EyqBCA/A= -cloud.google.com/go/redis v1.9.0/go.mod h1:HMYQuajvb2D0LvMgZmLDZW8V5aOC/WxstZHiy4g8OiA= -cloud.google.com/go/redis v1.10.0/go.mod h1:ThJf3mMBQtW18JzGgh41/Wld6vnDDc/F/F35UolRZPM= -cloud.google.com/go/redis v1.11.0/go.mod h1:/X6eicana+BWcUda5PpwZC48o37SiFVTFSs0fWAJ7uQ= -cloud.google.com/go/redis v1.13.1/go.mod h1:VP7DGLpE91M6bcsDdMuyCm2hIpB6Vp2hI090Mfd1tcg= -cloud.google.com/go/redis v1.13.2/go.mod h1:0Hg7pCMXS9uz02q+LoEVl5dNHUkIQv+C/3L76fandSA= -cloud.google.com/go/resourcemanager v1.3.0/go.mod h1:bAtrTjZQFJkiWTPDb1WBjzvc6/kifjj4QBYuKCCoqKA= -cloud.google.com/go/resourcemanager v1.4.0/go.mod h1:MwxuzkumyTX7/a3n37gmsT3py7LIXwrShilPh3P1tR0= -cloud.google.com/go/resourcemanager v1.5.0/go.mod h1:eQoXNAiAvCf5PXxWxXjhKQoTMaUSNrEfg+6qdf/wots= -cloud.google.com/go/resourcemanager v1.6.0/go.mod h1:YcpXGRs8fDzcUl1Xw8uOVmI8JEadvhRIkoXXUNVYcVo= -cloud.google.com/go/resourcemanager v1.7.0/go.mod h1:HlD3m6+bwhzj9XCouqmeiGuni95NTrExfhoSrkC/3EI= -cloud.google.com/go/resourcemanager v1.9.1/go.mod h1:dVCuosgrh1tINZ/RwBufr8lULmWGOkPS8gL5gqyjdT8= -cloud.google.com/go/resourcemanager v1.9.2/go.mod h1:OujkBg1UZg5lX2yIyMo5Vz9O5hf7XQOSV7WxqxxMtQE= -cloud.google.com/go/resourcesettings v1.3.0/go.mod h1:lzew8VfESA5DQ8gdlHwMrqZs1S9V87v3oCnKCWoOuQU= -cloud.google.com/go/resourcesettings v1.4.0/go.mod h1:ldiH9IJpcrlC3VSuCGvjR5of/ezRrOxFtpJoJo5SmXg= -cloud.google.com/go/resourcesettings v1.5.0/go.mod h1:+xJF7QSG6undsQDfsCJyqWXyBwUoJLhetkRMDRnIoXA= -cloud.google.com/go/resourcesettings v1.6.1/go.mod h1:M7mk9PIZrC5Fgsu1kZJci6mpgN8o0IUzVx3eJU3y4Jw= -cloud.google.com/go/resourcesettings v1.6.2/go.mod h1:mJIEDd9MobzunWMeniaMp6tzg4I2GvD3TTmPkc8vBXk= -cloud.google.com/go/retail v1.8.0/go.mod h1:QblKS8waDmNUhghY2TI9O3JLlFk8jybHeV4BF19FrE4= -cloud.google.com/go/retail v1.9.0/go.mod h1:g6jb6mKuCS1QKnH/dpu7isX253absFl6iE92nHwlBUY= -cloud.google.com/go/retail v1.10.0/go.mod h1:2gDk9HsL4HMS4oZwz6daui2/jmKvqShXKQuB2RZ+cCc= -cloud.google.com/go/retail v1.11.0/go.mod h1:MBLk1NaWPmh6iVFSz9MeKG/Psyd7TAgm6y/9L2B4x9Y= -cloud.google.com/go/retail v1.12.0/go.mod h1:UMkelN/0Z8XvKymXFbD4EhFJlYKRx1FGhQkVPU5kF14= -cloud.google.com/go/retail v1.14.1/go.mod h1:y3Wv3Vr2k54dLNIrCzenyKG8g8dhvhncT2NcNjb/6gE= -cloud.google.com/go/retail v1.14.2/go.mod h1:W7rrNRChAEChX336QF7bnMxbsjugcOCPU44i5kbLiL8= -cloud.google.com/go/run v0.2.0/go.mod h1:CNtKsTA1sDcnqqIFR3Pb5Tq0usWxJJvsWOCPldRU3Do= -cloud.google.com/go/run v0.3.0/go.mod h1:TuyY1+taHxTjrD0ZFk2iAR+xyOXEA0ztb7U3UNA0zBo= -cloud.google.com/go/run v0.8.0/go.mod h1:VniEnuBwqjigv0A7ONfQUaEItaiCRVujlMqerPPiktM= -cloud.google.com/go/run v0.9.0/go.mod h1:Wwu+/vvg8Y+JUApMwEDfVfhetv30hCG4ZwDR/IXl2Qg= -cloud.google.com/go/run v1.2.0/go.mod h1:36V1IlDzQ0XxbQjUx6IYbw8H3TJnWvhii963WW3B/bo= -cloud.google.com/go/run v1.3.0/go.mod h1:S/osX/4jIPZGg+ssuqh6GNgg7syixKe3YnprwehzHKU= -cloud.google.com/go/run v1.3.1/go.mod h1:cymddtZOzdwLIAsmS6s+Asl4JoXIDm/K1cpZTxV4Q5s= -cloud.google.com/go/scheduler v1.4.0/go.mod h1:drcJBmxF3aqZJRhmkHQ9b3uSSpQoltBPGPxGAWROx6s= -cloud.google.com/go/scheduler v1.5.0/go.mod h1:ri073ym49NW3AfT6DZi21vLZrG07GXr5p3H1KxN5QlI= -cloud.google.com/go/scheduler v1.6.0/go.mod h1:SgeKVM7MIwPn3BqtcBntpLyrIJftQISRrYB5ZtT+KOk= -cloud.google.com/go/scheduler v1.7.0/go.mod h1:jyCiBqWW956uBjjPMMuX09n3x37mtyPJegEWKxRsn44= -cloud.google.com/go/scheduler v1.8.0/go.mod h1:TCET+Y5Gp1YgHT8py4nlg2Sew8nUHMqcpousDgXJVQc= -cloud.google.com/go/scheduler v1.9.0/go.mod h1:yexg5t+KSmqu+njTIh3b7oYPheFtBWGcbVUYF1GGMIc= -cloud.google.com/go/scheduler v1.10.1/go.mod h1:R63Ldltd47Bs4gnhQkmNDse5w8gBRrhObZ54PxgR2Oo= -cloud.google.com/go/scheduler v1.10.2/go.mod h1:O3jX6HRH5eKCA3FutMw375XHZJudNIKVonSCHv7ropY= -cloud.google.com/go/secretmanager v1.6.0/go.mod h1:awVa/OXF6IiyaU1wQ34inzQNc4ISIDIrId8qE5QGgKA= -cloud.google.com/go/secretmanager v1.8.0/go.mod h1:hnVgi/bN5MYHd3Gt0SPuTPPp5ENina1/LxM+2W9U9J4= -cloud.google.com/go/secretmanager v1.9.0/go.mod h1:b71qH2l1yHmWQHt9LC80akm86mX8AL6X1MA01dW8ht4= -cloud.google.com/go/secretmanager v1.10.0/go.mod h1:MfnrdvKMPNra9aZtQFvBcvRU54hbPD8/HayQdlUgJpU= -cloud.google.com/go/secretmanager v1.11.1/go.mod h1:znq9JlXgTNdBeQk9TBW/FnR/W4uChEKGeqQWAJ8SXFw= -cloud.google.com/go/secretmanager v1.11.2/go.mod h1:MQm4t3deoSub7+WNwiC4/tRYgDBHJgJPvswqQVB1Vss= -cloud.google.com/go/security v1.5.0/go.mod h1:lgxGdyOKKjHL4YG3/YwIL2zLqMFCKs0UbQwgyZmfJl4= -cloud.google.com/go/security v1.7.0/go.mod h1:mZklORHl6Bg7CNnnjLH//0UlAlaXqiG7Lb9PsPXLfD0= -cloud.google.com/go/security v1.8.0/go.mod h1:hAQOwgmaHhztFhiQ41CjDODdWP0+AE1B3sX4OFlq+GU= -cloud.google.com/go/security v1.9.0/go.mod h1:6Ta1bO8LXI89nZnmnsZGp9lVoVWXqsVbIq/t9dzI+2Q= -cloud.google.com/go/security v1.10.0/go.mod h1:QtOMZByJVlibUT2h9afNDWRZ1G96gVywH8T5GUSb9IA= -cloud.google.com/go/security v1.12.0/go.mod h1:rV6EhrpbNHrrxqlvW0BWAIawFWq3X90SduMJdFwtLB8= -cloud.google.com/go/security v1.13.0/go.mod h1:Q1Nvxl1PAgmeW0y3HTt54JYIvUdtcpYKVfIB8AOMZ+0= -cloud.google.com/go/security v1.15.1/go.mod h1:MvTnnbsWnehoizHi09zoiZob0iCHVcL4AUBj76h9fXA= -cloud.google.com/go/security v1.15.2/go.mod h1:2GVE/v1oixIRHDaClVbHuPcZwAqFM28mXuAKCfMgYIg= -cloud.google.com/go/securitycenter v1.13.0/go.mod h1:cv5qNAqjY84FCN6Y9z28WlkKXyWsgLO832YiWwkCWcU= -cloud.google.com/go/securitycenter v1.14.0/go.mod h1:gZLAhtyKv85n52XYWt6RmeBdydyxfPeTrpToDPw4Auc= -cloud.google.com/go/securitycenter v1.15.0/go.mod h1:PeKJ0t8MoFmmXLXWm41JidyzI3PJjd8sXWaVqg43WWk= -cloud.google.com/go/securitycenter v1.16.0/go.mod h1:Q9GMaLQFUD+5ZTabrbujNWLtSLZIZF7SAR0wWECrjdk= -cloud.google.com/go/securitycenter v1.18.1/go.mod h1:0/25gAzCM/9OL9vVx4ChPeM/+DlfGQJDwBy/UC8AKK0= -cloud.google.com/go/securitycenter v1.19.0/go.mod h1:LVLmSg8ZkkyaNy4u7HCIshAngSQ8EcIRREP3xBnyfag= -cloud.google.com/go/securitycenter v1.23.0/go.mod h1:8pwQ4n+Y9WCWM278R8W3nF65QtY172h4S8aXyI9/hsQ= -cloud.google.com/go/securitycenter v1.23.1/go.mod h1:w2HV3Mv/yKhbXKwOCu2i8bCuLtNP1IMHuiYQn4HJq5s= -cloud.google.com/go/servicecontrol v1.4.0/go.mod h1:o0hUSJ1TXJAmi/7fLJAedOovnujSEvjKCAFNXPQ1RaU= -cloud.google.com/go/servicecontrol v1.5.0/go.mod h1:qM0CnXHhyqKVuiZnGKrIurvVImCs8gmqWsDoqe9sU1s= -cloud.google.com/go/servicecontrol v1.10.0/go.mod h1:pQvyvSRh7YzUF2efw7H87V92mxU8FnFDawMClGCNuAA= -cloud.google.com/go/servicecontrol v1.11.0/go.mod h1:kFmTzYzTUIuZs0ycVqRHNaNhgR+UMUpw9n02l/pY+mc= -cloud.google.com/go/servicecontrol v1.11.1/go.mod h1:aSnNNlwEFBY+PWGQ2DoM0JJ/QUXqV5/ZD9DOLB7SnUk= -cloud.google.com/go/servicedirectory v1.4.0/go.mod h1:gH1MUaZCgtP7qQiI+F+A+OpeKF/HQWgtAddhTbhL2bs= -cloud.google.com/go/servicedirectory v1.5.0/go.mod h1:QMKFL0NUySbpZJ1UZs3oFAmdvVxhhxB6eJ/Vlp73dfg= -cloud.google.com/go/servicedirectory v1.6.0/go.mod h1:pUlbnWsLH9c13yGkxCmfumWEPjsRs1RlmJ4pqiNjVL4= -cloud.google.com/go/servicedirectory v1.7.0/go.mod h1:5p/U5oyvgYGYejufvxhgwjL8UVXjkuw7q5XcG10wx1U= -cloud.google.com/go/servicedirectory v1.8.0/go.mod h1:srXodfhY1GFIPvltunswqXpVxFPpZjf8nkKQT7XcXaY= -cloud.google.com/go/servicedirectory v1.9.0/go.mod h1:29je5JjiygNYlmsGz8k6o+OZ8vd4f//bQLtvzkPPT/s= -cloud.google.com/go/servicedirectory v1.10.1/go.mod h1:Xv0YVH8s4pVOwfM/1eMTl0XJ6bzIOSLDt8f8eLaGOxQ= -cloud.google.com/go/servicedirectory v1.11.0/go.mod h1:Xv0YVH8s4pVOwfM/1eMTl0XJ6bzIOSLDt8f8eLaGOxQ= -cloud.google.com/go/servicedirectory v1.11.1/go.mod h1:tJywXimEWzNzw9FvtNjsQxxJ3/41jseeILgwU/QLrGI= -cloud.google.com/go/servicemanagement v1.4.0/go.mod h1:d8t8MDbezI7Z2R1O/wu8oTggo3BI2GKYbdG4y/SJTco= -cloud.google.com/go/servicemanagement v1.5.0/go.mod h1:XGaCRe57kfqu4+lRxaFEAuqmjzF0r+gWHjWqKqBvKFo= -cloud.google.com/go/servicemanagement v1.6.0/go.mod h1:aWns7EeeCOtGEX4OvZUWCCJONRZeFKiptqKf1D0l/Jc= -cloud.google.com/go/servicemanagement v1.8.0/go.mod h1:MSS2TDlIEQD/fzsSGfCdJItQveu9NXnUniTrq/L8LK4= -cloud.google.com/go/serviceusage v1.3.0/go.mod h1:Hya1cozXM4SeSKTAgGXgj97GlqUvF5JaoXacR1JTP/E= -cloud.google.com/go/serviceusage v1.4.0/go.mod h1:SB4yxXSaYVuUBYUml6qklyONXNLt83U0Rb+CXyhjEeU= -cloud.google.com/go/serviceusage v1.5.0/go.mod h1:w8U1JvqUqwJNPEOTQjrMHkw3IaIFLoLsPLvsE3xueec= -cloud.google.com/go/serviceusage v1.6.0/go.mod h1:R5wwQcbOWsyuOfbP9tGdAnCAc6B9DRwPG1xtWMDeuPA= -cloud.google.com/go/shell v1.3.0/go.mod h1:VZ9HmRjZBsjLGXusm7K5Q5lzzByZmJHf1d0IWHEN5X4= -cloud.google.com/go/shell v1.4.0/go.mod h1:HDxPzZf3GkDdhExzD/gs8Grqk+dmYcEjGShZgYa9URw= -cloud.google.com/go/shell v1.6.0/go.mod h1:oHO8QACS90luWgxP3N9iZVuEiSF84zNyLytb+qE2f9A= -cloud.google.com/go/shell v1.7.1/go.mod h1:u1RaM+huXFaTojTbW4g9P5emOrrmLE69KrxqQahKn4g= -cloud.google.com/go/shell v1.7.2/go.mod h1:KqRPKwBV0UyLickMn0+BY1qIyE98kKyI216sH/TuHmc= -cloud.google.com/go/spanner v1.41.0/go.mod h1:MLYDBJR/dY4Wt7ZaMIQ7rXOTLjYrmxLE/5ve9vFfWos= -cloud.google.com/go/spanner v1.44.0/go.mod h1:G8XIgYdOK+Fbcpbs7p2fiprDw4CaZX63whnSMLVBxjk= -cloud.google.com/go/spanner v1.45.0/go.mod h1:FIws5LowYz8YAE1J8fOS7DJup8ff7xJeetWEo5REA2M= -cloud.google.com/go/spanner v1.47.0/go.mod h1:IXsJwVW2j4UKs0eYDqodab6HgGuA1bViSqW4uH9lfUI= -cloud.google.com/go/spanner v1.49.0/go.mod h1:eGj9mQGK8+hkgSVbHNQ06pQ4oS+cyc4tXXd6Dif1KoM= -cloud.google.com/go/spanner v1.50.0/go.mod h1:eGj9mQGK8+hkgSVbHNQ06pQ4oS+cyc4tXXd6Dif1KoM= -cloud.google.com/go/speech v1.6.0/go.mod h1:79tcr4FHCimOp56lwC01xnt/WPJZc4v3gzyT7FoBkCM= -cloud.google.com/go/speech v1.7.0/go.mod h1:KptqL+BAQIhMsj1kOP2la5DSEEerPDuOP/2mmkhHhZQ= -cloud.google.com/go/speech v1.8.0/go.mod h1:9bYIl1/tjsAnMgKGHKmBZzXKEkGgtU+MpdDPTE9f7y0= -cloud.google.com/go/speech v1.9.0/go.mod h1:xQ0jTcmnRFFM2RfX/U+rk6FQNUF6DQlydUSyoooSpco= -cloud.google.com/go/speech v1.14.1/go.mod h1:gEosVRPJ9waG7zqqnsHpYTOoAS4KouMRLDFMekpJ0J0= -cloud.google.com/go/speech v1.15.0/go.mod h1:y6oH7GhqCaZANH7+Oe0BhgIogsNInLlz542tg3VqeYI= -cloud.google.com/go/speech v1.17.1/go.mod h1:8rVNzU43tQvxDaGvqOhpDqgkJTFowBpDvCJ14kGlJYo= -cloud.google.com/go/speech v1.19.0/go.mod h1:8rVNzU43tQvxDaGvqOhpDqgkJTFowBpDvCJ14kGlJYo= -cloud.google.com/go/speech v1.19.1/go.mod h1:WcuaWz/3hOlzPFOVo9DUsblMIHwxP589y6ZMtaG+iAA= 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= @@ -823,159 +66,20 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= -cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc= -cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= -cloud.google.com/go/storage v1.28.1/go.mod h1:Qnisd4CqDdo6BGs2AD5LLnEsmSQ80wQ5ogcBBKhU86Y= -cloud.google.com/go/storage v1.29.0/go.mod h1:4puEjyTKnku6gfKoTfNOU/W+a9JyuVNxjpS5GBrB8h4= -cloud.google.com/go/storage v1.30.1/go.mod h1:NfxhC0UJE1aXSx7CIIbCf7y9HKT7BiccwkR7+P7gN8E= cloud.google.com/go/storage v1.36.0 h1:P0mOkAcaJxhCTvAkMhxMfrTKiNcub4YmmPBtlhAyTr8= cloud.google.com/go/storage v1.36.0/go.mod h1:M6M/3V/D3KpzMTJyPOR/HU6n2Si5QdaXYEsng2xgOs8= -cloud.google.com/go/storagetransfer v1.5.0/go.mod h1:dxNzUopWy7RQevYFHewchb29POFv3/AaBgnhqzqiK0w= -cloud.google.com/go/storagetransfer v1.6.0/go.mod h1:y77xm4CQV/ZhFZH75PLEXY0ROiS7Gh6pSKrM8dJyg6I= -cloud.google.com/go/storagetransfer v1.7.0/go.mod h1:8Giuj1QNb1kfLAiWM1bN6dHzfdlDAVC9rv9abHot2W4= -cloud.google.com/go/storagetransfer v1.8.0/go.mod h1:JpegsHHU1eXg7lMHkvf+KE5XDJ7EQu0GwNJbbVGanEw= -cloud.google.com/go/storagetransfer v1.10.0/go.mod h1:DM4sTlSmGiNczmV6iZyceIh2dbs+7z2Ayg6YAiQlYfA= -cloud.google.com/go/storagetransfer v1.10.1/go.mod h1:rS7Sy0BtPviWYTTJVWCSV4QrbBitgPeuK4/FKa4IdLs= -cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw= -cloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g= -cloud.google.com/go/talent v1.3.0/go.mod h1:CmcxwJ/PKfRgd1pBjQgU6W3YBwiewmUzQYH5HHmSCmM= -cloud.google.com/go/talent v1.4.0/go.mod h1:ezFtAgVuRf8jRsvyE6EwmbTK5LKciD4KVnHuDEFmOOA= -cloud.google.com/go/talent v1.5.0/go.mod h1:G+ODMj9bsasAEJkQSzO2uHQWXHHXUomArjWQQYkqK6c= -cloud.google.com/go/talent v1.6.2/go.mod h1:CbGvmKCG61mkdjcqTcLOkb2ZN1SrQI8MDyma2l7VD24= -cloud.google.com/go/talent v1.6.3/go.mod h1:xoDO97Qd4AK43rGjJvyBHMskiEf3KulgYzcH6YWOVoo= -cloud.google.com/go/texttospeech v1.4.0/go.mod h1:FX8HQHA6sEpJ7rCMSfXuzBcysDAuWusNNNvN9FELDd8= -cloud.google.com/go/texttospeech v1.5.0/go.mod h1:oKPLhR4n4ZdQqWKURdwxMy0uiTS1xU161C8W57Wkea4= -cloud.google.com/go/texttospeech v1.6.0/go.mod h1:YmwmFT8pj1aBblQOI3TfKmwibnsfvhIBzPXcW4EBovc= -cloud.google.com/go/texttospeech v1.7.1/go.mod h1:m7QfG5IXxeneGqTapXNxv2ItxP/FS0hCZBwXYqucgSk= -cloud.google.com/go/texttospeech v1.7.2/go.mod h1:VYPT6aTOEl3herQjFHYErTlSZJ4vB00Q2ZTmuVgluD4= -cloud.google.com/go/tpu v1.3.0/go.mod h1:aJIManG0o20tfDQlRIej44FcwGGl/cD0oiRyMKG19IQ= -cloud.google.com/go/tpu v1.4.0/go.mod h1:mjZaX8p0VBgllCzF6wcU2ovUXN9TONFLd7iz227X2Xg= -cloud.google.com/go/tpu v1.5.0/go.mod h1:8zVo1rYDFuW2l4yZVY0R0fb/v44xLh3llq7RuV61fPM= -cloud.google.com/go/tpu v1.6.1/go.mod h1:sOdcHVIgDEEOKuqUoi6Fq53MKHJAtOwtz0GuKsWSH3E= -cloud.google.com/go/tpu v1.6.2/go.mod h1:NXh3NDwt71TsPZdtGWgAG5ThDfGd32X1mJ2cMaRlVgU= -cloud.google.com/go/trace v1.3.0/go.mod h1:FFUE83d9Ca57C+K8rDl/Ih8LwOzWIV1krKgxg6N0G28= -cloud.google.com/go/trace v1.4.0/go.mod h1:UG0v8UBqzusp+z63o7FK74SdFE+AXpCLdFb1rshXG+Y= -cloud.google.com/go/trace v1.8.0/go.mod h1:zH7vcsbAhklH8hWFig58HvxcxyQbaIqMarMg9hn5ECA= -cloud.google.com/go/trace v1.9.0/go.mod h1:lOQqpE5IaWY0Ixg7/r2SjixMuc6lfTFeO4QGM4dQWOk= -cloud.google.com/go/trace v1.10.1/go.mod h1:gbtL94KE5AJLH3y+WVpfWILmqgc6dXcqgNXdOPAQTYk= -cloud.google.com/go/trace v1.10.2/go.mod h1:NPXemMi6MToRFcSxRl2uDnu/qAlAQ3oULUphcHGh1vA= -cloud.google.com/go/translate v1.3.0/go.mod h1:gzMUwRjvOqj5i69y/LYLd8RrNQk+hOmIXTi9+nb3Djs= -cloud.google.com/go/translate v1.4.0/go.mod h1:06Dn/ppvLD6WvA5Rhdp029IX2Mi3Mn7fpMRLPvXT5Wg= -cloud.google.com/go/translate v1.5.0/go.mod h1:29YDSYveqqpA1CQFD7NQuP49xymq17RXNaUDdc0mNu0= -cloud.google.com/go/translate v1.6.0/go.mod h1:lMGRudH1pu7I3n3PETiOB2507gf3HnfLV8qlkHZEyos= -cloud.google.com/go/translate v1.7.0/go.mod h1:lMGRudH1pu7I3n3PETiOB2507gf3HnfLV8qlkHZEyos= -cloud.google.com/go/translate v1.8.1/go.mod h1:d1ZH5aaOA0CNhWeXeC8ujd4tdCFw8XoNWRljklu5RHs= -cloud.google.com/go/translate v1.8.2/go.mod h1:d1ZH5aaOA0CNhWeXeC8ujd4tdCFw8XoNWRljklu5RHs= -cloud.google.com/go/translate v1.9.0/go.mod h1:d1ZH5aaOA0CNhWeXeC8ujd4tdCFw8XoNWRljklu5RHs= -cloud.google.com/go/translate v1.9.1/go.mod h1:TWIgDZknq2+JD4iRcojgeDtqGEp154HN/uL6hMvylS8= -cloud.google.com/go/video v1.8.0/go.mod h1:sTzKFc0bUSByE8Yoh8X0mn8bMymItVGPfTuUBUyRgxk= -cloud.google.com/go/video v1.9.0/go.mod h1:0RhNKFRF5v92f8dQt0yhaHrEuH95m068JYOvLZYnJSw= -cloud.google.com/go/video v1.12.0/go.mod h1:MLQew95eTuaNDEGriQdcYn0dTwf9oWiA4uYebxM5kdg= -cloud.google.com/go/video v1.13.0/go.mod h1:ulzkYlYgCp15N2AokzKjy7MQ9ejuynOJdf1tR5lGthk= -cloud.google.com/go/video v1.14.0/go.mod h1:SkgaXwT+lIIAKqWAJfktHT/RbgjSuY6DobxEp0C5yTQ= -cloud.google.com/go/video v1.15.0/go.mod h1:SkgaXwT+lIIAKqWAJfktHT/RbgjSuY6DobxEp0C5yTQ= -cloud.google.com/go/video v1.17.1/go.mod h1:9qmqPqw/Ib2tLqaeHgtakU+l5TcJxCJbhFXM7UJjVzU= -cloud.google.com/go/video v1.19.0/go.mod h1:9qmqPqw/Ib2tLqaeHgtakU+l5TcJxCJbhFXM7UJjVzU= -cloud.google.com/go/video v1.20.0/go.mod h1:U3G3FTnsvAGqglq9LxgqzOiBc/Nt8zis8S+850N2DUM= -cloud.google.com/go/video v1.20.1/go.mod h1:3gJS+iDprnj8SY6pe0SwLeC5BUW80NjhwX7INWEuWGU= -cloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU= -cloud.google.com/go/videointelligence v1.7.0/go.mod h1:k8pI/1wAhjznARtVT9U1llUaFNPh7muw8QyOUpavru4= -cloud.google.com/go/videointelligence v1.8.0/go.mod h1:dIcCn4gVDdS7yte/w+koiXn5dWVplOZkE+xwG9FgK+M= -cloud.google.com/go/videointelligence v1.9.0/go.mod h1:29lVRMPDYHikk3v8EdPSaL8Ku+eMzDljjuvRs105XoU= -cloud.google.com/go/videointelligence v1.10.0/go.mod h1:LHZngX1liVtUhZvi2uNS0VQuOzNi2TkY1OakiuoUOjU= -cloud.google.com/go/videointelligence v1.11.1/go.mod h1:76xn/8InyQHarjTWsBR058SmlPCwQjgcvoW0aZykOvo= -cloud.google.com/go/videointelligence v1.11.2/go.mod h1:ocfIGYtIVmIcWk1DsSGOoDiXca4vaZQII1C85qtoplc= -cloud.google.com/go/vision v1.2.0/go.mod h1:SmNwgObm5DpFBme2xpyOyasvBc1aPdjvMk2bBk0tKD0= -cloud.google.com/go/vision/v2 v2.2.0/go.mod h1:uCdV4PpN1S0jyCyq8sIM42v2Y6zOLkZs+4R9LrGYwFo= -cloud.google.com/go/vision/v2 v2.3.0/go.mod h1:UO61abBx9QRMFkNBbf1D8B1LXdS2cGiiCRx0vSpZoUo= -cloud.google.com/go/vision/v2 v2.4.0/go.mod h1:VtI579ll9RpVTrdKdkMzckdnwMyX2JILb+MhPqRbPsY= -cloud.google.com/go/vision/v2 v2.5.0/go.mod h1:MmaezXOOE+IWa+cS7OhRRLK2cNv1ZL98zhqFFZaaH2E= -cloud.google.com/go/vision/v2 v2.6.0/go.mod h1:158Hes0MvOS9Z/bDMSFpjwsUrZ5fPrdwuyyvKSGAGMY= -cloud.google.com/go/vision/v2 v2.7.0/go.mod h1:H89VysHy21avemp6xcf9b9JvZHVehWbET0uT/bcuY/0= -cloud.google.com/go/vision/v2 v2.7.2/go.mod h1:jKa8oSYBWhYiXarHPvP4USxYANYUEdEsQrloLjrSwJU= -cloud.google.com/go/vision/v2 v2.7.3/go.mod h1:V0IcLCY7W+hpMKXK1JYE0LV5llEqVmj+UJChjvA1WsM= -cloud.google.com/go/vmmigration v1.2.0/go.mod h1:IRf0o7myyWFSmVR1ItrBSFLFD/rJkfDCUTO4vLlJvsE= -cloud.google.com/go/vmmigration v1.3.0/go.mod h1:oGJ6ZgGPQOFdjHuocGcLqX4lc98YQ7Ygq8YQwHh9A7g= -cloud.google.com/go/vmmigration v1.5.0/go.mod h1:E4YQ8q7/4W9gobHjQg4JJSgXXSgY21nA5r8swQV+Xxc= -cloud.google.com/go/vmmigration v1.6.0/go.mod h1:bopQ/g4z+8qXzichC7GW1w2MjbErL54rk3/C843CjfY= -cloud.google.com/go/vmmigration v1.7.1/go.mod h1:WD+5z7a/IpZ5bKK//YmT9E047AD+rjycCAvyMxGJbro= -cloud.google.com/go/vmmigration v1.7.2/go.mod h1:iA2hVj22sm2LLYXGPT1pB63mXHhrH1m/ruux9TwWLd8= -cloud.google.com/go/vmwareengine v0.1.0/go.mod h1:RsdNEf/8UDvKllXhMz5J40XxDrNJNN4sagiox+OI208= -cloud.google.com/go/vmwareengine v0.2.2/go.mod h1:sKdctNJxb3KLZkE/6Oui94iw/xs9PRNC2wnNLXsHvH8= -cloud.google.com/go/vmwareengine v0.3.0/go.mod h1:wvoyMvNWdIzxMYSpH/R7y2h5h3WFkx6d+1TIsP39WGY= -cloud.google.com/go/vmwareengine v0.4.1/go.mod h1:Px64x+BvjPZwWuc4HdmVhoygcXqEkGHXoa7uyfTgSI0= -cloud.google.com/go/vmwareengine v1.0.0/go.mod h1:Px64x+BvjPZwWuc4HdmVhoygcXqEkGHXoa7uyfTgSI0= -cloud.google.com/go/vmwareengine v1.0.1/go.mod h1:aT3Xsm5sNx0QShk1Jc1B8OddrxAScYLwzVoaiXfdzzk= -cloud.google.com/go/vpcaccess v1.4.0/go.mod h1:aQHVbTWDYUR1EbTApSVvMq1EnT57ppDmQzZ3imqIk4w= -cloud.google.com/go/vpcaccess v1.5.0/go.mod h1:drmg4HLk9NkZpGfCmZ3Tz0Bwnm2+DKqViEpeEpOq0m8= -cloud.google.com/go/vpcaccess v1.6.0/go.mod h1:wX2ILaNhe7TlVa4vC5xce1bCnqE3AeH27RV31lnmZes= -cloud.google.com/go/vpcaccess v1.7.1/go.mod h1:FogoD46/ZU+JUBX9D606X21EnxiszYi2tArQwLY4SXs= -cloud.google.com/go/vpcaccess v1.7.2/go.mod h1:mmg/MnRHv+3e8FJUjeSibVFvQF1cCy2MsFaFqxeY1HU= -cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xXZmFiHmGE= -cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg= -cloud.google.com/go/webrisk v1.6.0/go.mod h1:65sW9V9rOosnc9ZY7A7jsy1zoHS5W9IAXv6dGqhMQMc= -cloud.google.com/go/webrisk v1.7.0/go.mod h1:mVMHgEYH0r337nmt1JyLthzMr6YxwN1aAIEc2fTcq7A= -cloud.google.com/go/webrisk v1.8.0/go.mod h1:oJPDuamzHXgUc+b8SiHRcVInZQuybnvEW72PqTc7sSg= -cloud.google.com/go/webrisk v1.9.1/go.mod h1:4GCmXKcOa2BZcZPn6DCEvE7HypmEJcJkr4mtM+sqYPc= -cloud.google.com/go/webrisk v1.9.2/go.mod h1:pY9kfDgAqxUpDBOrG4w8deLfhvJmejKB0qd/5uQIPBc= -cloud.google.com/go/websecurityscanner v1.3.0/go.mod h1:uImdKm2wyeXQevQJXeh8Uun/Ym1VqworNDlBXQevGMo= -cloud.google.com/go/websecurityscanner v1.4.0/go.mod h1:ebit/Fp0a+FWu5j4JOmJEV8S8CzdTkAS77oDsiSqYWQ= -cloud.google.com/go/websecurityscanner v1.5.0/go.mod h1:Y6xdCPy81yi0SQnDY1xdNTNpfY1oAgXUlcfN3B3eSng= -cloud.google.com/go/websecurityscanner v1.6.1/go.mod h1:Njgaw3rttgRHXzwCB8kgCYqv5/rGpFCsBOvPbYgszpg= -cloud.google.com/go/websecurityscanner v1.6.2/go.mod h1:7YgjuU5tun7Eg2kpKgGnDuEOXWIrh8x8lWrJT4zfmas= -cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0= -cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M= -cloud.google.com/go/workflows v1.8.0/go.mod h1:ysGhmEajwZxGn1OhGOGKsTXc5PyxOc0vfKf5Af+to4M= -cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA= -cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcPALq2CxzdePw= -cloud.google.com/go/workflows v1.11.1/go.mod h1:Z+t10G1wF7h8LgdY/EmRcQY8ptBD/nvofaL6FqlET6g= -cloud.google.com/go/workflows v1.12.0/go.mod h1:PYhSk2b6DhZ508tj8HXKaBh+OFe+xdl0dHF/tJdzPQM= -cloud.google.com/go/workflows v1.12.1/go.mod h1:5A95OhD/edtOhQd/O741NSfIMezNTbCwLM1P1tBRGHM= -collectd.org v0.3.0/go.mod h1:A/8DzQBkF6abtvrT2j/AU/4tiBgJWYyh0y/oB/4MlWE= dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= -gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= -git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc= -github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= -github.com/Azure/azure-sdk-for-go/sdk/azcore v0.21.1/go.mod h1:fBF9PQNqB8scdgpZ3ufzaLntG0AG7C1WjPMsiFOmfHM= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.0.0/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.0.0/go.mod h1:+6sju8gk8FRmSajX3Oz4G5Gm7P+mbqE9FVaXXFYTkCM= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0/go.mod h1:OQeznEEkTZ9OrhHJoDD8ZDq51FHgXjqtP9z6bEwBq9U= -github.com/Azure/azure-sdk-for-go/sdk/internal v0.8.3/go.mod h1:KLF4gFr6DcKFZwSuH8w8yEK6DpFl3LP5rhdvAb7Yz5I= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal v1.0.0/go.mod h1:ceIuwmxDWptoW3eCqSXlnPsZFKh4X+R38dWPv7GS9Vs= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.0.0/go.mod h1:s1tW/At+xHqjNFvWU4G0c0Qv33KOhvbGNj0RCTQDV8s= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.2.0/go.mod h1:c+Lifp3EDEamAkPVzMooRNOK6CZjNSdEnf1A7jsI9u4= -github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.3.0/go.mod h1:tPaiy8S5bQ+S5sOiDlINkp7+Ef339+Nz5L5XO+cnOHo= -github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.2.0/go.mod h1:+6KLcKIVgxoBDMqMO/Nvy7bZ9a0nbU3I1DtFQK3YvB4= -github.com/AzureAD/microsoft-authentication-library-for-go v0.4.0/go.mod h1:Vt9sXTKwMyGcOxSmLDMnGPgqsUg7m8pe215qMLrDXw4= -github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0/go.mod h1:kgDmCTgBzIEPFElEF+FK0SdjAor06dRq2Go927dnQ6o= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/CloudyKit/fastprinter v0.0.0-20170127035650-74b38d55f37a/go.mod h1:EFZQ978U7x8IRnstaskI3IysnWY5Ao3QgZUKOXlsAdw= -github.com/CloudyKit/jet v2.1.3-0.20180809161101-62edd43e4f88+incompatible/go.mod h1:HPYO+50pSWkPoj9Q/eq0aRGByCL6ScRlUmiEX5Zgm+w= -github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= -github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8= github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= -github.com/GoogleCloudPlatform/cloudsql-proxy v0.0.0-20190129172621-c8b1d7a94ddf/go.mod h1:aJ4qN3TfrelA6NZ6AXsXRfmEVaYin3EDbSPJrKS8OXo= -github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= -github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk= -github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY= -github.com/Joker/jade v1.0.1-0.20190614124447-d475f43051e7/go.mod h1:6E6s8o2AE4KhCrqr6GRJjdC/gNfTdxkIXvuGZZda2VM= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= -github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= @@ -983,26 +87,14 @@ github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8 github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= github.com/SaveTheRbtz/mph v0.1.1-0.20240117162131-4166ec7869bc h1:DCHzPQOcU/7gwDTWbFQZc5qHMPS1g0xTO56k8NXsv9M= github.com/SaveTheRbtz/mph v0.1.1-0.20240117162131-4166ec7869bc/go.mod h1:LJM5a3zcIJ/8TmZwlUczvROEJT8ntOdhdG9jjcR1B0I= -github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= -github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= 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.6.0/go.mod h1:0qHz5QP0GMX4pfmMA/zt5RgfNuXJrTP0zS7DqpHGGTw= -github.com/VictoriaMetrics/fastcache v1.12.1/go.mod h1:tX04vaqcNoQeGLD+ra5pU5sWkuxnzWhEzLwhP9w653o= github.com/VictoriaMetrics/fastcache v1.12.2 h1:N0y9ASrJ0F6h0QaC3o6uJb3NIZ9VKLjCM7NQbSmF7WI= github.com/VictoriaMetrics/fastcache v1.12.2/go.mod h1:AmC+Nzz1+3G2eCPapF6UcsnkThDcMsQicp4xDukwJYI= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= -github.com/aclements/go-gg v0.0.0-20170118225347-6dbb4e4fefb0/go.mod h1:55qNq4vcpkIuHowELi5C8e+1yUHtoLoOUR9QU5j7Tes= -github.com/aclements/go-moremath v0.0.0-20210112150236-f10218a38794/go.mod h1:7e+I0LQFUI9AXWxOfsQROs9xPhoJtbsyWcjJqDd4KPY= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= -github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= -github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY= -github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk= -github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= -github.com/ajstarks/svgo v0.0.0-20210923152817-c3b6e2f0c527/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= -github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= @@ -1010,18 +102,11 @@ github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= -github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= -github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/apache/arrow/go/arrow v0.0.0-20191024131854-af6fa24be0db/go.mod h1:VTxUBvSJ3s3eHAg65PNgrsn5BtqCRPdmyXh6rAfdxN0= -github.com/apache/arrow/go/v10 v10.0.1/go.mod h1:YvhnlEePVnBS4+0z3fhPfUy7W1Ikj0Ih0vcRo/gZ1M0= -github.com/apache/arrow/go/v11 v11.0.0/go.mod h1:Eg5OsL5H+e299f7u5ssuXsuHQVEGC4xei5aX110hRiI= -github.com/apache/arrow/go/v12 v12.0.0/go.mod h1:d+tV/eHZZ7Dz7RPrFKtPK02tpr+c9/PEd/zm8mDS9Vg= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= -github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= @@ -1030,85 +115,48 @@ github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6l github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= -github.com/aws/aws-sdk-go-v2 v1.2.0/go.mod h1:zEQs02YRBw1DjK0PoJv3ygDYOFTre1ejlJWl8FwAuQo= github.com/aws/aws-sdk-go-v2 v1.9.0/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= -github.com/aws/aws-sdk-go-v2 v1.21.2/go.mod h1:ErQhvNuEMhJjweavOYhxVkn2RUx7kQXVATHrjKtxIpM= -github.com/aws/aws-sdk-go-v2 v1.23.1/go.mod h1:i1XDttT4rnf6vxc9AuskLc6s7XBee8rlLilKlc03uAA= github.com/aws/aws-sdk-go-v2 v1.27.0 h1:7bZWKoXhzI+mMR/HjdMx8ZCC5+6fY0lS5tr0bbgiLlo= github.com/aws/aws-sdk-go-v2 v1.27.0/go.mod h1:ffIFB97e2yNsv4aTSGkqtHnppsIJzw7G7BReUZ3jCXM= -github.com/aws/aws-sdk-go-v2/config v1.1.1/go.mod h1:0XsVy9lBI/BCXm+2Tuvt39YmdHwS5unDQmxZOYe8F5Y= github.com/aws/aws-sdk-go-v2/config v1.8.0/go.mod h1:w9+nMZ7soXCe5nT46Ri354SNhXDQ6v+V5wqDjnZE+GY= -github.com/aws/aws-sdk-go-v2/config v1.18.45/go.mod h1:ZwDUgFnQgsazQTnWfeLWk5GjeqTQTL8lMkoE1UXzxdE= -github.com/aws/aws-sdk-go-v2/config v1.25.5/go.mod h1:Bf4gDvy4ZcFIK0rqDu1wp9wrubNba2DojiPB2rt6nvI= github.com/aws/aws-sdk-go-v2/config v1.27.15 h1:uNnGLZ+DutuNEkuPh6fwqK7LpEiPmzb7MIMA1mNWEUc= github.com/aws/aws-sdk-go-v2/config v1.27.15/go.mod h1:7j7Kxx9/7kTmL7z4LlhwQe63MYEE5vkVV6nWg4ZAI8M= -github.com/aws/aws-sdk-go-v2/credentials v1.1.1/go.mod h1:mM2iIjwl7LULWtS6JCACyInboHirisUUdkBPoTHMOUo= github.com/aws/aws-sdk-go-v2/credentials v1.4.0/go.mod h1:dgGR+Qq7Wjcd4AOAW5Rf5Tnv3+x7ed6kETXyS9WCuAY= -github.com/aws/aws-sdk-go-v2/credentials v1.13.43/go.mod h1:zWJBz1Yf1ZtX5NGax9ZdNjhhI4rgjfgsyk6vTY1yfVg= -github.com/aws/aws-sdk-go-v2/credentials v1.16.4/go.mod h1:Kdh/okh+//vQ/AjEt81CjvkTo64+/zIE4OewP7RpfXk= github.com/aws/aws-sdk-go-v2/credentials v1.17.15 h1:YDexlvDRCA8ems2T5IP1xkMtOZ1uLJOCJdTr0igs5zo= github.com/aws/aws-sdk-go-v2/credentials v1.17.15/go.mod h1:vxHggqW6hFNaeNC0WyXS3VdyjcV0a4KMUY4dKJ96buU= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.0.2/go.mod h1:3hGg3PpiEjHnrkrlasTfxFqUsZ2GCk/fMUn4CbKgSkM= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.5.0/go.mod h1:CpNzHK9VEFUCknu50kkB8z58AH2B5DvPP7ea1LHve/Y= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.13/go.mod h1:f/Ib/qYjhV2/qdsf79H3QP/eRE4AkVyEf6sk7XfZ1tg= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.5/go.mod h1:VhnExhw6uXy9QzetvpXDolo1/hjhx4u9qukBGkuUwjs= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.3 h1:dQLK4TjtnlRGb0czOht2CevZ5l6RSyRWAnKeGd7VAFE= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.3/go.mod h1:TL79f2P6+8Q7dTsILpiVST+AL9lkF6PPGI167Ny0Cjw= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.5.1 h1:VGkV9KmhGqOQWnHyi4gLG98kE6OecT42fdrCGFWxJsc= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.5.1/go.mod h1:PLlnMiki//sGnCJiW+aVpvP/C8Kcm8mEj/IVm9+9qk4= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.43/go.mod h1:auo+PiyLl0n1l8A0e8RIeR8tOzYPfZZH/JNlrJ8igTQ= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.4/go.mod h1:xEhvbJcyUf/31yfGSQBe01fukXwXJ0gxDp7rLfymWE0= github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.7 h1:lf/8VTF2cM+N4SLzaYJERKEWAXq8MOMpZfU6wEPWsPk= github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.7/go.mod h1:4SjkU7QiqK2M9oozyMzfZ/23LmUY+h3oFqhdeP5OMiI= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.37/go.mod h1:Qe+2KtKml+FEsQF/DHmDV+xjtche/hwoF75EG4UlHW8= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.4/go.mod h1:dYvTNAggxDZy6y1AF7YDwXsPuHFy/VNEpEI/2dWK9IU= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.7 h1:4OYVp0705xu8yjdyoWix0r9wPIRXnIzzOoUpQVHIJ/g= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.7/go.mod h1:vd7ESTEvI76T2Na050gODNmNU7+OyKrIKroYTu4ABiI= github.com/aws/aws-sdk-go-v2/internal/ini v1.2.2/go.mod h1:BQV0agm+JEhqR+2RT5e1XTFIDcAAV0eW6z2trp+iduw= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.45/go.mod h1:lD5M20o09/LCuQ2mE62Mb/iSdSlCNuj6H5ci7tW7OsE= -github.com/aws/aws-sdk-go-v2/internal/ini v1.7.1/go.mod h1:6fQQgfuGmw8Al/3M2IgIllycxV7ZW7WCdVSqfBeUiCY= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.3.0/go.mod h1:v8ygadNyATSm6elwJ/4gzJwcFhri9RqS8skgHKiwXPU= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.1/go.mod h1:l9ymW25HOqymeU2m1gbUQ3rUIsTwKs8gYHXkqDQUhiI= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2 h1:Ji0DY1xUsUr3I8cHps0G+XM3WWU16lP6yG8qu1GAZAs= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2/go.mod h1:5CsjAbs3NlGQyZNFACh+zztPDI7fU6eW9QsxjfnuBKg= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.0.2/go.mod h1:45MfaXZ0cNbeuT0KQ1XJylq8A6+OpVV2E5kvY/Kq+u8= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.3.0/go.mod h1:R1KK+vY8AfalhG1AOu5e35pOD2SdoPKQCFLTvnxiohk= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.37/go.mod h1:vBmDnwWXWxNPFRMmG2m/3MKOe+xEcMDo1tanpaWCcck= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.4/go.mod h1:aYCGNjyUCUelhofxlZyj63srdxWUSsBSGg5l6MCuXuE= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.9 h1:Wx0rlZoEJR7JwlSZcHnEa7CNjrSIyVxMFWGAaXy4fJY= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.9/go.mod h1:aVMHdE0aHO3v+f/iw01fmXV/5DbfQ3Bi9nN7nd9bE9Y= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.7.0 h1:HWsM0YQWX76V6MOp07YuTYacm8k7h69ObJuw7Nck+og= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.7.0/go.mod h1:LKb3cKNQIMh+itGnEpKGcnL/6OIjPZqrtYah1w5f+3o= -github.com/aws/aws-sdk-go-v2/service/kms v1.26.3/go.mod h1:N3++/sLV97B8Zliz7KRqNcojOX7iMBZWKiuit5FKtH0= -github.com/aws/aws-sdk-go-v2/service/route53 v1.1.1/go.mod h1:rLiOUrPLW/Er5kRcQ7NkwbjlijluLsrIbu/iyl35RO4= -github.com/aws/aws-sdk-go-v2/service/route53 v1.30.2/go.mod h1:TQZBt/WaQy+zTHoW++rnl8JBrmZ0VO6EUbVua1+foCA= github.com/aws/aws-sdk-go-v2/service/s3 v1.15.0 h1:nPLfLPfglacc29Y949sDxpr3X/blaY40s3B85WT2yZU= github.com/aws/aws-sdk-go-v2/service/s3 v1.15.0/go.mod h1:Iv2aJVtVSm/D22rFoX99cLG4q4uB7tppuCsulGe98k4= -github.com/aws/aws-sdk-go-v2/service/sso v1.1.1/go.mod h1:SuZJxklHxLAXgLTc1iFXbEWkXs7QRTQpCLGaKIprQW0= github.com/aws/aws-sdk-go-v2/service/sso v1.4.0/go.mod h1:+1fpWnL96DL23aXPpMGbsmKe8jLTEfbjuQoA4WS1VaA= -github.com/aws/aws-sdk-go-v2/service/sso v1.15.2/go.mod h1:gsL4keucRCgW+xA85ALBpRFfdSLH4kHOVSnLMSuBECo= -github.com/aws/aws-sdk-go-v2/service/sso v1.17.3/go.mod h1:oA6VjNsLll2eVuUoF2D+CMyORgNzPEW/3PyUdq6WQjI= github.com/aws/aws-sdk-go-v2/service/sso v1.20.8 h1:Kv1hwNG6jHC/sxMTe5saMjH6t6ZLkgfvVxyEjfWL1ks= github.com/aws/aws-sdk-go-v2/service/sso v1.20.8/go.mod h1:c1qtZUWtygI6ZdvKppzCSXsDOq5I4luJPZ0Ud3juFCA= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.17.3/go.mod h1:a7bHA82fyUXOm+ZSWKU6PIoBxrjSprdLoM8xPYvzYVg= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.20.1/go.mod h1:hHL974p5auvXlZPIjJTblXJpbkfK4klBczlsEaMCGVY= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.2 h1:nWBZ1xHCF+A7vv9sDzJOq4NWIdzFYm0kH7Pr4OjHYsQ= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.2/go.mod h1:9lmoVDVLz/yUZwLaQ676TK02fhCu4+PgRSmMaKR1ozk= -github.com/aws/aws-sdk-go-v2/service/sts v1.1.1/go.mod h1:Wi0EBZwiz/K44YliU0EKxqTCJGUfYTWXrrBwkq736bM= github.com/aws/aws-sdk-go-v2/service/sts v1.7.0/go.mod h1:0qcSMCyASQPN2sk/1KQLQ2Fh6yq8wm0HSDAimPhzCoM= -github.com/aws/aws-sdk-go-v2/service/sts v1.23.2/go.mod h1:Eows6e1uQEsc4ZaHANmsPRzAKcVDrcmjjWiih2+HUUQ= -github.com/aws/aws-sdk-go-v2/service/sts v1.25.4/go.mod h1:feTnm2Tk/pJxdX+eooEsxvlvTWBvDm6CasRZ+JOs2IY= github.com/aws/aws-sdk-go-v2/service/sts v1.28.9 h1:Qp6Boy0cGDloOE3zI6XhNLNZgjNS8YmiFQFHe71SaW0= github.com/aws/aws-sdk-go-v2/service/sts v1.28.9/go.mod h1:0Aqn1MnEuitqfsCNyKsdKLhDUOr4txD/g19EfiUqgws= -github.com/aws/smithy-go v1.1.0/go.mod h1:EzMw8dbp/YJL4A5/sbhGddag+NPT7q084agLbB9LgIw= github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= -github.com/aws/smithy-go v1.15.0/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= -github.com/aws/smithy-go v1.17.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE= github.com/aws/smithy-go v1.20.2 h1:tbp628ireGtzcHDDmLT/6ADHidqnwgF57XOXZe6tp4Q= github.com/aws/smithy-go v1.20.2/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E= -github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= @@ -1118,100 +166,63 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce 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.5.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= -github.com/bits-and-blooms/bitset v1.7.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= 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/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c= -github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= -github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= -github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= -github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU= -github.com/btcsuite/btcd/btcec/v2 v2.2.1/go.mod h1:9/CSmJxmuvqzX9Wh2fXMWToLOHhPd11lSPuIupwTkI8= github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ= github.com/btcsuite/btcd/btcec/v2 v2.3.4/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= -github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 h1:KdUfX2zKommPRa+PD0sWZUyXe9w277ABlgELO7H04IM= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= -github.com/bytecodealliance/wasmtime-go/v7 v7.0.0/go.mod h1:bu6fic7trDt20w+LMooX7j3fsOwv4/ln6j8gAdP6vmA= -github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34= -github.com/c-bata/go-prompt v0.2.6/go.mod h1:/LMAke8wD2FsNu9EXNdHxNLbd9MedkPnCdfpU9wwHfY= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk= github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 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/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudflare/cloudflare-go v0.14.0/go.mod h1:EnwdgGMaFOruiPZRFSgn+TsQ3hQ7C/YWzIGLeu5c304= -github.com/cloudflare/cloudflare-go v0.79.0/go.mod h1:gkHQf9xEubaQPEuerBuoinR9P8bf8a05Lq0X6WKy1Oc= 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/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20230310173818-32f1caf87195/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20230428030218-4003588d1b74/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa h1:jQCWAUqqlij9Pgj2i/PB79y4KOPYVyFYdROxgaCwdTQ= github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa/go.mod h1:x/1Gn8zydmfq8dk6e9PdstVsDgu9RuyIIJqAaF//0IM= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= -github.com/cockroachdb/datadriven v1.0.0/go.mod h1:5Ib8Meh+jk1RlHIXej6Pzevx/NLlNvQB9pmSBZErGA4= 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.6.1/go.mod h1:tm6FTP5G81vwJ5lC0SizQo374JNCOPrHyXGitRJoDqM= -github.com/cockroachdb/errors v1.8.1/go.mod h1:qGwQn6JmZ+oMjuLwjWzUNqblqk0xl4CVV3SQbGwK7Ac= github.com/cockroachdb/errors v1.11.3 h1:5bA+k2Y6r+oz/6Z/RFlNeVCesGARKuC6YymtcDrbC/I= github.com/cockroachdb/errors v1.11.3/go.mod h1:m4UIW4CDjx+R5cybPsNrRbreomiFqt8o1h1wUVazSd8= github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce h1:giXvy4KSc/6g/esnpM7Geqxka4WSqI1SZc7sMJFd3y4= github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce/go.mod h1:9/y3cnZ5GKakj/H4y9r9GTjCvAFta7KLgSHPJJYc52M= -github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= -github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593/go.mod h1:6hk1eMY/u5t+Cf18q5lFMUA1Rc+Sm5I6Ra1QuPyxXCo= github.com/cockroachdb/pebble v1.1.1 h1:XnKU22oiCLy2Xn8vp1re67cXg4SAasg/WDt1NtcRFaw= github.com/cockroachdb/pebble v1.1.1/go.mod h1:4exszw1r40423ZsmkG/09AFEG83I0uDgfujJdbL6kYU= -github.com/cockroachdb/redact v1.0.8/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= 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/sentry-go v0.6.1-cockroachdb.2/go.mod h1:8BT+cPK6xvFOcRlk0R8eg+OTkcqI6baNH4xAkpiYVvQ= 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/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= -github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= -github.com/consensys/bavard v0.1.8-0.20210406032232-f3452dc9b572/go.mod h1:Bpd0/3mZuaj6Sj+PqrmIquiOKy397AKGThQPaGzNXAQ= 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.4.1-0.20210426202927-39ac3d4b3f1f/go.mod h1:815PAHg3wvysy0SyIqanF8gZ0Y1wjk/hrDHD/iT88+Q= -github.com/consensys/gnark-crypto v0.10.0/go.mod h1:Iq/P3HHl0ElSjsg2E1gsMwhAyxnxoKK5nVyZKd+/KhU= 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/containerd/cgroups v0.0.0-20201119153540-4cbc285b3327/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= @@ -1235,49 +246,28 @@ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfc github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 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.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/crate-crypto/go-ipa v0.0.0-20230601170251-1830d0757c80/go.mod h1:gzbVz57IDJgQ9rLQwfSk696JGWof8ftznEL9GoAv3NI= 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 v0.7.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= 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/cskr/pubsub v1.0.2 h1:vlOzMhl6PFn60gRlTQQsIfVwaPB/B/8MziK8FhEPt/0= github.com/cskr/pubsub v1.0.2/go.mod h1:/8MzYXk/NJAz782G8RPkFzXTZVu63VotefPnR9TIRis= -github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= -github.com/dave/astrid v0.0.0-20170323122508-8c2895878b14/go.mod h1:Sth2QfxfATb/nW4EsrSi2KyJmbcniZ8TgTaji17D6ms= -github.com/dave/brenda v1.1.0/go.mod h1:4wCUr6gSlu5/1Tk7akE5X7UorwiQ8Rij0SKH3/BGMOM= -github.com/dave/courtney v0.3.0/go.mod h1:BAv3hA06AYfNUjfjQr+5gc6vxeBVOupLqrColj+QSD8= -github.com/dave/dst v0.27.2/go.mod h1:jHh6EOibnHgcUW3WjKHisiooEkYwqpHLBSX1iOBhEyc= -github.com/dave/gopackages v0.0.0-20170318123100-46e7023ec56e/go.mod h1:i00+b/gKdIDIxuLDFob7ustLAVqhsZRk2qVZrArELGQ= -github.com/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg= -github.com/dave/jennifer v1.5.0/go.mod h1:4MnyiFIlZS3l5tSDn8VnzE6ffAhYBMB2SZntBsZGUok= -github.com/dave/kerr v0.0.0-20170318121727-bc25dd6abe8e/go.mod h1:qZqlPyPvfsDJt+3wHJ1EvSXDuVjFTK0j2p/ca+gtsb8= -github.com/dave/patsy v0.0.0-20210517141501-957256f50cba/go.mod h1:qfR88CgEGLoiqDaE+xxDCi5QA5v4vUoW0UCX2Nd5Tlc= -github.com/dave/rebecca v0.9.1/go.mod h1:N6XYdMD/OKw3lkF3ywh8Z6wPGuwNFDNtWYEMFWEmXBA= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c h1:pFUpOrbxDR6AkioZ1ySsx5yxlDQZ8stG2b88gTPxgJU= github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c/go.mod h1:6UhI8N9EjYm1c2odKpFpAYeR8dsBeM7PtzQhRgxRr9U= -github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo= -github.com/deckarep/golang-set/v2 v2.1.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= 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/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= -github.com/deepmap/oapi-codegen v1.6.0/go.mod h1:ryDa9AgbELGeB+YEXE1dR53yAjHwFvE9iAUlWl9Al3M= -github.com/deepmap/oapi-codegen v1.8.2/go.mod h1:YLgSKSDv/bZQB7N4ws6luhozi3cEdRktEqrX88CvjIw= github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f h1:U5y3Y5UE0w7amNe7Z5G/twsBW0KEalRQXZzf8ufSh9I= github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE= -github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= github.com/dgraph-io/badger/v2 v2.2007.3/go.mod h1:26P/7fbL4kUZVEVKLAKXkBXKOydDmM2p1e+NhhnBCAE= github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o= github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk= @@ -1285,24 +275,12 @@ github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KP github.com/dgraph-io/ristretto v0.1.0 h1:Jv3CGQHp9OjuMBSne1485aDpUkTKEcUqF+jm/LuerPI= github.com/dgraph-io/ristretto v0.1.0/go.mod h1:fux0lOrBhrVCJd3lcTHsIJhq1T2rokOu6v9Vcb3Q9ug= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-bitstream v0.0.0-20180413035011-3522498ce2c8/go.mod h1:VMaSuZ+SZcx/wljOQKvp5srsbCiKDEb6K2wC4+PiBmQ= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= -github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko= -github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= -github.com/docker/docker v1.6.2/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/dop251/goja v0.0.0-20211022113120-dc8c55024d06/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk= -github.com/dop251/goja v0.0.0-20220405120441-9037c2b61cbf/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk= -github.com/dop251/goja v0.0.0-20230806174421-c933cf95e127/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4= -github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y= -github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d/go.mod h1:DngW8aVqWbuLRMHItjPUyqdj+HWPvnQe8V8y1nDpIbM= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= @@ -1310,11 +288,9 @@ github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+m github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= -github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/ef-ds/deque v1.0.4 h1:iFAZNmveMT9WERAkqLJ+oaABF9AcVQ5AjXem/hroniI= github.com/ef-ds/deque v1.0.4/go.mod h1:gXDnTC3yqvBcHbq2lcExjtAcVrOnJCbMcZXmuj8Z4tg= -github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= github.com/elastic/gosigar v0.12.0/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= github.com/elastic/gosigar v0.14.2 h1:Dg80n8cr90OZ7x+bAax/QjoW/XqTI11RmA79ZwIm9/4= github.com/elastic/gosigar v0.14.2/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= @@ -1328,46 +304,23 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.m github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= -github.com/envoyproxy/go-control-plane v0.10.3/go.mod h1:fJJn/j26vwOu972OllsvAgJJM//w9BV6Fxbg2LuVd34= -github.com/envoyproxy/go-control-plane v0.11.0/go.mod h1:VnHyVMpzcLvCFt9yUz1UnCwHLhwx1WguiVDV7pTG/tI= -github.com/envoyproxy/go-control-plane v0.11.1-0.20230524094728-9239064ad72f/go.mod h1:sfYdkwUW4BA3PbKjySwjJy+O4Pu0h62rlqCMHNk+K+Q= -github.com/envoyproxy/go-control-plane v0.11.1/go.mod h1:uhMcXKCQMEJHiAb0w+YGefQLaTEw+YhGluxZkrTmD0g= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo= -github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w= -github.com/envoyproxy/protoc-gen-validate v0.10.0/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= -github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= -github.com/envoyproxy/protoc-gen-validate v1.0.1/go.mod h1:0vj8bNkYbSTNS2PIyH87KZaeN4x9zpL9Qt8fQC7d+vs= -github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A= github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew= -github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= -github.com/ethereum/c-kzg-4844 v0.4.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= 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.10.26/go.mod h1:EYFyF19u3ezGLD4RqOkLq+ZCXzYbLoNDdZlMt7kyKFg= -github.com/ethereum/go-ethereum v1.13.5/go.mod h1:yMTu38GSuyxaYzQMViqNmQ1s3cE84abZexQmTgenWk0= github.com/ethereum/go-ethereum v1.13.10 h1:Ppdil79nN+Vc+mXfge0AuUgmKWuVv4eMqzoIVSdqZek= github.com/ethereum/go-ethereum v1.13.10/go.mod h1:sc48XYQxCzH3fG9BcrXCOOgQk2JfZzNAmIKnceogzsA= 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/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/felixge/fgprof v0.9.3 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g= github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/fjl/gencodec v0.0.0-20220412091415-8bb9e558978c/go.mod h1:AzA8Lj6YtixmJWL+wkKoBGsLWy9gFrAzi4g+5bCKwpY= -github.com/fjl/gencodec v0.0.0-20230517082657-f9840df7b83e/go.mod h1:AzA8Lj6YtixmJWL+wkKoBGsLWy9gFrAzi4g+5bCKwpY= -github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= -github.com/flosch/pongo2 v0.0.0-20190707114632-bbf5a6c351f4/go.mod h1:T9YF2M40nIgbVgp3rreNmTged+9HrbNTIQf1PsaIiTA= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/flynn/noise v1.0.1 h1:vPp/jdQLXC6ppsXSj/pM3W1BIJ5FEHE2TulSJBpb43Y= github.com/flynn/noise v1.0.1/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag= -github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= -github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= @@ -1378,7 +331,6 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/fxamacker/cbor/v2 v2.4.1-0.20220515183430-ad2eae63303f/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= github.com/fxamacker/cbor/v2 v2.4.1-0.20230228173756-c0c9f774e40c h1:5tm/Wbs9d9r+qZaUFXk59CWDD0+77PBqDREffYkyi5c= github.com/fxamacker/cbor/v2 v2.4.1-0.20230228173756-c0c9f774e40c/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= github.com/fxamacker/circlehash v0.3.0 h1:XKdvTtIJV9t7DDUtsf0RIpC1OcxZtPbmgIH7ekx28WA= @@ -1389,42 +341,21 @@ github.com/gammazero/deque v0.1.0 h1:f9LnNmq66VDeuAlSAapemq/U7hJ2jpIWa4c09q8Dlik github.com/gammazero/deque v0.1.0/go.mod h1:KQw7vFau1hHuM8xmI9RbgKFbAsQFWmBpqQ2KenFLk6M= github.com/gammazero/workerpool v1.1.2 h1:vuioDQbgrz4HoaCi2q1HLlOXdpbap5AET7xu5/qj87g= github.com/gammazero/workerpool v1.1.2/go.mod h1:UelbXcO0zCIGFcufcirHhq2/xtLXJdQ29qZNlXG9OjQ= -github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61/go.mod h1:Q0X6pkwTILDlzrGEckF6HKjXe48EgsY/l7K7vhY4MW8= -github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI= github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= -github.com/gballet/go-verkle v0.0.0-20230607174250-df487255f46b/go.mod h1:CDncRYVRSDqwakm282WEkjfaAj1hxU/v5RXxk5nXOiI= -github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= -github.com/getkin/kin-openapi v0.61.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps= github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= -github.com/ghemawat/stream v0.0.0-20171120220530-696b145b53b9/go.mod h1:106OIgooyS7OzLDOpUGgm9fA3bQENb/cFSyyBmMoJDs= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do= github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8= github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= -github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ= -github.com/glebarez/go-sqlite v1.22.0/go.mod h1:PlBIdHe0+aUEFn+r2/uthrWq4FxbzugL0L8Li6yQJbc= github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= -github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= -github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= -github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= -github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= 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-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g= -github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks= -github.com/go-fonts/latin-modern v0.3.0/go.mod h1:ysEQXnuT/sCDOAONxC7ImeEDVINbltClhasMAqEtRK0= -github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= -github.com/go-fonts/liberation v0.2.0/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= -github.com/go-fonts/liberation v0.3.0/go.mod h1:jdJ+cqF+F4SUL2V+qxBth8fvBpBDS7yloUL5Fi8GTGY= -github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmnUIzUY= 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= @@ -1433,34 +364,22 @@ github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2 github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= github.com/go-kit/kit v0.12.0 h1:e4o3o3IsBfAKQh5Qbbiqyfu97Ku7jrO/JbohvztANh4= github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEaizzs= -github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= -github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U= -github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81/go.mod h1:SX0U8uGpxhq9o2S/CELCSUxEWWAuoCUcVCQWv7G2OCk= -github.com/go-latex/latex v0.0.0-20230307184459-12ec69307ad9/go.mod h1:gWuR/CrFDDeVRFQwHPvsv9soJVB/iqymhuZQuJ3a9OM= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= -github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.2.6/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-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= -github.com/go-pdf/fpdf v0.6.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= @@ -1475,11 +394,8 @@ github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91 github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= github.com/go-playground/validator/v10 v10.14.1 h1:9c50NUPC30zyuKprjL3vNZ0m5oG+jU0zvx4AqHGnv4k= github.com/go-playground/validator/v10 v10.14.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= -github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -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/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= @@ -1493,7 +409,6 @@ github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6Wezm github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= github.com/gobwas/ws v1.2.1 h1:F2aeBZrm2NDsc7vbovKrWSogd4wvfAxg0FQ89/iqOTk= github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= -github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -1502,8 +417,6 @@ github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/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 v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -1511,18 +424,7 @@ github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zV github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM= -github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= -github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang-jwt/jwt/v4 v4.3.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= -github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= -github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= -github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68= github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -1560,7 +462,6 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw 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/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -1569,17 +470,8 @@ github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEW 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/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y= -github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= -github.com/gonum/blas v0.0.0-20181208220705-f22b278b28ac/go.mod h1:P32wAyui1PQ58Oce/KYkOqQv8cVw1zAapXOl+dRFGbc= -github.com/gonum/floats v0.0.0-20181209220543-c233463c7e82/go.mod h1:PxC8OnwL11+aosOB5+iEPoV3picfs8tUpkVd0pDo+Kg= -github.com/gonum/internal v0.0.0-20181124074243-f884aa714029/go.mod h1:Pu4dmpkhSyOzRwuXkOgAvijx4o+4YMUJJo9OvPYMkks= -github.com/gonum/lapack v0.0.0-20181123203213-e4cdc5a0bff9/go.mod h1:XA3DeT6rxh2EAE789SSiSJNqxPaC0aE9J8NTOI0Jo/A= -github.com/gonum/matrix v0.0.0-20181209220409-c518dec07be9/go.mod h1:0EXg4mc1CNP0HCqCz+K4ts155PXIlUywf0wqN+GfPZw= 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/flatbuffers v1.11.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= -github.com/google/flatbuffers v2.0.8+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= 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= @@ -1594,16 +486,11 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 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/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= -github.com/google/go-pkcs11 v0.2.0/go.mod h1:6eQoGcuNJpa7jnd5pMGdkSaQpNDYvPlXWMcjXXThLlY= -github.com/google/go-pkcs11 v0.2.1-0.20230907215043-c6f79328ddf9/go.mod h1:6eQoGcuNJpa7jnd5pMGdkSaQpNDYvPlXWMcjXXThLlY= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= -github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa/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/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= @@ -1632,36 +519,19 @@ github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg= github.com/google/pprof v0.0.0-20220412212628-83db2b799d1f/go.mod h1:Pt31oes+eGImORns3McJn8zHefuQl2rG8l6xQjGYB4U= -github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg= github.com/google/pprof v0.0.0-20231229205709-960ae82b1e42 h1:dHLYa5D8/Ta0aLR2XcPsrkpAgGeFs6thhMcQK0oQ0n8= github.com/google/pprof v0.0.0-20231229205709-960ae82b1e42/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/s2a-go v0.1.0/go.mod h1:OJpEgntRZo8ugHpF9hkoLJbS5dSI20XZeXJ9JVywLlM= -github.com/google/s2a-go v0.1.3/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= -github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= -github.com/google/safehtml v0.0.2/go.mod h1:L4KWwDsUJdECRAEpZoBn3O64bQaywRscowZjJAzjHnU= github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= -github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= -github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= -github.com/googleapis/enterprise-certificate-proxy v0.2.1/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= -github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= -github.com/googleapis/enterprise-certificate-proxy v0.2.4/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= -github.com/googleapis/enterprise-certificate-proxy v0.2.5/go.mod h1:RxW0N9901Cko1VOCW3SXCpWP+mlIEkk2tP7jnHy9a3w= github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= -github.com/googleapis/gax-go v0.0.0-20161107002406-da06d194a00e/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= @@ -1671,13 +541,6 @@ github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0 github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= -github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo= -github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY= -github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= -github.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= -github.com/googleapis/gax-go/v2 v2.8.0/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= -github.com/googleapis/gax-go/v2 v2.10.0/go.mod h1:4UOEnMCrxsSqQ940WnTiD6qJ63le2ev3xfyagutxiPw= -github.com/googleapis/gax-go/v2 v2.11.0/go.mod h1:DxmR61SGKkGLa2xigwuZIQpkCI2S5iydzRfb3peWZJI= github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= @@ -1688,16 +551,13 @@ github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORR github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/graph-gophers/graphql-go v1.3.0/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= @@ -1710,27 +570,19 @@ github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpg github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w= github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 h1:Wqo399gCIufwto+VfwCSvsnfGpF/w5E9CNxSwbpD6No= github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0/go.mod h1:qmOFXW2epJhM0qSnUUYpldc7gVz2KMQwJ/QYCDIa7XU= -github.com/guptarohit/asciigraph v0.5.5/go.mod h1:dYl5wwK4gNsnFf9Zp+l06rFiDZ5YtXM6x7SRWZ3KGag= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -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-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= -github.com/hashicorp/go-hclog v1.2.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= 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-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-retryablehttp v0.7.4/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= 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= @@ -1740,7 +592,6 @@ github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09 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/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= @@ -1751,11 +602,8 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO 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/holiman/billy v0.0.0-20230718173358-1c7e68d277a7/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= -github.com/holiman/uint256 v1.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw= -github.com/holiman/uint256 v1.2.3/go.mod h1:SC8Ryt4n+UBbPbIBKaG9zbbDlp4jOru9xFZmPzLUTxw= github.com/holiman/uint256 v1.3.0 h1:4wdcm/tnd0xXdu7iS3ruNvxkWwrb4aeBQv19ayYn8F4= github.com/holiman/uint256 v1.3.0/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= @@ -1766,35 +614,17 @@ github.com/huandu/go-clone v1.6.0/go.mod h1:ReGivhG6op3GYr+UY3lS6mxjKp7MIGTknuU5 github.com/huandu/go-clone/generic v1.7.2 h1:47pQphxs1Xc9cVADjOHN+Bm5D0hNagwH9UXErbxgVKA= github.com/huandu/go-clone/generic v1.7.2/go.mod h1:xgd9ZebcMsBWWcBx5mVMCoqMX24gLWr5lQicr+nVXNs= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= -github.com/huin/goupnp v1.0.3/go.mod h1:ZxNlw5WqJj6wSsRK5+YfflQGXYfccj5VgQsMNixHM7Y= github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= -github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= -github.com/hydrogen18/memlistener v0.0.0-20141126152155-54553eb933fb/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE= -github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= 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/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= -github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= -github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= github.com/improbable-eng/grpc-web v0.15.0 h1:BN+7z6uNXZ1tQGcNAuaU1YjsLTApzkjt2tzCixLaUPQ= github.com/improbable-eng/grpc-web v0.15.0/go.mod h1:1sy9HKV4Jt9aEs9JSnkWlRJPuPtwNr0l57L4f878wP8= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/influxdata/flux v0.65.1/go.mod h1:J754/zds0vvpfwuq7Gc2wRdVwEodfpCFM7mYlOw2LqY= -github.com/influxdata/influxdb v1.8.3/go.mod h1:JugdFhsvvI8gadxOI6noqNeeBHvWNTbfYGtiAn+2jhI= -github.com/influxdata/influxdb-client-go/v2 v2.4.0/go.mod h1:vLNHdxTJkIf2mSLvGrpj8TCcISApPoXkaxP8g9uRlW8= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= -github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= -github.com/influxdata/influxql v1.1.1-0.20200828144457-65d3ef77d385/go.mod h1:gHp9y86a/pxhjJ+zMjNXiQAA197Xk9wLxaz+fGG+kWk= -github.com/influxdata/line-protocol v0.0.0-20180522152040-32c6aa80de5e/go.mod h1:4kt73NQhadE3daL3WhR5EJ/J2ocX0PZzwxQ0gXJ7oFE= -github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo= -github.com/influxdata/line-protocol v0.0.0-20210311194329-9aa0e372d097/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo= -github.com/influxdata/promql/v2 v2.12.0/go.mod h1:fxOPu+DY0bqCTCECchSRtWfc+0X19ybifQhZoQNF5D8= -github.com/influxdata/roaring v0.4.13-0.20180809181101-fc520f41fab6/go.mod h1:bSgUQ7q5ZLSO+bKBGqJiCBGAl+9DxyW63zLTujjUlOE= -github.com/influxdata/tdigest v0.0.0-20181121200506-bf2b5ad3c0a9/go.mod h1:Js0mqiSBE6Ffsg94weZZ2c+v/ciT8QRHFOap7EKDrR0= -github.com/influxdata/usage-client v0.0.0-20160829180054-6d3895376368/go.mod h1:Wbbw6tYNvwa5dlB6304Sd+82Z3f7PmVZHVKU637d4po= github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs= github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0= github.com/ipfs/go-block-format v0.2.0 h1:ZqrkxBA2ICbDRbK8KJs/u0O3dlp6gmAuuXUJNiW1Ycs= @@ -1837,10 +667,6 @@ github.com/ipfs/go-peertaskqueue v0.8.1 h1:YhxAs1+wxb5jk7RvS0LHdyiILpNmRIRnZVzte github.com/ipfs/go-peertaskqueue v0.8.1/go.mod h1:Oxxd3eaK279FxeydSPPVGHzbwVeHjatZ2GA8XD+KbPU= github.com/ipld/go-ipld-prime v0.21.0 h1:n4JmcpOlPDIxBcY037SVfpd1G+Sj1nKZah0m6QH9C2E= github.com/ipld/go-ipld-prime v0.21.0/go.mod h1:3RLqy//ERg/y5oShXXdx5YIp50cFGOanyMctpPjsvxQ= -github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI= -github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0= -github.com/iris-contrib/i18n v0.0.0-20171121225848-987a633949d0/go.mod h1:pMCz62A0xJL6I+umB2YTlFRwWXaDFA0jy+5HzGiJjqI= -github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw= 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/jbenet/go-cienv v0.1.0/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA= @@ -1848,8 +674,6 @@ github.com/jbenet/go-temp-err-catcher v0.1.0 h1:zpb3ZH6wIE8Shj2sKS+khgRvf7T7RABo github.com/jbenet/go-temp-err-catcher v0.1.0/go.mod h1:0kJRvmDZXNMIiJirNPEYfhpPwbGVtZVWC34vc5WLsDk= github.com/jbenet/goprocess v0.1.4 h1:DRGOFReOMqqDNXwW70QkacFW0YN9QnwLV0Vqk+3oU0o= github.com/jbenet/goprocess v0.1.4/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4= -github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e/go.mod h1:G1CVv03EnqU1wYL2dFwXxW2An0az9JTl/ZsqXQeBlkU= -github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267/go.mod h1:h1nSAbGFqGVzn6Jyl1R/iCcBUHN4g+gW1u9CoBTrb9E= github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= @@ -1863,63 +687,33 @@ github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 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/jsternberg/zap-logfmt v1.0.0/go.mod h1:uvPs/4X51zdkcm5jXl5SYoN+4RK21K8mysFmDaM/h+o= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q= -github.com/juju/loggo v0.0.0-20180524022052-584905176618/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U= -github.com/juju/testing v0.0.0-20180920084828-472a3e8b2073/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= -github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= -github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef/go.mod h1:Ct9fl0F6iIOGgxJ5npU/IUOhOhqlVrGjyIZc8/MagT0= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 h1:uC1QfSlInpQF+M0ao65imhwqKnz3Q2z/d8PWZRMQvDM= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw= github.com/k0kubun/pp v3.0.1+incompatible h1:3tqvf7QgUnZ5tXO6pNAZlrvHgl6DvifjDrd9g2S9Z40= github.com/k0kubun/pp v3.0.1+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg= -github.com/k0kubun/pp/v3 v3.2.0/go.mod h1:ODtJQbQcIRfAD3N+theGCV1m/CBxweERz2dapdz1EwA= -github.com/karalabe/usb v0.0.2/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= -github.com/kataras/golog v0.0.9/go.mod h1:12HJgwBIZFNGL0EJnMRhmvGA0PQGx8VFwrZtM4CqbAk= -github.com/kataras/iris/v12 v12.0.1/go.mod h1:udK4vLQKkdDqMGJJVd/msuMtN6hpYJhg/lSzuxjhO+U= -github.com/kataras/neffos v0.0.10/go.mod h1:ZYmJC07hQPW67eKuzlfY7SO3bC0mw83A3j6im82hfqw= -github.com/kataras/pio v0.0.0-20190103105442-ea782b38602d/go.mod h1:NV88laa9UiiDuX9AhMbDPkGYSPugBOV6yTZB1l2K9Z0= -github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= -github.com/kevinburke/go-bindata v3.23.0+incompatible/go.mod h1:/pEEZ72flUW2p0yi30bslSp9YqD9pysLxunQDdb2CPM= github.com/kevinburke/go-bindata v3.24.0+incompatible h1:qajFA3D0pH94OTLU4zcCCKCDgR+Zr2cZK/RPJHDdFoY= github.com/kevinburke/go-bindata v3.24.0+incompatible/go.mod h1:/pEEZ72flUW2p0yi30bslSp9YqD9pysLxunQDdb2CPM= -github.com/kilic/bls12-381 v0.1.0/go.mod h1:vDTTHJONJ6G+P2R74EhnyotQDTliQDnFEwhdmfzw1ig= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= 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/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= -github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/compress v1.9.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= -github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= -github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4= github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= -github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= -github.com/klauspost/cpuid/v2 v2.2.0/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= -github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= -github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6/go.mod h1:+ZoRqAPRLkC4NPOvfYeR5KNOrY6TD+/sAC3HXPZgDYg= -github.com/klauspost/pgzip v1.0.2-0.20170402124221-0bf5dcad4ada/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= 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.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/koron/go-ssdp v0.0.4 h1:1IDwrghSKYM7yLf7XCzbByg2sJ/JcNOZRXS2jczTwz0= @@ -1929,28 +723,21 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= 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.3/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 v0.0.0-20170224010052-a616ab194758/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= 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/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g= -github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg= -github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= 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/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= -github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/libp2p/go-addr-util v0.1.0 h1:acKsntI33w2bTU7tC9a0SaPimJGfSI0bFKC18ChxeVI= github.com/libp2p/go-addr-util v0.1.0/go.mod h1:6I3ZYuFr2O/9D+SoyM0zEw0EF3YkldtTX406BpdQMqw= github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= @@ -1993,67 +780,37 @@ github.com/logrusorgru/aurora/v4 v4.0.0/go.mod h1:lP0iIa2nrnT/qoFXcOZSrZQpJ1o6n2 github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= -github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= -github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= -github.com/lyft/protoc-gen-star/v2 v2.0.1/go.mod h1:RcCdONR2ScXaYnQC5tUzxzlpA3WVYF7/opLeUgcQs/o= -github.com/lyft/protoc-gen-star/v2 v2.0.3/go.mod h1:amey7yeodaJhXSbf/TlLvWiqQfLOSpEk//mLlc+axEk= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 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.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd h1:br0buuQ854V8u83wA0rVZ8ttrq5CpaPZdvrK0LP2lOk= github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd/go.mod h1:QuCEs1Nt24+FYQEqAAncTDPJIuGs+LxK1MCiFL25pMU= -github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ= 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.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/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.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= -github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.17/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.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -github.com/mattn/go-sqlite3 v1.14.5/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI= -github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= -github.com/mattn/go-tty v0.0.0-20180907095812-13ff1204f104/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE= -github.com/mattn/go-tty v0.0.3/go.mod h1:ihxohKRERHTVzN+aSVRwACLCeqIoZAWpoICkkvrWyR0= -github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= -github.com/mediocregopher/mediocre-go-lib v0.0.0-20181029021733-cb65787f37ed/go.mod h1:dSsfyI2zABAdhcbvkXqgxOxrCsbYeHCPgrZkku60dSg= -github.com/mediocregopher/radix/v3 v3.3.0/go.mod h1:EmfVyvspXz1uZEyPBMyGK+kjWiKQGvsUt6O3Pj+LDCQ= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= -github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM= @@ -2064,9 +821,7 @@ github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b h1:z78hV3sbSMAUoyUM github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b/go.mod h1:lxPUiZwKoFL8DUUmalo2yJJUCxbPKtm8OKfqr2/FTNU= github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc h1:PTfri+PuQmWDqERdnNMiD9ZejrlswWrCpBEZgWOiTrc= github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc/go.mod h1:cGKTAVKx4SxOuR/czcZ/E2RSJ3sfHs8FpHhQ5CWMf9s= -github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= -github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= @@ -2080,10 +835,8 @@ github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS4 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/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= @@ -2094,17 +847,13 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= -github.com/montanaflynn/stats v0.6.6/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= github.com/montanaflynn/stats v0.7.0 h1:r3y12KyNxj/Sb/iOE46ws+3mS1+MZca1wlHQFPsY/JU= github.com/montanaflynn/stats v0.7.0/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= -github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= -github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg= github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA= github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE= github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI= @@ -2142,20 +891,15 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/grpc-proxy v0.0.0-20181017164139-0f1106ef9c76/go.mod h1:x5OoJHDHqxHS801UIuhqGl6QdSAEJvtausosHSdazIo= -github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0= -github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E= github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= -github.com/nats-io/nats.go v1.8.1/go.mod h1:BrFz9vVn0fU3AcH9Vn4Kd7W0NpJ651tD5omQ3M8LwxM= github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= -github.com/nats-io/nkeys v0.0.2/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7TDb/4= github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= 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= @@ -2165,51 +909,41 @@ github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= 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/onflow/atree v0.6.1-0.20230711151834-86040b30171f/go.mod h1:xvP61FoOs95K7IYdIYRnNcYQGf4nbF/uuJ0tHf4DRuM= -github.com/onflow/atree v0.8.0-rc.6 h1:GWgaylK24b5ta2Hq+TvyOF7X5tZLiLzMMn7lEt59fsA= -github.com/onflow/atree v0.8.0-rc.6/go.mod h1:yccR+LR7xc1Jdic0mrjocbHvUD7lnVvg8/Ct1AA5zBo= +github.com/onflow/atree v0.8.0 h1:qg5c6J1gVDNObughpEeWm8oxqhPGdEyGrda121GM4u0= +github.com/onflow/atree v0.8.0/go.mod h1:yccR+LR7xc1Jdic0mrjocbHvUD7lnVvg8/Ct1AA5zBo= github.com/onflow/boxo v0.0.0-20240201202436-f2477b92f483 h1:LpiQhTAfM9CAmNVEs0n//cBBgCg+vJSiIxTHYUklZ84= github.com/onflow/boxo v0.0.0-20240201202436-f2477b92f483/go.mod h1:pIZgTWdm3k3pLF9Uq6MB8JEcW07UDwNJjlXW1HELW80= -github.com/onflow/cadence v1.0.0-M3/go.mod h1:odXGZZ/wGNA5mwT8bC9v8u8EXACHllB2ABSZK65TGL8= -github.com/onflow/cadence v1.0.0-preview.52 h1:hZ92e6lL2+PQa3C1i5jJh0zZYFdW89+X1MS0Bkd6Ayo= -github.com/onflow/cadence v1.0.0-preview.52/go.mod h1:7wvvecnAZtYOspLOS3Lh+FuAmMeSrXhAWiycC3kQ1UU= -github.com/onflow/crypto v0.25.0/go.mod h1:C8FbaX0x8y+FxWjbkHy0Q4EASCDR9bSPWZqlpCLYyVI= +github.com/onflow/cadence v1.2.2 h1:LwigF/2lPiXlwX5rFn71KeMpmW5Iu/f/JtsPLLULBCc= +github.com/onflow/cadence v1.2.2/go.mod h1:PYX1xLejqswtDsQzN93x/VpfSKNyjUk6hrkc/mpv7xs= github.com/onflow/crypto v0.25.2 h1:GjHunqVt+vPcdqhxxhAXiMIF3YiLX7gTuTR5O+VG2ns= github.com/onflow/crypto v0.25.2/go.mod h1:fY7eLqUdMKV8EGOw301unP8h7PvLVy8/6gVR++/g0BY= github.com/onflow/flow v0.3.4 h1:FXUWVdYB90f/rjNcY0Owo30gL790tiYff9Pb/sycXYE= github.com/onflow/flow v0.3.4/go.mod h1:lzyAYmbu1HfkZ9cfnL5/sjrrsnJiUU8fRL26CqLP7+c= -github.com/onflow/flow-core-contracts/lib/go/contracts v1.3.1 h1:q9tXLIALwQ76bO4bmSrhtTkyc2cZF4/gH11ix9E3F5k= -github.com/onflow/flow-core-contracts/lib/go/contracts v1.3.1/go.mod h1:u/mkP/B+PbV33tEG3qfkhhBlydSvAKxfLZSfB4lsJHg= -github.com/onflow/flow-core-contracts/lib/go/templates v1.3.1 h1:FfhMBAb78p6VAWkJ+iqdKLErGQVQgxk5w6DP5ZruWX8= -github.com/onflow/flow-core-contracts/lib/go/templates v1.3.1/go.mod h1:NgbMOYnMh0GN48VsNKZuiwK7uyk38Wyo8jN9+C9QE30= -github.com/onflow/flow-ft/lib/go/contracts v1.0.0 h1:mToacZ5NWqtlWwk/7RgIl/jeKB/Sy/tIXdw90yKHcV0= -github.com/onflow/flow-ft/lib/go/contracts v1.0.0/go.mod h1:PwsL8fC81cjnUnTfmyL/HOIyHnyaw/JA474Wfj2tl6A= -github.com/onflow/flow-ft/lib/go/templates v1.0.0 h1:6cMS/lUJJ17HjKBfMO/eh0GGvnpElPgBXx7h5aoWJhs= -github.com/onflow/flow-ft/lib/go/templates v1.0.0/go.mod h1:uQ8XFqmMK2jxyBSVrmyuwdWjTEb+6zGjRYotfDJ5pAE= -github.com/onflow/flow-go-sdk v1.0.0-M1/go.mod h1:TDW0MNuCs4SvqYRUzkbRnRmHQL1h4X8wURsCw9P9beo= -github.com/onflow/flow-go-sdk v1.0.0-preview.55 h1:tUM8K7GcWltM0YSzei/g2Gq4z3BwGFTdpq2QwvB6ubk= -github.com/onflow/flow-go-sdk v1.0.0-preview.55/go.mod h1:rBRNboXaTprn7M0MeO6/R1bxNpctbrx66I2FLp0V6fM= -github.com/onflow/flow-nft/lib/go/contracts v1.2.1 h1:woAAS5z651sDpi7ihAHll8NvRS9uFXIXkL6xR+bKFZY= -github.com/onflow/flow-nft/lib/go/contracts v1.2.1/go.mod h1:2gpbza+uzs1k7x31hkpBPlggIRkI53Suo0n2AyA2HcE= -github.com/onflow/flow-nft/lib/go/templates v1.2.0 h1:JSQyh9rg0RC+D1930BiRXN8lrtMs+ubVMK6aQPon6Yc= -github.com/onflow/flow-nft/lib/go/templates v1.2.0/go.mod h1:p+2hRvtjLUR3MW1NsoJe5Gqgr2eeH49QB6+s6ze00w0= -github.com/onflow/flow/protobuf/go/flow v0.3.2-0.20231121210617-52ee94b830c2/go.mod h1:NA2pX2nw8zuaxfKphhKsk00kWLwfd+tv8mS23YXO4Sk= -github.com/onflow/flow/protobuf/go/flow v0.4.6 h1:KE/CsRVfyG5lGBtm1aNcjojMciQyS5GfPF3ixOWRfi0= -github.com/onflow/flow/protobuf/go/flow v0.4.6/go.mod h1:NA2pX2nw8zuaxfKphhKsk00kWLwfd+tv8mS23YXO4Sk= +github.com/onflow/flow-core-contracts/lib/go/contracts v1.4.0 h1:R86HaOuk6vpuECZnriEUE7bw9inC2AtdSn8lL/iwQLQ= +github.com/onflow/flow-core-contracts/lib/go/contracts v1.4.0/go.mod h1:9asTBnB6Tw2UlVVtQKyS/egYv3xr4zVlJnJ75z1dfac= +github.com/onflow/flow-core-contracts/lib/go/templates v1.4.0 h1:u2DAG8pk0xFH7TwS70t1gSZ/FtIIZWMSNyiu4SeXBYg= +github.com/onflow/flow-core-contracts/lib/go/templates v1.4.0/go.mod h1:pN768Al/wLRlf3bwugv9TyxniqJxMu4sxnX9eQJam64= +github.com/onflow/flow-ft/lib/go/contracts v1.0.1 h1:Ts5ob+CoCY2EjEd0W6vdLJ7hLL3SsEftzXG2JlmSe24= +github.com/onflow/flow-ft/lib/go/contracts v1.0.1/go.mod h1:PwsL8fC81cjnUnTfmyL/HOIyHnyaw/JA474Wfj2tl6A= +github.com/onflow/flow-ft/lib/go/templates v1.0.1 h1:FDYKAiGowABtoMNusLuRCILIZDtVqJ/5tYI4VkF5zfM= +github.com/onflow/flow-ft/lib/go/templates v1.0.1/go.mod h1:uQ8XFqmMK2jxyBSVrmyuwdWjTEb+6zGjRYotfDJ5pAE= +github.com/onflow/flow-go-sdk v1.2.3 h1:jb+0dIXBO12Zt8x3c2xDXYPv6k3sRTUvhe59M+EcXTI= +github.com/onflow/flow-go-sdk v1.2.3/go.mod h1:jMaffBTlAIdutx+pBhRIigLZFIBYSDDST0Uax1rW2qo= +github.com/onflow/flow-nft/lib/go/contracts v1.2.2 h1:XFERNVUDGbZ4ViZjt7P1cGD80mO1PzUJYPfdhXFsGbQ= +github.com/onflow/flow-nft/lib/go/contracts v1.2.2/go.mod h1:eZ9VMMNfCq0ho6kV25xJn1kXeCfxnkhj3MwF3ed08gY= +github.com/onflow/flow-nft/lib/go/templates v1.2.1 h1:SAALMZPDw9Eb9p5kSLnmnFxjyig1MLiT4JUlLp0/bSE= +github.com/onflow/flow-nft/lib/go/templates v1.2.1/go.mod h1:W6hOWU0xltPqNpv9gQX8Pj8Jtf0OmRxc1XX2V0kzJaI= +github.com/onflow/flow/protobuf/go/flow v0.4.7 h1:iP6DFx4wZ3ETORsyeqzHu7neFT3d1CXF6wdK+AOOjmc= +github.com/onflow/flow/protobuf/go/flow v0.4.7/go.mod h1:NA2pX2nw8zuaxfKphhKsk00kWLwfd+tv8mS23YXO4Sk= github.com/onflow/go-ethereum v1.14.7 h1:gg3awYqI02e3AypRdpJKEvNTJ6kz/OhAqRti0h54Wlc= github.com/onflow/go-ethereum v1.14.7/go.mod h1:zV14QLrXyYu5ucvcwHUA0r6UaqveqbXaehAVQJlSW+I= -github.com/onflow/nft-storefront/lib/go/contracts v1.0.0 h1:sxyWLqGm/p4EKT6DUlQESDG1ZNMN9GjPCm1gTq7NGfc= -github.com/onflow/nft-storefront/lib/go/contracts v1.0.0/go.mod h1:kMeq9zUwCrgrSojEbTUTTJpZ4WwacVm2pA7LVFr+glk= -github.com/onflow/sdks v0.5.1-0.20230912225508-b35402f12bba/go.mod h1:F0dj0EyHC55kknLkeD10js4mo14yTdMotnWMslPirrU= github.com/onflow/sdks v0.6.0-preview.1 h1:mb/cUezuqWEP1gFZNAgUI4boBltudv4nlfxke1KBp9k= github.com/onflow/sdks v0.6.0-preview.1/go.mod h1:F0dj0EyHC55kknLkeD10js4mo14yTdMotnWMslPirrU= github.com/onflow/wal v1.0.2 h1:5bgsJVf2O3cfMNK12fiiTyYZ8cOrUiELt3heBJfHOhc= github.com/onflow/wal v1.0.2/go.mod h1:iMC8gkLqu4nkbkAla5HkSBb+FGyQOZiWz3DYm2wSXCk= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= @@ -2227,7 +961,6 @@ github.com/opencontainers/runtime-spec v1.1.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/ github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/opentracing/opentracing-go v1.0.3-0.20180606204148-bd9c31933947/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= @@ -2238,7 +971,6 @@ github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnh github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/paulbellamy/ratecounter v0.2.0/go.mod h1:Hfx1hDpSGoqxkVVpBi/IlYD7kChlfo5C6hzIHwPqfFE= 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/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= @@ -2246,21 +978,12 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU= github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= -github.com/peterh/liner v1.0.1-0.20180619022028-8c1271fcf47f/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc= -github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= -github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= -github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY= -github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= -github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM= github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= 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/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ= -github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -2269,10 +992,7 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA= github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo= -github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= -github.com/pkg/term v0.0.0-20180730021639-bffc007b7fd5/go.mod h1:eCbImbZ95eXtAUIbLAuAVnBnwf83mjf6QIVH8SHYwqQ= -github.com/pkg/term v1.2.0-beta.2/go.mod h1:E25nymQcrSllhX42Ok8MRm1+hyBdHY0dCeiKZ9jpNGw= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/polydawn/refmt v0.89.0 h1:ADJTApkvkeBZsN0tBTx8QjpD9JkmxbKp0cxfr9qszm4= @@ -2280,7 +1000,6 @@ github.com/polydawn/refmt v0.89.0/go.mod h1:/zvteZs/GwLtCgZ4BL6CBsk9IKIlexP43ObX github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= -github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= @@ -2288,8 +1007,6 @@ github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDf github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.0/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk= github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= @@ -2298,9 +1015,6 @@ github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1: github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.1-0.20210607210712-147c58e9608a/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= -github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= -github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= @@ -2308,12 +1022,9 @@ github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7q github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= @@ -2324,12 +1035,9 @@ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.3.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/protolambda/bls12-381-util v0.0.0-20220416220906-d8552aa452c7/go.mod h1:IToEjHuttnUzwZI5KBSM/LOOW3qLbbrHOEfp3SbECGY= github.com/psiemens/sconfig v0.1.0 h1:xfWqW+TRpih7mXZIqKYTmpRhlZLQ1kbxV8EjllPv76s= github.com/psiemens/sconfig v0.1.0/go.mod h1:+MLKqdledP/8G3rOBpknbLh0IclCf4WneJUtS26JB2U= github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= @@ -2343,19 +1051,12 @@ github.com/quic-go/webtransport-go v0.6.0/go.mod h1:9KjU4AEBqEQidGHNDkZrb8CAa1ab github.com/raulk/go-watchdog v1.3.0 h1:oUmdlHxdkXRJlwfG0O9omj8ukerm8MEQavSiDTEtBsk= github.com/raulk/go-watchdog v1.3.0/go.mod h1:fIvOnLbF0b0ZwkB9YU4mOW9Did//4vPZtDqv66NfsMU= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= -github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -github.com/retailnext/hllpp v1.0.1-0.20180308014038-101a6d2f8b52/go.mod h1:RDpi1RftBQPUCDRw6SmxeaREsAaRKnOclghuzp/WRzc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRrjvIXnJho= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= 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.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= @@ -2368,20 +1069,12 @@ github.com/rs/zerolog v1.29.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6us github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= -github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/schollz/progressbar/v3 v3.13.1 h1:o8rySDYiQ59Mwzy2FELeHY5ZARXZTVJC7iHD6PEFUiE= github.com/schollz/progressbar/v3 v3.13.1/go.mod h1:xvrbki8kfT1fzWzBT/UZd9L6GA+jdL7HAgq2RFnO6fQ= -github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= -github.com/segmentio/kafka-go v0.2.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sethvargo/go-retry v0.2.3 h1:oYlgvIvsju3jNbottWABtbnoLC+GDtLdBHxKWxQm/iU= github.com/sethvargo/go-retry v0.2.3/go.mod h1:1afjQuvh7s4gflMObvjLPaWgluLLyhA1wmVZ6KLpICw= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= @@ -2415,7 +1108,6 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/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/slok/go-http-metrics v0.10.0 h1:rh0LaYEKza5eaYRGDXujKrOln57nHBi4TtVhmNEpbgM= @@ -2436,9 +1128,6 @@ github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasO github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= -github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= -github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= github.com/spf13/afero v1.10.0 h1:EaGW2JJh15aKOejeuJ+wpFSHnbd7GE6Wvp3TsNhb6LY= github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= @@ -2446,8 +1135,6 @@ github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= -github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= @@ -2461,7 +1148,6 @@ github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DM github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/spf13/viper v1.15.0 h1:js3yy885G8xwJa6iOISGFwd+qlUo5AvyXb7CiihdtiU= github.com/spf13/viper v1.15.0/go.mod h1:fFcTBJxvhhzSJiZy8n+PeW6t8l+KeT/uTARa0jHOQLA= -github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q= 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/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= @@ -2473,7 +1159,6 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS 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.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 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= @@ -2481,17 +1166,13 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 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.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -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.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= -github.com/supranational/blst v0.3.8-0.20220526154634-513d2456b344/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= 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.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= @@ -2499,13 +1180,9 @@ github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45 github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= github.com/texttheater/golang-levenshtein/levenshtein v0.0.0-20200805054039-cae8b0eaed6c h1:HelZ2kAFadG0La9d+4htN4HzQ68Bm2iM9qKMSMES6xg= github.com/texttheater/golang-levenshtein/levenshtein v0.0.0-20200805054039-cae8b0eaed6c/go.mod h1:JlzghshsemAMDGZLytTFY8C1JQxQPhnatWqNwUXjggo= -github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= -github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= -github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI= github.com/tklauser/go-sysconf v0.3.9/go.mod h1:11DU/5sG7UexIrp/O6g35hrWzu0JxlwQ3LSFUzyeuhs= 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.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM= github.com/tklauser/numcpus v0.3.0/go.mod h1:yFGUr7TUHQRAhyqBcEg0Ge34zDBAsIvJJcyE6boqnA8= github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= @@ -2513,7 +1190,6 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1 github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/turbolent/prettier v0.0.0-20220320183459-661cc755135d h1:5JInRQbk5UBX8JfUvKh2oYTLMVwj3p6n+wapDDm7hko= github.com/turbolent/prettier v0.0.0-20220320183459-661cc755135d/go.mod h1:Nlx5Y115XQvNcIdIy7dZXaNSUpzwBSge4/Ivk93/Yog= -github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs= 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/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= @@ -2527,16 +1203,6 @@ github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijb github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.10/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= -github.com/urfave/cli/v2 v2.10.2/go.mod h1:f8iq5LtQ/bLxafbdBSLPPNsgaW0l/2fYYEHhAyPlwvo= -github.com/urfave/cli/v2 v2.24.1/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc= -github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= -github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= -github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.6.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w= -github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= -github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= -github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= @@ -2552,26 +1218,15 @@ github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0/go.mod h1:x6AKhvS github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 h1:EKhdznlJHPMoKr0XTrX+IlJs1LH3lyx2nfr1dOlZ79k= github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1/go.mod h1:8UvriyWtv5Q5EOgjHaSseUEdkQfvwFv1I/In/O2M9gc= github.com/whyrusleeping/go-logging v0.0.0-20170515211332-0457bb6b88fc/go.mod h1:bopw91TMyo8J3tvftk8xmU2kPmlrt4nScJQZU2hE5EM= -github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= -github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= -github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= -github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= -github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI= -github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= -github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= -github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc= 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.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg= github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= @@ -2582,7 +1237,6 @@ github.com/zeebo/blake3 v0.2.3 h1:TFoLXsjeXqRNFxSbk35Dk4YtszE/MQQGK10BH4ptoTg= github.com/zeebo/blake3 v0.2.3/go.mod h1:mjJjZpnsyIVtVgTOSpJ9vmRE4wgDeyt2HU3qXvvKCaQ= github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo= github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= -github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= @@ -2602,26 +1256,19 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.4 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0/go.mod h1:r9vWsPS/3AQItv3OSlEJ/E4mbrhUbbw18meOjArPtKQ= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 h1:sv9kVfal0MK0wBMCOGr+HeJm9v803BkJxGrk2au7j08= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0/go.mod h1:SK2UL73Zy1quvRPonmOmRDiWk1KBV3LyIeeIxcEApWw= -go.opentelemetry.io/otel v1.8.0/go.mod h1:2pkj+iMj0o03Y+cW6/m8Y4WkRdYN3AvCXCnzRMp9yvM= -go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 h1:cl5P5/GIfFh4t6xyruOgJP5QiA1pw4fYYdv6nc6CBWw= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0/go.mod h1:zgBdWWAu7oEEMC06MMKc5NLbA/1YDXV1sMpSqEeLQLg= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 h1:tIqheXEFWAZ7O8A7m+J0aPTmpJN3YQ7qetUAdkkkKpk= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0/go.mod h1:nUeKExfxAQVbiVFn32YXpXZZHZ61Cc3s3Rn1pDBGAb0= -go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4= go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw= go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg= -go.opentelemetry.io/otel/trace v1.8.0/go.mod h1:0Bt3PXY8w+3pheS3hQUt+wow8b1ojPaTBoTCh2zIFI4= -go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= -go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -2631,14 +1278,11 @@ 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/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.uber.org/automaxprocs v1.5.2/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0= go.uber.org/dig v1.17.1 h1:Tga8Lz8PcYNsWsyHMZ1Vm0OQOUaJNDyvPImgbAu9YSc= go.uber.org/dig v1.17.1/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE= go.uber.org/fx v1.20.1 h1:zVwVQGS8zYvhh9Xxcu4w1M6ESyeMzebzj2NbSayZ4Mk= go.uber.org/fx v1.20.1/go.mod h1:iSYNbHf2y55acNCwCXKx7LbWb5WG1Bnue5RDXz1OREg= -go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= 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/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= @@ -2650,7 +1294,6 @@ go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9i go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.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.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= @@ -2669,40 +1312,19 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190909091759-094676da4a83/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200602180216-279210d13fed/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= -golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= -golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= -golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= -golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= -golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= -golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= -golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= -golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= +golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/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-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE= 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= @@ -2710,29 +1332,10 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0 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-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= -golang.org/x/exp v0.0.0-20220426173459-3bcf042a4bf5/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= -golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= -golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= -golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= -golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= -golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA= golang.org/x/exp v0.0.0-20240119083558-1b970713d09a/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= -golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= 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/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20210216034530-4410531fe030/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20210607152325-775e3b0c77b9/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= -golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= -golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= -golang.org/x/image v0.0.0-20220302094943-723b81ca9867/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= -golang.org/x/image v0.5.0/go.mod h1:FVC7BI/5Ym8R25iw5OLsgshdUBbT1h5jZTpA+mvAdZ4= -golang.org/x/image v0.6.0/go.mod h1:MXLdDR43H7cDJq5GEGXEVeeNhPgi+YYEQ2pC1byI1x0= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -2757,18 +1360,7 @@ 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.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= -golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 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= @@ -2786,7 +1378,6 @@ golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190227160552-c95aed5357e7/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-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190327091125-710a502c58a2/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= @@ -2797,7 +1388,6 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL 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-20190827160401-ba9fcec4b297/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= @@ -2815,55 +1405,26 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 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-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d/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-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= -golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ= -golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= -golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= -golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= -golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= -golang.org/x/oauth2 v0.0.0-20170207211851-4464e7848382/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -2885,24 +1446,9 @@ golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= -golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= -golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.0.0-20221006150949-b44042a4b9c1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec= -golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= -golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= -golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= -golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= -golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= -golang.org/x/oauth2 v0.11.0/go.mod h1:LdF7O/8bLR/qWK9DrpXmbHLTouvRHK0SgJl0GmDBchk= -golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0= golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ= golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA= golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= -golang.org/x/perf v0.0.0-20230113213139-801c7ef9e5c5/go.mod h1:UBKtEnL8aqnd+0JHqZ+2qoMDwtuy6cYhhKNoHLBiTQc= 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= @@ -2914,15 +1460,7 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ 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.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180810173357-98c5dad5d1a0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -2954,14 +1492,12 @@ golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7w 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-20191008105621-543471e840be/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-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200107162124-548cf772de50/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= @@ -2983,111 +1519,63 @@ golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200918174421-af09f7315aff/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/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-20201204225414-ed752295db88/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-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210304124612-50617c2ba197/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-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210420205809-ac73e9fd8988/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603125802-9665404d3644/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-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/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-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211020174200-9d6173849985/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211210111614-af8b64212486/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-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/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-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/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-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/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-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= -golang.org/x/sys v0.23.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/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= -golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= -golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo= -golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= -golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= -golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= -golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= -golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= -golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= -golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= +golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= +golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= 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= @@ -3098,44 +1586,24 @@ 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.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= -golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 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-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.3.0/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-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181221001348-537d06c36207/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-20190206041539-40960b6deb8e/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-20190327201419-c70d86f8b7cf/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-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= @@ -3145,21 +1613,17 @@ golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/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-20191108193012-7d206e10da11/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-20191126055441-b0650ceb63d9/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-20200108203644-89082a384178/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= @@ -3181,7 +1645,6 @@ golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc 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-20201124115921-2c860bdd6e78/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= @@ -3194,19 +1657,7 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.8-0.20211029000441-d6a9af8af023/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= -golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= -golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= -golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= -golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4= -golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= -golang.org/x/tools v0.10.0/go.mod h1:UJwyiVBsOA2uwvK/e5OY3GTpDUJriEd+/YlqAwLPmyM= -golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= -golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -3214,28 +1665,10 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T 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-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= -gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= -gonum.org/v1/gonum v0.0.0-20181121035319-3f7ecaa7e8ca/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= -gonum.org/v1/gonum v0.6.0/go.mod h1:9mxDZsDKxgMAuccQkewq682L+0eCu4dCN2yonUJTCLU= -gonum.org/v1/gonum v0.6.1/go.mod h1:9mxDZsDKxgMAuccQkewq682L+0eCu4dCN2yonUJTCLU= -gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= -gonum.org/v1/gonum v0.9.3/go.mod h1:TZumC3NeyVQskjXqmyWt4S3bINhy7B4eYwW69EbyX+0= -gonum.org/v1/gonum v0.11.0/go.mod h1:fSG4YDCxxUZQJ7rKsQrj0gMOg00Il0Z96/qMA4bVQhA= -gonum.org/v1/gonum v0.13.0/go.mod h1:/WPYRckkfWrhWefxyYTfrTtQR0KH4iyHNuzxqXAKyAU= gonum.org/v1/gonum v0.14.0 h1:2NiG67LD1tEH0D7kM+ps2V+fXmsAnpUeec7n8tcr4S0= gonum.org/v1/gonum v0.14.0/go.mod h1:AoWeoz0becf9QMWtE8iWXNXc27fK4fNeHNf/oMejGfU= -gonum.org/v1/netlib v0.0.0-20181029234149-ec6d1f5cefe6/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= -gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= -gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= -gonum.org/v1/plot v0.9.0/go.mod h1:3Pcqqmp6RHvJI72kgb8fThyUnav364FOsdDo2aGW5lY= -gonum.org/v1/plot v0.10.0/go.mod h1:JWIHJ7U20drSQb/aDpTetJzfC1KlAPldJLpkSy88dvQ= -gonum.org/v1/plot v0.10.1/go.mod h1:VZW5OlhkL1mysU9vaqNHnsy86inf6Ot+jB3r+BczCEo= -google.golang.org/api v0.0.0-20170206182103-3d017632ea10/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= @@ -3276,36 +1709,7 @@ google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/S google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= -google.golang.org/api v0.77.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw= -google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg= -google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o= -google.golang.org/api v0.85.0/go.mod h1:AqZf8Ep9uZ2pyTvgL+x0D3Zt0eoT9b5E8fmzfu6FO2g= -google.golang.org/api v0.90.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= -google.golang.org/api v0.93.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= -google.golang.org/api v0.95.0/go.mod h1:eADj+UBuxkh5zlrSntJghuNeg8HwQ1w5lTKkuqaETEI= -google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= -google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= -google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= -google.golang.org/api v0.99.0/go.mod h1:1YOf74vkVndF7pG6hIHuINsM7eWwpVTAfNMNiL91A08= -google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70= -google.golang.org/api v0.102.0/go.mod h1:3VFl6/fzoA+qNuS1N1/VfXY4LjoXN/wzeIp7TweWwGo= -google.golang.org/api v0.103.0/go.mod h1:hGtW6nK1AC+d9si/UBhw8Xli+QMOf6xyNAyJw4qU9w0= -google.golang.org/api v0.106.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= -google.golang.org/api v0.107.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= -google.golang.org/api v0.108.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= -google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60ca7iI= -google.golang.org/api v0.111.0/go.mod h1:qtFHvU9mhgTJegR31csQ+rwxyUTHOKFqCKWp1J0fdw0= -google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg= -google.golang.org/api v0.118.0/go.mod h1:76TtD3vkgmZ66zZzp72bUUklpmQmKlhh6sYtIjYK+5E= -google.golang.org/api v0.122.0/go.mod h1:gcitW0lvnyWjSp9nKxAbdHKIZ6vF4aajGueeslZOyms= -google.golang.org/api v0.124.0/go.mod h1:xu2HQurE5gi/3t1aFCvhPD781p0a3p11sdunTJ2BlP4= -google.golang.org/api v0.125.0/go.mod h1:mBwVAtz+87bEN6CbA1GtZPDOqY2R5ONPqJeIlvyo4Aw= -google.golang.org/api v0.126.0/go.mod h1:mBwVAtz+87bEN6CbA1GtZPDOqY2R5ONPqJeIlvyo4Aw= -google.golang.org/api v0.128.0/go.mod h1:Y611qgqaE92On/7g65MQgxYul3c0rEB894kniWLY750= -google.golang.org/api v0.139.0/go.mod h1:CVagp6Eekz9CjGZ718Z+sloknzkDJE7Vc1Ckj9+viBk= -google.golang.org/api v0.149.0/go.mod h1:Mwn1B7JTXrzXtnvmzQE2BD6bYZQ8DShKZDZbeN9I7qI= -google.golang.org/api v0.151.0/go.mod h1:ccy+MJ6nrYFgE3WgRx/AMXOxOmU8Q4hSa+jjibzhxcg= google.golang.org/api v0.162.0 h1:Vhs54HkaEpkMBdgGdOT2P6F0csGG/vxDS0hWHJzmmps= google.golang.org/api v0.162.0/go.mod h1:6SulDkfoBIg4NFmCuZ39XeeAgSHCPecfSUuDyYlAHs0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= @@ -3319,7 +1723,6 @@ google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCID google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= -google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= @@ -3330,7 +1733,6 @@ google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRn 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-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= -google.golang.org/genproto v0.0.0-20190716160619-c506a9f90610/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 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= @@ -3338,7 +1740,6 @@ google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvx 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-20200108215221-bd8f9a0ef82f/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= @@ -3399,130 +1800,21 @@ google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2 google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= -google.golang.org/genproto v0.0.0-20220329172620-7be39ac1afc7/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220722212130-b98a9ff5e252/go.mod h1:GkXuJDJ6aQ7lnJcRF+SJVgFdQhypqgl3LB1C9vabdRE= -google.golang.org/genproto v0.0.0-20220801145646-83ce21fca29f/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc= -google.golang.org/genproto v0.0.0-20220815135757-37a418bb8959/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220817144833-d7fd3f11b9b1/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220829144015-23454907ede3/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220913154956-18f8339a66a5/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220914142337-ca0e39ece12f/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220915135415-7fd63a7952de/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220916172020-2692e8806bfa/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220919141832-68c03719ef51/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006/go.mod h1:ht8XFiar2npT/g4vkk7O0WYS1sHOHbdujxbEp7CJWbw= -google.golang.org/genproto v0.0.0-20220926165614-551eb538f295/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= -google.golang.org/genproto v0.0.0-20220926220553-6981cbe3cfce/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= -google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqwhZAwq4wsRUaVG555sVgsNmIjRtO7t/JH29U= -google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= -google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= -google.golang.org/genproto v0.0.0-20221024153911-1573dae28c9c/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= -google.golang.org/genproto v0.0.0-20221024183307-1bc688fe9f3e/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= -google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c/go.mod h1:CGI5F/G+E5bKwmfYo09AXuVN4dD894kIKUFmVbP2/Fo= -google.golang.org/genproto v0.0.0-20221109142239-94d6d90a7d66/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221114212237-e4508ebdbee1/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221117204609-8f9c96812029/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221201204527-e3fa12d562f3/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd/go.mod h1:cTsE614GARnxrLsqKREzmNYJACSWWpAWdNMwnD7c2BE= -google.golang.org/genproto v0.0.0-20221207170731-23e4bf6bdc37/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230112194545-e10362b5ecf9/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230113154510-dbe35b8444a5/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230123190316-2c411cf9d197/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230124163310-31e0e69b6fc2/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230125152338-dcaf20b6aeaa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230127162408-596548ed4efa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230216225411-c8e22ba71e44/go.mod h1:8B0gmkoRebU8ukX6HP+4wrVQUY1+6PkQ44BSyIlflHA= -google.golang.org/genproto v0.0.0-20230222225845-10f96fb3dbec/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= -google.golang.org/genproto v0.0.0-20230223222841-637eb2293923/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= -google.golang.org/genproto v0.0.0-20230303212802-e74f57abe488/go.mod h1:TvhZT5f700eVlTNwND1xoEZQeWTB2RY/65kplwl/bFA= -google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= -google.golang.org/genproto v0.0.0-20230320184635-7606e756e683/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= -google.golang.org/genproto v0.0.0-20230323212658-478b75c54725/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= -google.golang.org/genproto v0.0.0-20230330154414-c0448cd141ea/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= -google.golang.org/genproto v0.0.0-20230331144136-dcfb400f0633/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= -google.golang.org/genproto v0.0.0-20230403163135-c38d8f061ccd/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= -google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= -google.golang.org/genproto v0.0.0-20230525234025-438c736192d0/go.mod h1:9ExIQyXL5hZrHzQceCwuSYwZZ5QZBazOcprJ5rgs3lY= -google.golang.org/genproto v0.0.0-20230526161137-0005af68ea54/go.mod h1:zqTuNwFlFRsw5zIts5VnzLQxSRqh+CGOTVMlYbY0Eyk= -google.golang.org/genproto v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:zqTuNwFlFRsw5zIts5VnzLQxSRqh+CGOTVMlYbY0Eyk= -google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:xZnkP7mREFX5MORlOPEzLMr+90PPZQ2QWzrVTWfAq64= -google.golang.org/genproto v0.0.0-20230629202037-9506855d4529/go.mod h1:xZnkP7mREFX5MORlOPEzLMr+90PPZQ2QWzrVTWfAq64= -google.golang.org/genproto v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:O9kGHb51iE/nOGvQaDUuadVYqovW56s5emA88lQnj6Y= -google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98/go.mod h1:S7mY02OqCJTD0E1OiQy1F72PWFB4bZJ87cAtLPYgDR0= -google.golang.org/genproto v0.0.0-20230726155614-23370e0ffb3e/go.mod h1:0ggbjUrZYpy1q+ANUS30SEoGZ53cdfwtbuG7Ptgy108= -google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5/go.mod h1:oH/ZOT02u4kWEp7oYBGYFFkCdKS/uYR9Z7+0/xuuFp8= -google.golang.org/genproto v0.0.0-20230821184602-ccc8af3d0e93/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4= -google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4= -google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4= -google.golang.org/genproto v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:CCviP9RmpZ1mxVr8MUjCnSiY09IbAXZxhLE6EhHIdPU= -google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97/go.mod h1:t1VqOqqvce95G3hIDCT5FeO3YUc6Q4Oe24L/+rNMxRk= -google.golang.org/genproto v0.0.0-20231012201019-e917dd12ba7a/go.mod h1:EMfReVxb80Dq1hhioy0sOsY9jCE46YDgHlJ7fWVUWRE= -google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:CgAqfJo+Xmu0GwA0411Ht3OU3OntXwsGmrmjI8ioGXI= google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY= google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo= -google.golang.org/genproto/googleapis/api v0.0.0-20230525234020-1aefcd67740a/go.mod h1:ts19tUU+Z0ZShN1y3aPyq2+O3d5FUNNgT6FtOzmrNn8= -google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= -google.golang.org/genproto/googleapis/api v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= -google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= -google.golang.org/genproto/googleapis/api v0.0.0-20230629202037-9506855d4529/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= -google.golang.org/genproto/googleapis/api v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:mPBs5jNgx2GuQGvFwUvVKqtn6HsUw9nP64BedgvqEsQ= -google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ= -google.golang.org/genproto/googleapis/api v0.0.0-20230726155614-23370e0ffb3e/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ= -google.golang.org/genproto/googleapis/api v0.0.0-20230803162519-f966b187b2e5/go.mod h1:5DZzOUPCLYL3mNkQ0ms0F3EuUNZ7py1Bqeq6sxzI7/Q= -google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk= -google.golang.org/genproto/googleapis/api v0.0.0-20230913181813-007df8e322eb/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk= -google.golang.org/genproto/googleapis/api v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:RdyHbowztCGQySiCvQPgWQWgWhGnouTdCflKoDBt32U= -google.golang.org/genproto/googleapis/api v0.0.0-20231002182017-d307bd883b97/go.mod h1:iargEX0SFPm3xcfMI0d1domjg0ZF4Aa0p2awqyxhvF0= -google.golang.org/genproto/googleapis/api v0.0.0-20231012201019-e917dd12ba7a/go.mod h1:SUBoKXbI1Efip18FClrQVGjWcyd0QZd8KkvdP34t7ww= -google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:IBQ646DjkDkvUIsVq/cc03FUFQ9wbZu7yE396YcL870= google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de h1:jFNzHPIeuzhdRwVhbZdiym9q0ory/xY3sA+v2wPg8I0= google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:5iCWqnniDlqZHrd3neWVTOwvh/v6s3232omMecelax8= -google.golang.org/genproto/googleapis/bytestream v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:ylj+BE99M198VPbBh6A8d9n3w8fChvyLK3wwBOjXBFA= -google.golang.org/genproto/googleapis/bytestream v0.0.0-20230807174057-1744710a1577/go.mod h1:NjCQG/D8JandXxM57PZbAJL1DCNL6EypA0vPPwfsc7c= -google.golang.org/genproto/googleapis/bytestream v0.0.0-20231030173426-d783a09b4405/go.mod h1:GRUCuLdzVqZte8+Dl/D4N25yLzcGqqWaYkeVOwulFqw= google.golang.org/genproto/googleapis/bytestream v0.0.0-20240125205218-1f4bbc51befe h1:weYsP+dNijSQVoLAb5bpUos3ciBpNU/NEVlHFKrk8pg= google.golang.org/genproto/googleapis/bytestream v0.0.0-20240125205218-1f4bbc51befe/go.mod h1:SCz6T5xjNXM4QFPRwxHcfChp7V+9DcXR3ay2TkHR8Tg= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234015-3fc162c6f38a/go.mod h1:xURIpW9ES5+/GZhnV6beoEtxQrnkRGIfP5VQG2tCBLc= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230629202037-9506855d4529/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:8mL13HKkDa+IuJ8yruA3ci0q+0vsUz4m//+ottjwS5o= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230731190214-cbb8c96f2d6d/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230803162519-f966b187b2e5/go.mod h1:zBEcrKX2ZOcEkHWxBPAIvYUWOKKMIhYcmNiUIu2ji3I= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230920183334-c177e329c48b/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:KSqppvjFjtoCI+KGd4PELB0qLNxdJHRGqRI09mB6pQA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97/go.mod h1:v7nGkzlmW8P3n/bKmWBn2WpBjpOEx8Q6gMueudAmKfY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231012201019-e917dd12ba7a/go.mod h1:4cYg8o5yUbm77w8ZX00LhMVNl/YVBFJRYWDc0uYWMs0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:swOH3j0KzcDDgGUWr+SNpyTen5YrXjS3eyPzFYKc6lc= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405/go.mod h1:67X1fPuzjcrkymZzZV1vvkFeTn2Rvc6lYF9MYFGCcwE= google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de h1:cZGRis4/ot9uVm639a+rHCUaG0JJHEsdyzSQTMX+suY= google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY= -google.golang.org/grpc v0.0.0-20170208002647-2a6bf6142e96/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= -google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= @@ -3557,28 +1849,9 @@ google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnD google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= -google.golang.org/grpc v1.52.0/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY= -google.golang.org/grpc v1.52.3/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY= -google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= -google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= -google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= -google.golang.org/grpc v1.56.1/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= -google.golang.org/grpc v1.56.2/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= -google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= -google.golang.org/grpc v1.58.2/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= -google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= -google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= @@ -3598,18 +1871,12 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 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-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/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/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= @@ -3617,16 +1884,11 @@ 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/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= -gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= 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/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= -gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= -gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98= 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/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= @@ -3642,7 +1904,6 @@ 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.0/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= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= @@ -3656,78 +1917,14 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh 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= -honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= -lukechampine.com/blake3 v1.1.7/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA= -lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE= lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= -lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= -lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= -modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= -modernc.org/cc/v3 v3.36.2/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= -modernc.org/cc/v3 v3.36.3/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= -modernc.org/cc/v3 v3.37.0/go.mod h1:vtL+3mdHx/wcj3iEGz84rQa8vEqR6XM84v5Lcvfph20= -modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0= -modernc.org/ccgo/v3 v3.0.0-20220428102840-41399a37e894/go.mod h1:eI31LL8EwEBKPpNpA4bU1/i+sKOwOrQy8D87zWUcRZc= -modernc.org/ccgo/v3 v3.0.0-20220430103911-bc99d88307be/go.mod h1:bwdAnOoaIt8Ax9YdWGjxWsdkPcZyRPHqrOvJxaKAKGw= -modernc.org/ccgo/v3 v3.0.0-20220904174949-82d86e1b6d56/go.mod h1:YSXjPL62P2AMSxBphRHPn7IkzhVHqkvOnRKAKh+W6ZI= -modernc.org/ccgo/v3 v3.16.4/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= -modernc.org/ccgo/v3 v3.16.6/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= -modernc.org/ccgo/v3 v3.16.8/go.mod h1:zNjwkizS+fIFDrDjIAgBSCLkWbJuHF+ar3QRn+Z9aws= -modernc.org/ccgo/v3 v3.16.9/go.mod h1:zNMzC9A9xeNUepy6KuZBbugn3c0Mc9TeiJO4lgvkJDo= -modernc.org/ccgo/v3 v3.16.13-0.20221017192402-261537637ce8/go.mod h1:fUB3Vn0nVPReA+7IG7yZDfjv1TMWjhQP8gCxrFAtL5g= -modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY= -modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= -modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= -modernc.org/libc v0.0.0-20220428101251-2d5f3daf273b/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= -modernc.org/libc v1.16.0/go.mod h1:N4LD6DBE9cf+Dzf9buBlzVJndKr/iJHG97vGLHYnb5A= -modernc.org/libc v1.16.1/go.mod h1:JjJE0eu4yeK7tab2n4S1w8tlWd9MxXLRzheaRnAKymU= -modernc.org/libc v1.16.17/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU= -modernc.org/libc v1.16.19/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= -modernc.org/libc v1.17.0/go.mod h1:XsgLldpP4aWlPlsjqKRdHPqCxCjISdHfM/yeWC5GyW0= -modernc.org/libc v1.17.1/go.mod h1:FZ23b+8LjxZs7XtFMbSzL/EhPxNbfZbErxEHc7cbD9s= -modernc.org/libc v1.17.4/go.mod h1:WNg2ZH56rDEwdropAJeZPQkXmDwh+JCA1s/htl6r2fA= -modernc.org/libc v1.18.0/go.mod h1:vj6zehR5bfc98ipowQOM2nIDUZnVew/wNC/2tOGS+q0= -modernc.org/libc v1.20.3/go.mod h1:ZRfIaEkgrYgZDl6pa4W39HgN5G/yDW+NRmNKZBDFrk0= -modernc.org/libc v1.21.4/go.mod h1:przBsL5RDOZajTVslkugzLBj1evTue36jEomFQOoYuI= -modernc.org/libc v1.22.2/go.mod h1:uvQavJ1pZ0hIoC/jfqNoMLURIMhKzINIWypNM17puug= -modernc.org/libc v1.37.6 h1:orZH3c5wmhIQFTXF+Nt+eeauyd+ZIt2BX6ARe+kD+aw= -modernc.org/libc v1.37.6/go.mod h1:YAXkAZ8ktnkCKaN9sw/UDeUVkGYJ/YquGO4FTi5nmHE= -modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= -modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= -modernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= -modernc.org/memory v1.2.0/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= -modernc.org/memory v1.2.1/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= -modernc.org/memory v1.3.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= -modernc.org/memory v1.4.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= -modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= -modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E= -modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E= -modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/sqlite v1.18.1/go.mod h1:6ho+Gow7oX5V+OiOQ6Tr4xeqbx13UZ6t+Fw9IRUG4d4= -modernc.org/sqlite v1.18.2/go.mod h1:kvrTLEWgxUcHa2GfHBQtanR1H9ht3hTJNtKpzH9k1u0= -modernc.org/sqlite v1.28.0 h1:Zx+LyDDmXczNnEQdvPuEfcFVA2ZPyaD7UCZDjef3BHQ= -modernc.org/sqlite v1.28.0/go.mod h1:Qxpazz0zH8Z1xCFyi5GSL3FzbtZ3fvbjmywNogldEW0= -modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= -modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= -modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw= -modernc.org/tcl v1.13.2/go.mod h1:7CLiGIPo1M8Rv1Mitpv5akc2+8fxUd2y2UzC/MfMzy0= -modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= -modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= -modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= -modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8= nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g= nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= -pgregory.net/rapid v0.4.7/go.mod h1:UYpPVyjFHzYBGHIxLFoupi8vwk6rXNzRY9OMvVxFIOU= pgregory.net/rapid v1.1.0 h1:CMa0sjHSru3puNx+J0MIAuiiEV4N0qj8/cMWGBBCsjw= pgregory.net/rapid v1.1.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= 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= diff --git a/insecure/corruptlibp2p/pubsub_adapter_config.go b/insecure/corruptlibp2p/pubsub_adapter_config.go index adc3337d629..e3343f2dce8 100644 --- a/insecure/corruptlibp2p/pubsub_adapter_config.go +++ b/insecure/corruptlibp2p/pubsub_adapter_config.go @@ -1,6 +1,8 @@ package corruptlibp2p import ( + "time" + pb "github.com/libp2p/go-libp2p-pubsub/pb" "github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/core/routing" @@ -157,6 +159,10 @@ func (c *CorruptPubSubAdapterConfig) WithRpcInspector(_ p2p.GossipSubRPCInspecto // CorruptPubSub does not support inspector suite. This is a no-op. } +func (c *CorruptPubSubAdapterConfig) WithPeerGater(_ map[string]float64, _ time.Duration) { + // CorruptPubSub does not need peer gater. This is a no-op. +} + func (c *CorruptPubSubAdapterConfig) Build() []corrupt.Option { return c.options } diff --git a/insecure/go.mod b/insecure/go.mod index 0086182c027..2690e0f7796 100644 --- a/insecure/go.mod +++ b/insecure/go.mod @@ -202,16 +202,16 @@ require ( github.com/multiformats/go-varint v0.0.7 // indirect github.com/nxadm/tail v1.4.8 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect - github.com/onflow/atree v0.8.0-rc.6 // indirect - github.com/onflow/cadence v1.0.0-preview.52 // indirect - github.com/onflow/flow-core-contracts/lib/go/contracts v1.3.1 // indirect - github.com/onflow/flow-core-contracts/lib/go/templates v1.3.1 // indirect - github.com/onflow/flow-ft/lib/go/contracts v1.0.0 // indirect - github.com/onflow/flow-ft/lib/go/templates v1.0.0 // indirect - github.com/onflow/flow-go-sdk v1.0.0-preview.55 // indirect - github.com/onflow/flow-nft/lib/go/contracts v1.2.1 // indirect - github.com/onflow/flow-nft/lib/go/templates v1.2.0 // indirect - github.com/onflow/flow/protobuf/go/flow v0.4.6 // indirect + github.com/onflow/atree v0.8.0 // indirect + github.com/onflow/cadence v1.2.2 // indirect + github.com/onflow/flow-core-contracts/lib/go/contracts v1.4.0 // indirect + github.com/onflow/flow-core-contracts/lib/go/templates v1.4.0 // indirect + github.com/onflow/flow-ft/lib/go/contracts v1.0.1 // indirect + github.com/onflow/flow-ft/lib/go/templates v1.0.1 // indirect + github.com/onflow/flow-go-sdk v1.2.3 // indirect + github.com/onflow/flow-nft/lib/go/contracts v1.2.2 // indirect + github.com/onflow/flow-nft/lib/go/templates v1.2.1 // indirect + github.com/onflow/flow/protobuf/go/flow v0.4.7 // indirect github.com/onflow/go-ethereum v1.14.7 // indirect github.com/onflow/sdks v0.6.0-preview.1 // indirect github.com/onflow/wal v1.0.2 // indirect @@ -282,15 +282,15 @@ require ( go.uber.org/mock v0.4.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.26.0 // indirect - golang.org/x/crypto v0.26.0 // indirect + golang.org/x/crypto v0.28.0 // indirect golang.org/x/exp v0.0.0-20240119083558-1b970713d09a // indirect golang.org/x/mod v0.17.0 // indirect golang.org/x/net v0.25.0 // indirect golang.org/x/oauth2 v0.17.0 // indirect golang.org/x/sync v0.8.0 // indirect - golang.org/x/sys v0.23.0 // indirect - golang.org/x/term v0.23.0 // indirect - golang.org/x/text v0.17.0 // indirect + golang.org/x/sys v0.26.0 // indirect + golang.org/x/term v0.25.0 // indirect + golang.org/x/text v0.19.0 // indirect golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect diff --git a/insecure/go.sum b/insecure/go.sum index ab6a5936284..8e25d6b07da 100644 --- a/insecure/go.sum +++ b/insecure/go.sum @@ -1,17 +1,14 @@ -cloud.google.com/go v0.0.0-20170206221025-ce650573d812/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.31.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.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.43.0/go.mod h1:BOSR3VbTLkk6FDC/TcffxP4NF/FFBGA5ku+jvKOP7pg= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.51.0/go.mod h1:hWtGJ6gnXH+KgDv+V0zFGDvpi07n3z8ZNj3T1RW0Gcw= 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= @@ -22,960 +19,48 @@ cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHOb cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= -cloud.google.com/go 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 v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= -cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= -cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= -cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= -cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= -cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= -cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= -cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= -cloud.google.com/go v0.100.1/go.mod h1:fs4QogzfH5n2pBXBP9vRiU+eCny7lD2vmFZy79Iuw1U= -cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= -cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= -cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU= -cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA= -cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM= -cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I= -cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY= -cloud.google.com/go v0.110.2/go.mod h1:k04UEeEtb6ZBRTv3dZz4CeJC3jKGxyhl0sAiVVquxiw= -cloud.google.com/go v0.110.4/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI= -cloud.google.com/go v0.110.6/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI= -cloud.google.com/go v0.110.7/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI= -cloud.google.com/go v0.110.8/go.mod h1:Iz8AkXJf1qmxC3Oxoep8R1T36w8B92yU29PcBhHO5fk= cloud.google.com/go v0.112.0 h1:tpFCD7hpHFlQ8yPwT3x+QeXqc2T6+n6T+hmABHfDUSM= cloud.google.com/go v0.112.0/go.mod h1:3jEEVwZ/MHU4djK5t5RHuKOA/GbLddgTdVubX1qnPD4= -cloud.google.com/go/accessapproval v1.4.0/go.mod h1:zybIuC3KpDOvotz59lFe5qxRZx6C75OtwbisN56xYB4= -cloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw= -cloud.google.com/go/accessapproval v1.6.0/go.mod h1:R0EiYnwV5fsRFiKZkPHr6mwyk2wxUJ30nL4j2pcFY2E= -cloud.google.com/go/accessapproval v1.7.1/go.mod h1:JYczztsHRMK7NTXb6Xw+dwbs/WnOJxbo/2mTI+Kgg68= -cloud.google.com/go/accessapproval v1.7.2/go.mod h1:/gShiq9/kK/h8T/eEn1BTzalDvk0mZxJlhfw0p+Xuc0= -cloud.google.com/go/accesscontextmanager v1.3.0/go.mod h1:TgCBehyr5gNMz7ZaH9xubp+CE8dkrszb4oK9CWyvD4o= -cloud.google.com/go/accesscontextmanager v1.4.0/go.mod h1:/Kjh7BBu/Gh83sv+K60vN9QE5NJcd80sU33vIe2IFPE= -cloud.google.com/go/accesscontextmanager v1.6.0/go.mod h1:8XCvZWfYw3K/ji0iVnp+6pu7huxoQTLmxAbVjbloTtM= -cloud.google.com/go/accesscontextmanager v1.7.0/go.mod h1:CEGLewx8dwa33aDAZQujl7Dx+uYhS0eay198wB/VumQ= -cloud.google.com/go/accesscontextmanager v1.8.0/go.mod h1:uI+AI/r1oyWK99NN8cQ3UK76AMelMzgZCvJfsi2c+ps= -cloud.google.com/go/accesscontextmanager v1.8.1/go.mod h1:JFJHfvuaTC+++1iL1coPiG1eu5D24db2wXCDWDjIrxo= -cloud.google.com/go/accesscontextmanager v1.8.2/go.mod h1:E6/SCRM30elQJ2PKtFMs2YhfJpZSNcJyejhuzoId4Zk= -cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw= -cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY= -cloud.google.com/go/aiplatform v1.27.0/go.mod h1:Bvxqtl40l0WImSb04d0hXFU7gDOiq9jQmorivIiWcKg= -cloud.google.com/go/aiplatform v1.35.0/go.mod h1:7MFT/vCaOyZT/4IIFfxH4ErVg/4ku6lKv3w0+tFTgXQ= -cloud.google.com/go/aiplatform v1.36.1/go.mod h1:WTm12vJRPARNvJ+v6P52RDHCNe4AhvjcIZ/9/RRHy/k= -cloud.google.com/go/aiplatform v1.37.0/go.mod h1:IU2Cv29Lv9oCn/9LkFiiuKfwrRTq+QQMbW+hPCxJGZw= -cloud.google.com/go/aiplatform v1.45.0/go.mod h1:Iu2Q7sC7QGhXUeOhAj/oCK9a+ULz1O4AotZiqjQ8MYA= -cloud.google.com/go/aiplatform v1.48.0/go.mod h1:Iu2Q7sC7QGhXUeOhAj/oCK9a+ULz1O4AotZiqjQ8MYA= -cloud.google.com/go/aiplatform v1.50.0/go.mod h1:IRc2b8XAMTa9ZmfJV1BCCQbieWWvDnP1A8znyz5N7y4= -cloud.google.com/go/aiplatform v1.51.0/go.mod h1:IRc2b8XAMTa9ZmfJV1BCCQbieWWvDnP1A8znyz5N7y4= -cloud.google.com/go/aiplatform v1.51.1/go.mod h1:kY3nIMAVQOK2XDqDPHaOuD9e+FdMA6OOpfBjsvaFSOo= -cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI= -cloud.google.com/go/analytics v0.12.0/go.mod h1:gkfj9h6XRf9+TS4bmuhPEShsh3hH8PAZzm/41OOhQd4= -cloud.google.com/go/analytics v0.17.0/go.mod h1:WXFa3WSym4IZ+JiKmavYdJwGG/CvpqiqczmL59bTD9M= -cloud.google.com/go/analytics v0.18.0/go.mod h1:ZkeHGQlcIPkw0R/GW+boWHhCOR43xz9RN/jn7WcqfIE= -cloud.google.com/go/analytics v0.19.0/go.mod h1:k8liqf5/HCnOUkbawNtrWWc+UAzyDlW89doe8TtoDsE= -cloud.google.com/go/analytics v0.21.2/go.mod h1:U8dcUtmDmjrmUTnnnRnI4m6zKn/yaA5N9RlEkYFHpQo= -cloud.google.com/go/analytics v0.21.3/go.mod h1:U8dcUtmDmjrmUTnnnRnI4m6zKn/yaA5N9RlEkYFHpQo= -cloud.google.com/go/analytics v0.21.4/go.mod h1:zZgNCxLCy8b2rKKVfC1YkC2vTrpfZmeRCySM3aUbskA= -cloud.google.com/go/apigateway v1.3.0/go.mod h1:89Z8Bhpmxu6AmUxuVRg/ECRGReEdiP3vQtk4Z1J9rJk= -cloud.google.com/go/apigateway v1.4.0/go.mod h1:pHVY9MKGaH9PQ3pJ4YLzoj6U5FUDeDFBllIz7WmzJoc= -cloud.google.com/go/apigateway v1.5.0/go.mod h1:GpnZR3Q4rR7LVu5951qfXPJCHquZt02jf7xQx7kpqN8= -cloud.google.com/go/apigateway v1.6.1/go.mod h1:ufAS3wpbRjqfZrzpvLC2oh0MFlpRJm2E/ts25yyqmXA= -cloud.google.com/go/apigateway v1.6.2/go.mod h1:CwMC90nnZElorCW63P2pAYm25AtQrHfuOkbRSHj0bT8= -cloud.google.com/go/apigeeconnect v1.3.0/go.mod h1:G/AwXFAKo0gIXkPTVfZDd2qA1TxBXJ3MgMRBQkIi9jc= -cloud.google.com/go/apigeeconnect v1.4.0/go.mod h1:kV4NwOKqjvt2JYR0AoIWo2QGfoRtn/pkS3QlHp0Ni04= -cloud.google.com/go/apigeeconnect v1.5.0/go.mod h1:KFaCqvBRU6idyhSNyn3vlHXc8VMDJdRmwDF6JyFRqZ8= -cloud.google.com/go/apigeeconnect v1.6.1/go.mod h1:C4awq7x0JpLtrlQCr8AzVIzAaYgngRqWf9S5Uhg+wWs= -cloud.google.com/go/apigeeconnect v1.6.2/go.mod h1:s6O0CgXT9RgAxlq3DLXvG8riw8PYYbU/v25jqP3Dy18= -cloud.google.com/go/apigeeregistry v0.4.0/go.mod h1:EUG4PGcsZvxOXAdyEghIdXwAEi/4MEaoqLMLDMIwKXY= -cloud.google.com/go/apigeeregistry v0.5.0/go.mod h1:YR5+s0BVNZfVOUkMa5pAR2xGd0A473vA5M7j247o1wM= -cloud.google.com/go/apigeeregistry v0.6.0/go.mod h1:BFNzW7yQVLZ3yj0TKcwzb8n25CFBri51GVGOEUcgQsc= -cloud.google.com/go/apigeeregistry v0.7.1/go.mod h1:1XgyjZye4Mqtw7T9TsY4NW10U7BojBvG4RMD+vRDrIw= -cloud.google.com/go/apigeeregistry v0.7.2/go.mod h1:9CA2B2+TGsPKtfi3F7/1ncCCsL62NXBRfM6iPoGSM+8= -cloud.google.com/go/apikeys v0.4.0/go.mod h1:XATS/yqZbaBK0HOssf+ALHp8jAlNHUgyfprvNcBIszU= -cloud.google.com/go/apikeys v0.5.0/go.mod h1:5aQfwY4D+ewMMWScd3hm2en3hCj+BROlyrt3ytS7KLI= -cloud.google.com/go/apikeys v0.6.0/go.mod h1:kbpXu5upyiAlGkKrJgQl8A0rKNNJ7dQ377pdroRSSi8= -cloud.google.com/go/appengine v1.4.0/go.mod h1:CS2NhuBuDXM9f+qscZ6V86m1MIIqPj3WC/UoEuR1Sno= -cloud.google.com/go/appengine v1.5.0/go.mod h1:TfasSozdkFI0zeoxW3PTBLiNqRmzraodCWatWI9Dmak= -cloud.google.com/go/appengine v1.6.0/go.mod h1:hg6i0J/BD2cKmDJbaFSYHFyZkgBEfQrDg/X0V5fJn84= -cloud.google.com/go/appengine v1.7.0/go.mod h1:eZqpbHFCqRGa2aCdope7eC0SWLV1j0neb/QnMJVWx6A= -cloud.google.com/go/appengine v1.7.1/go.mod h1:IHLToyb/3fKutRysUlFO0BPt5j7RiQ45nrzEJmKTo6E= -cloud.google.com/go/appengine v1.8.1/go.mod h1:6NJXGLVhZCN9aQ/AEDvmfzKEfoYBlfB80/BHiKVputY= -cloud.google.com/go/appengine v1.8.2/go.mod h1:WMeJV9oZ51pvclqFN2PqHoGnys7rK0rz6s3Mp6yMvDo= -cloud.google.com/go/area120 v0.5.0/go.mod h1:DE/n4mp+iqVyvxHN41Vf1CR602GiHQjFPusMFW6bGR4= -cloud.google.com/go/area120 v0.6.0/go.mod h1:39yFJqWVgm0UZqWTOdqkLhjoC7uFfgXRC8g/ZegeAh0= -cloud.google.com/go/area120 v0.7.0/go.mod h1:a3+8EUD1SX5RUcCs3MY5YasiO1z6yLiNLRiFrykbynY= -cloud.google.com/go/area120 v0.7.1/go.mod h1:j84i4E1RboTWjKtZVWXPqvK5VHQFJRF2c1Nm69pWm9k= -cloud.google.com/go/area120 v0.8.1/go.mod h1:BVfZpGpB7KFVNxPiQBuHkX6Ed0rS51xIgmGyjrAfzsg= -cloud.google.com/go/area120 v0.8.2/go.mod h1:a5qfo+x77SRLXnCynFWPUZhnZGeSgvQ+Y0v1kSItkh4= -cloud.google.com/go/artifactregistry v1.6.0/go.mod h1:IYt0oBPSAGYj/kprzsBjZ/4LnG/zOcHyFHjWPCi6SAQ= -cloud.google.com/go/artifactregistry v1.7.0/go.mod h1:mqTOFOnGZx8EtSqK/ZWcsm/4U8B77rbcLP6ruDU2Ixk= -cloud.google.com/go/artifactregistry v1.8.0/go.mod h1:w3GQXkJX8hiKN0v+at4b0qotwijQbYUqF2GWkZzAhC0= -cloud.google.com/go/artifactregistry v1.9.0/go.mod h1:2K2RqvA2CYvAeARHRkLDhMDJ3OXy26h3XW+3/Jh2uYc= -cloud.google.com/go/artifactregistry v1.11.1/go.mod h1:lLYghw+Itq9SONbCa1YWBoWs1nOucMH0pwXN1rOBZFI= -cloud.google.com/go/artifactregistry v1.11.2/go.mod h1:nLZns771ZGAwVLzTX/7Al6R9ehma4WUEhZGWV6CeQNQ= -cloud.google.com/go/artifactregistry v1.12.0/go.mod h1:o6P3MIvtzTOnmvGagO9v/rOjjA0HmhJ+/6KAXrmYDCI= -cloud.google.com/go/artifactregistry v1.13.0/go.mod h1:uy/LNfoOIivepGhooAUpL1i30Hgee3Cu0l4VTWHUC08= -cloud.google.com/go/artifactregistry v1.14.1/go.mod h1:nxVdG19jTaSTu7yA7+VbWL346r3rIdkZ142BSQqhn5E= -cloud.google.com/go/artifactregistry v1.14.2/go.mod h1:Xk+QbsKEb0ElmyeMfdHAey41B+qBq3q5R5f5xD4XT3U= -cloud.google.com/go/artifactregistry v1.14.3/go.mod h1:A2/E9GXnsyXl7GUvQ/2CjHA+mVRoWAXC0brg2os+kNI= -cloud.google.com/go/asset v1.5.0/go.mod h1:5mfs8UvcM5wHhqtSv8J1CtxxaQq3AdBxxQi2jGW/K4o= -cloud.google.com/go/asset v1.7.0/go.mod h1:YbENsRK4+xTiL+Ofoj5Ckf+O17kJtgp3Y3nn4uzZz5s= -cloud.google.com/go/asset v1.8.0/go.mod h1:mUNGKhiqIdbr8X7KNayoYvyc4HbbFO9URsjbytpUaW0= -cloud.google.com/go/asset v1.9.0/go.mod h1:83MOE6jEJBMqFKadM9NLRcs80Gdw76qGuHn8m3h8oHQ= -cloud.google.com/go/asset v1.10.0/go.mod h1:pLz7uokL80qKhzKr4xXGvBQXnzHn5evJAEAtZiIb0wY= -cloud.google.com/go/asset v1.11.1/go.mod h1:fSwLhbRvC9p9CXQHJ3BgFeQNM4c9x10lqlrdEUYXlJo= -cloud.google.com/go/asset v1.12.0/go.mod h1:h9/sFOa4eDIyKmH6QMpm4eUK3pDojWnUhTgJlk762Hg= -cloud.google.com/go/asset v1.13.0/go.mod h1:WQAMyYek/b7NBpYq/K4KJWcRqzoalEsxz/t/dTk4THw= -cloud.google.com/go/asset v1.14.1/go.mod h1:4bEJ3dnHCqWCDbWJ/6Vn7GVI9LerSi7Rfdi03hd+WTQ= -cloud.google.com/go/asset v1.15.0/go.mod h1:tpKafV6mEut3+vN9ScGvCHXHj7FALFVta+okxFECHcg= -cloud.google.com/go/asset v1.15.1/go.mod h1:yX/amTvFWRpp5rcFq6XbCxzKT8RJUam1UoboE179jU4= -cloud.google.com/go/assuredworkloads v1.5.0/go.mod h1:n8HOZ6pff6re5KYfBXcFvSViQjDwxFkAkmUFffJRbbY= -cloud.google.com/go/assuredworkloads v1.6.0/go.mod h1:yo2YOk37Yc89Rsd5QMVECvjaMKymF9OP+QXWlKXUkXw= -cloud.google.com/go/assuredworkloads v1.7.0/go.mod h1:z/736/oNmtGAyU47reJgGN+KVoYoxeLBoj4XkKYscNI= -cloud.google.com/go/assuredworkloads v1.8.0/go.mod h1:AsX2cqyNCOvEQC8RMPnoc0yEarXQk6WEKkxYfL6kGIo= -cloud.google.com/go/assuredworkloads v1.9.0/go.mod h1:kFuI1P78bplYtT77Tb1hi0FMxM0vVpRC7VVoJC3ZoT0= -cloud.google.com/go/assuredworkloads v1.10.0/go.mod h1:kwdUQuXcedVdsIaKgKTp9t0UJkE5+PAVNhdQm4ZVq2E= -cloud.google.com/go/assuredworkloads v1.11.1/go.mod h1:+F04I52Pgn5nmPG36CWFtxmav6+7Q+c5QyJoL18Lry0= -cloud.google.com/go/assuredworkloads v1.11.2/go.mod h1:O1dfr+oZJMlE6mw0Bp0P1KZSlj5SghMBvTpZqIcUAW4= -cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0= -cloud.google.com/go/automl v1.6.0/go.mod h1:ugf8a6Fx+zP0D59WLhqgTDsQI9w07o64uf/Is3Nh5p8= -cloud.google.com/go/automl v1.7.0/go.mod h1:RL9MYCCsJEOmt0Wf3z9uzG0a7adTT1fe+aObgSpkCt8= -cloud.google.com/go/automl v1.8.0/go.mod h1:xWx7G/aPEe/NP+qzYXktoBSDfjO+vnKMGgsApGJJquM= -cloud.google.com/go/automl v1.12.0/go.mod h1:tWDcHDp86aMIuHmyvjuKeeHEGq76lD7ZqfGLN6B0NuU= -cloud.google.com/go/automl v1.13.1/go.mod h1:1aowgAHWYZU27MybSCFiukPO7xnyawv7pt3zK4bheQE= -cloud.google.com/go/automl v1.13.2/go.mod h1:gNY/fUmDEN40sP8amAX3MaXkxcqPIn7F1UIIPZpy4Mg= -cloud.google.com/go/baremetalsolution v0.3.0/go.mod h1:XOrocE+pvK1xFfleEnShBlNAXf+j5blPPxrhjKgnIFc= -cloud.google.com/go/baremetalsolution v0.4.0/go.mod h1:BymplhAadOO/eBa7KewQ0Ppg4A4Wplbn+PsFKRLo0uI= -cloud.google.com/go/baremetalsolution v0.5.0/go.mod h1:dXGxEkmR9BMwxhzBhV0AioD0ULBmuLZI8CdwalUxuss= -cloud.google.com/go/baremetalsolution v1.1.1/go.mod h1:D1AV6xwOksJMV4OSlWHtWuFNZZYujJknMAP4Qa27QIA= -cloud.google.com/go/baremetalsolution v1.2.0/go.mod h1:68wi9AwPYkEWIUT4SvSGS9UJwKzNpshjHsH4lzk8iOw= -cloud.google.com/go/baremetalsolution v1.2.1/go.mod h1:3qKpKIw12RPXStwQXcbhfxVj1dqQGEvcmA+SX/mUR88= -cloud.google.com/go/batch v0.3.0/go.mod h1:TR18ZoAekj1GuirsUsR1ZTKN3FC/4UDnScjT8NXImFE= -cloud.google.com/go/batch v0.4.0/go.mod h1:WZkHnP43R/QCGQsZ+0JyG4i79ranE2u8xvjq/9+STPE= -cloud.google.com/go/batch v0.7.0/go.mod h1:vLZN95s6teRUqRQ4s3RLDsH8PvboqBK+rn1oevL159g= -cloud.google.com/go/batch v1.3.1/go.mod h1:VguXeQKXIYaeeIYbuozUmBR13AfL4SJP7IltNPS+A4A= -cloud.google.com/go/batch v1.4.1/go.mod h1:KdBmDD61K0ovcxoRHGrN6GmOBWeAOyCgKD0Mugx4Fkk= -cloud.google.com/go/batch v1.5.0/go.mod h1:KdBmDD61K0ovcxoRHGrN6GmOBWeAOyCgKD0Mugx4Fkk= -cloud.google.com/go/batch v1.5.1/go.mod h1:RpBuIYLkQu8+CWDk3dFD/t/jOCGuUpkpX+Y0n1Xccs8= -cloud.google.com/go/beyondcorp v0.2.0/go.mod h1:TB7Bd+EEtcw9PCPQhCJtJGjk/7TC6ckmnSFS+xwTfm4= -cloud.google.com/go/beyondcorp v0.3.0/go.mod h1:E5U5lcrcXMsCuoDNyGrpyTm/hn7ne941Jz2vmksAxW8= -cloud.google.com/go/beyondcorp v0.4.0/go.mod h1:3ApA0mbhHx6YImmuubf5pyW8srKnCEPON32/5hj+RmM= -cloud.google.com/go/beyondcorp v0.5.0/go.mod h1:uFqj9X+dSfrheVp7ssLTaRHd2EHqSL4QZmH4e8WXGGU= -cloud.google.com/go/beyondcorp v0.6.1/go.mod h1:YhxDWw946SCbmcWo3fAhw3V4XZMSpQ/VYfcKGAEU8/4= -cloud.google.com/go/beyondcorp v1.0.0/go.mod h1:YhxDWw946SCbmcWo3fAhw3V4XZMSpQ/VYfcKGAEU8/4= -cloud.google.com/go/beyondcorp v1.0.1/go.mod h1:zl/rWWAFVeV+kx+X2Javly7o1EIQThU4WlkynffL/lk= 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/bigquery v1.42.0/go.mod h1:8dRTJxhtG+vwBKzE5OseQn/hiydoQN3EedCaOdYmxRA= -cloud.google.com/go/bigquery v1.43.0/go.mod h1:ZMQcXHsl+xmU1z36G2jNGZmKp9zNY5BUua5wDgmNCfw= -cloud.google.com/go/bigquery v1.44.0/go.mod h1:0Y33VqXTEsbamHJvJHdFmtqHvMIY28aK1+dFsvaChGc= -cloud.google.com/go/bigquery v1.47.0/go.mod h1:sA9XOgy0A8vQK9+MWhEQTY6Tix87M/ZurWFIxmF9I/E= -cloud.google.com/go/bigquery v1.48.0/go.mod h1:QAwSz+ipNgfL5jxiaK7weyOhzdoAy1zFm0Nf1fysJac= -cloud.google.com/go/bigquery v1.49.0/go.mod h1:Sv8hMmTFFYBlt/ftw2uN6dFdQPzBlREY9yBh7Oy7/4Q= -cloud.google.com/go/bigquery v1.50.0/go.mod h1:YrleYEh2pSEbgTBZYMJ5SuSr0ML3ypjRB1zgf7pvQLU= -cloud.google.com/go/bigquery v1.52.0/go.mod h1:3b/iXjRQGU4nKa87cXeg6/gogLjO8C6PmuM8i5Bi/u4= -cloud.google.com/go/bigquery v1.53.0/go.mod h1:3b/iXjRQGU4nKa87cXeg6/gogLjO8C6PmuM8i5Bi/u4= -cloud.google.com/go/bigquery v1.55.0/go.mod h1:9Y5I3PN9kQWuid6183JFhOGOW3GcirA5LpsKCUn+2ec= -cloud.google.com/go/bigquery v1.56.0/go.mod h1:KDcsploXTEY7XT3fDQzMUZlpQLHzE4itubHrnmhUrZA= -cloud.google.com/go/bigtable v1.2.0/go.mod h1:JcVAOl45lrTmQfLj7T6TxyMzIN/3FGGcFm+2xVAli2o= -cloud.google.com/go/billing v1.4.0/go.mod h1:g9IdKBEFlItS8bTtlrZdVLWSSdSyFUZKXNS02zKMOZY= -cloud.google.com/go/billing v1.5.0/go.mod h1:mztb1tBc3QekhjSgmpf/CV4LzWXLzCArwpLmP2Gm88s= -cloud.google.com/go/billing v1.6.0/go.mod h1:WoXzguj+BeHXPbKfNWkqVtDdzORazmCjraY+vrxcyvI= -cloud.google.com/go/billing v1.7.0/go.mod h1:q457N3Hbj9lYwwRbnlD7vUpyjq6u5U1RAOArInEiD5Y= -cloud.google.com/go/billing v1.12.0/go.mod h1:yKrZio/eu+okO/2McZEbch17O5CB5NpZhhXG6Z766ss= -cloud.google.com/go/billing v1.13.0/go.mod h1:7kB2W9Xf98hP9Sr12KfECgfGclsH3CQR0R08tnRlRbc= -cloud.google.com/go/billing v1.16.0/go.mod h1:y8vx09JSSJG02k5QxbycNRrN7FGZB6F3CAcgum7jvGA= -cloud.google.com/go/billing v1.17.0/go.mod h1:Z9+vZXEq+HwH7bhJkyI4OQcR6TSbeMrjlpEjO2vzY64= -cloud.google.com/go/billing v1.17.1/go.mod h1:Z9+vZXEq+HwH7bhJkyI4OQcR6TSbeMrjlpEjO2vzY64= -cloud.google.com/go/billing v1.17.2/go.mod h1:u/AdV/3wr3xoRBk5xvUzYMS1IawOAPwQMuHgHMdljDg= -cloud.google.com/go/binaryauthorization v1.1.0/go.mod h1:xwnoWu3Y84jbuHa0zd526MJYmtnVXn0syOjaJgy4+dM= -cloud.google.com/go/binaryauthorization v1.2.0/go.mod h1:86WKkJHtRcv5ViNABtYMhhNWRrD1Vpi//uKEy7aYEfI= -cloud.google.com/go/binaryauthorization v1.3.0/go.mod h1:lRZbKgjDIIQvzYQS1p99A7/U1JqvqeZg0wiI5tp6tg0= -cloud.google.com/go/binaryauthorization v1.4.0/go.mod h1:tsSPQrBd77VLplV70GUhBf/Zm3FsKmgSqgm4UmiDItk= -cloud.google.com/go/binaryauthorization v1.5.0/go.mod h1:OSe4OU1nN/VswXKRBmciKpo9LulY41gch5c68htf3/Q= -cloud.google.com/go/binaryauthorization v1.6.1/go.mod h1:TKt4pa8xhowwffiBmbrbcxijJRZED4zrqnwZ1lKH51U= -cloud.google.com/go/binaryauthorization v1.7.0/go.mod h1:Zn+S6QqTMn6odcMU1zDZCJxPjU2tZPV1oDl45lWY154= -cloud.google.com/go/binaryauthorization v1.7.1/go.mod h1:GTAyfRWYgcbsP3NJogpV3yeunbUIjx2T9xVeYovtURE= -cloud.google.com/go/certificatemanager v1.3.0/go.mod h1:n6twGDvcUBFu9uBgt4eYvvf3sQ6My8jADcOVwHmzadg= -cloud.google.com/go/certificatemanager v1.4.0/go.mod h1:vowpercVFyqs8ABSmrdV+GiFf2H/ch3KyudYQEMM590= -cloud.google.com/go/certificatemanager v1.6.0/go.mod h1:3Hh64rCKjRAX8dXgRAyOcY5vQ/fE1sh8o+Mdd6KPgY8= -cloud.google.com/go/certificatemanager v1.7.1/go.mod h1:iW8J3nG6SaRYImIa+wXQ0g8IgoofDFRp5UMzaNk1UqI= -cloud.google.com/go/certificatemanager v1.7.2/go.mod h1:15SYTDQMd00kdoW0+XY5d9e+JbOPjp24AvF48D8BbcQ= -cloud.google.com/go/channel v1.8.0/go.mod h1:W5SwCXDJsq/rg3tn3oG0LOxpAo6IMxNa09ngphpSlnk= -cloud.google.com/go/channel v1.9.0/go.mod h1:jcu05W0my9Vx4mt3/rEHpfxc9eKi9XwsdDL8yBMbKUk= -cloud.google.com/go/channel v1.11.0/go.mod h1:IdtI0uWGqhEeatSB62VOoJ8FSUhJ9/+iGkJVqp74CGE= -cloud.google.com/go/channel v1.12.0/go.mod h1:VkxCGKASi4Cq7TbXxlaBezonAYpp1GCnKMY6tnMQnLU= -cloud.google.com/go/channel v1.16.0/go.mod h1:eN/q1PFSl5gyu0dYdmxNXscY/4Fi7ABmeHCJNf/oHmc= -cloud.google.com/go/channel v1.17.0/go.mod h1:RpbhJsGi/lXWAUM1eF4IbQGbsfVlg2o8Iiy2/YLfVT0= -cloud.google.com/go/channel v1.17.1/go.mod h1:xqfzcOZAcP4b/hUDH0GkGg1Sd5to6di1HOJn/pi5uBQ= -cloud.google.com/go/cloudbuild v1.3.0/go.mod h1:WequR4ULxlqvMsjDEEEFnOG5ZSRSgWOywXYDb1vPE6U= -cloud.google.com/go/cloudbuild v1.4.0/go.mod h1:5Qwa40LHiOXmz3386FrjrYM93rM/hdRr7b53sySrTqA= -cloud.google.com/go/cloudbuild v1.6.0/go.mod h1:UIbc/w9QCbH12xX+ezUsgblrWv+Cv4Tw83GiSMHOn9M= -cloud.google.com/go/cloudbuild v1.7.0/go.mod h1:zb5tWh2XI6lR9zQmsm1VRA+7OCuve5d8S+zJUul8KTg= -cloud.google.com/go/cloudbuild v1.9.0/go.mod h1:qK1d7s4QlO0VwfYn5YuClDGg2hfmLZEb4wQGAbIgL1s= -cloud.google.com/go/cloudbuild v1.10.1/go.mod h1:lyJg7v97SUIPq4RC2sGsz/9tNczhyv2AjML/ci4ulzU= -cloud.google.com/go/cloudbuild v1.13.0/go.mod h1:lyJg7v97SUIPq4RC2sGsz/9tNczhyv2AjML/ci4ulzU= -cloud.google.com/go/cloudbuild v1.14.0/go.mod h1:lyJg7v97SUIPq4RC2sGsz/9tNczhyv2AjML/ci4ulzU= -cloud.google.com/go/cloudbuild v1.14.1/go.mod h1:K7wGc/3zfvmYWOWwYTgF/d/UVJhS4pu+HAy7PL7mCsU= -cloud.google.com/go/clouddms v1.3.0/go.mod h1:oK6XsCDdW4Ib3jCCBugx+gVjevp2TMXFtgxvPSee3OM= -cloud.google.com/go/clouddms v1.4.0/go.mod h1:Eh7sUGCC+aKry14O1NRljhjyrr0NFC0G2cjwX0cByRk= -cloud.google.com/go/clouddms v1.5.0/go.mod h1:QSxQnhikCLUw13iAbffF2CZxAER3xDGNHjsTAkQJcQA= -cloud.google.com/go/clouddms v1.6.1/go.mod h1:Ygo1vL52Ov4TBZQquhz5fiw2CQ58gvu+PlS6PVXCpZI= -cloud.google.com/go/clouddms v1.7.0/go.mod h1:MW1dC6SOtI/tPNCciTsXtsGNEM0i0OccykPvv3hiYeM= -cloud.google.com/go/clouddms v1.7.1/go.mod h1:o4SR8U95+P7gZ/TX+YbJxehOCsM+fe6/brlrFquiszk= -cloud.google.com/go/cloudtasks v1.5.0/go.mod h1:fD92REy1x5woxkKEkLdvavGnPJGEn8Uic9nWuLzqCpY= -cloud.google.com/go/cloudtasks v1.6.0/go.mod h1:C6Io+sxuke9/KNRkbQpihnW93SWDU3uXt92nu85HkYI= -cloud.google.com/go/cloudtasks v1.7.0/go.mod h1:ImsfdYWwlWNJbdgPIIGJWC+gemEGTBK/SunNQQNCAb4= -cloud.google.com/go/cloudtasks v1.8.0/go.mod h1:gQXUIwCSOI4yPVK7DgTVFiiP0ZW/eQkydWzwVMdHxrI= -cloud.google.com/go/cloudtasks v1.9.0/go.mod h1:w+EyLsVkLWHcOaqNEyvcKAsWp9p29dL6uL9Nst1cI7Y= -cloud.google.com/go/cloudtasks v1.10.0/go.mod h1:NDSoTLkZ3+vExFEWu2UJV1arUyzVDAiZtdWcsUyNwBs= -cloud.google.com/go/cloudtasks v1.11.1/go.mod h1:a9udmnou9KO2iulGscKR0qBYjreuX8oHwpmFsKspEvM= -cloud.google.com/go/cloudtasks v1.12.1/go.mod h1:a9udmnou9KO2iulGscKR0qBYjreuX8oHwpmFsKspEvM= -cloud.google.com/go/cloudtasks v1.12.2/go.mod h1:A7nYkjNlW2gUoROg1kvJrQGhJP/38UaWwsnuBDOBVUk= -cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= -cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= -cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= -cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= -cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= -cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= -cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU= -cloud.google.com/go/compute v1.12.0/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= -cloud.google.com/go/compute v1.12.1/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= -cloud.google.com/go/compute v1.13.0/go.mod h1:5aPTS0cUNMIc1CE546K+Th6weJUNQErARyZtRXDJ8GE= -cloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvjxega5vAdo= -cloud.google.com/go/compute v1.15.1/go.mod h1:bjjoF/NtFUrkD/urWfdHaKuOPDR5nWIs63rR+SXhcpA= -cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs= -cloud.google.com/go/compute v1.19.0/go.mod h1:rikpw2y+UMidAe9tISo04EHNOIf42RLYF/q8Bs93scU= -cloud.google.com/go/compute v1.19.1/go.mod h1:6ylj3a05WF8leseCdIf77NK0g1ey+nj5IKd5/kvShxE= -cloud.google.com/go/compute v1.19.3/go.mod h1:qxvISKp/gYnXkSAD1ppcSOveRAmzxicEv/JlizULFrI= -cloud.google.com/go/compute v1.20.1/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= -cloud.google.com/go/compute v1.21.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= -cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= -cloud.google.com/go/compute v1.23.1/go.mod h1:CqB3xpmPKKt3OJpW2ndFIXnA9A4xAy/F3Xp1ixncW78= cloud.google.com/go/compute v1.24.0 h1:phWcR2eWzRJaL/kOiJwfFsPs4BaKq1j6vnpZrc1YlVg= cloud.google.com/go/compute v1.24.0/go.mod h1:kw1/T+h/+tK2LJK0wiPPx1intgdAM3j/g3hFDlscY40= -cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU= -cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= -cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= -cloud.google.com/go/contactcenterinsights v1.3.0/go.mod h1:Eu2oemoePuEFc/xKFPjbTuPSj0fYJcPls9TFlPNnHHY= -cloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck= -cloud.google.com/go/contactcenterinsights v1.6.0/go.mod h1:IIDlT6CLcDoyv79kDv8iWxMSTZhLxSCofVV5W6YFM/w= -cloud.google.com/go/contactcenterinsights v1.9.1/go.mod h1:bsg/R7zGLYMVxFFzfh9ooLTruLRCG9fnzhH9KznHhbM= -cloud.google.com/go/contactcenterinsights v1.10.0/go.mod h1:bsg/R7zGLYMVxFFzfh9ooLTruLRCG9fnzhH9KznHhbM= -cloud.google.com/go/contactcenterinsights v1.11.0/go.mod h1:hutBdImE4XNZ1NV4vbPJKSFOnQruhC5Lj9bZqWMTKiU= -cloud.google.com/go/contactcenterinsights v1.11.1/go.mod h1:FeNP3Kg8iteKM80lMwSk3zZZKVxr+PGnAId6soKuXwE= -cloud.google.com/go/container v1.6.0/go.mod h1:Xazp7GjJSeUYo688S+6J5V+n/t+G5sKBTFkKNudGRxg= -cloud.google.com/go/container v1.7.0/go.mod h1:Dp5AHtmothHGX3DwwIHPgq45Y8KmNsgN3amoYfxVkLo= -cloud.google.com/go/container v1.13.1/go.mod h1:6wgbMPeQRw9rSnKBCAJXnds3Pzj03C4JHamr8asWKy4= -cloud.google.com/go/container v1.14.0/go.mod h1:3AoJMPhHfLDxLvrlVWaK57IXzaPnLaZq63WX59aQBfM= -cloud.google.com/go/container v1.15.0/go.mod h1:ft+9S0WGjAyjDggg5S06DXj+fHJICWg8L7isCQe9pQA= -cloud.google.com/go/container v1.22.1/go.mod h1:lTNExE2R7f+DLbAN+rJiKTisauFCaoDq6NURZ83eVH4= -cloud.google.com/go/container v1.24.0/go.mod h1:lTNExE2R7f+DLbAN+rJiKTisauFCaoDq6NURZ83eVH4= -cloud.google.com/go/container v1.26.0/go.mod h1:YJCmRet6+6jnYYRS000T6k0D0xUXQgBSaJ7VwI8FBj4= -cloud.google.com/go/container v1.26.1/go.mod h1:5smONjPRUxeEpDG7bMKWfDL4sauswqEtnBK1/KKpR04= -cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I= -cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4= -cloud.google.com/go/containeranalysis v0.7.0/go.mod h1:9aUL+/vZ55P2CXfuZjS4UjQ9AgXoSw8Ts6lemfmxBxI= -cloud.google.com/go/containeranalysis v0.9.0/go.mod h1:orbOANbwk5Ejoom+s+DUCTTJ7IBdBQJDcSylAx/on9s= -cloud.google.com/go/containeranalysis v0.10.1/go.mod h1:Ya2jiILITMY68ZLPaogjmOMNkwsDrWBSTyBubGXO7j0= -cloud.google.com/go/containeranalysis v0.11.0/go.mod h1:4n2e99ZwpGxpNcz+YsFT1dfOHPQFGcAC8FN2M2/ne/U= -cloud.google.com/go/containeranalysis v0.11.1/go.mod h1:rYlUOM7nem1OJMKwE1SadufX0JP3wnXj844EtZAwWLY= -cloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSiyOzm8kpLq0a0= -cloud.google.com/go/datacatalog v1.5.0/go.mod h1:M7GPLNQeLfWqeIm3iuiruhPzkt65+Bx8dAKvScX8jvs= -cloud.google.com/go/datacatalog v1.6.0/go.mod h1:+aEyF8JKg+uXcIdAmmaMUmZ3q1b/lKLtXCmXdnc0lbc= -cloud.google.com/go/datacatalog v1.7.0/go.mod h1:9mEl4AuDYWw81UGc41HonIHH7/sn52H0/tc8f8ZbZIE= -cloud.google.com/go/datacatalog v1.8.0/go.mod h1:KYuoVOv9BM8EYz/4eMFxrr4DUKhGIOXxZoKYF5wdISM= -cloud.google.com/go/datacatalog v1.8.1/go.mod h1:RJ58z4rMp3gvETA465Vg+ag8BGgBdnRPEMMSTr5Uv+M= -cloud.google.com/go/datacatalog v1.12.0/go.mod h1:CWae8rFkfp6LzLumKOnmVh4+Zle4A3NXLzVJ1d1mRm0= -cloud.google.com/go/datacatalog v1.13.0/go.mod h1:E4Rj9a5ZtAxcQJlEBTLgMTphfP11/lNaAshpoBgemX8= -cloud.google.com/go/datacatalog v1.14.0/go.mod h1:h0PrGtlihoutNMp/uvwhawLQ9+c63Kz65UFqh49Yo+E= -cloud.google.com/go/datacatalog v1.14.1/go.mod h1:d2CevwTG4yedZilwe+v3E3ZBDRMobQfSG/a6cCCN5R4= -cloud.google.com/go/datacatalog v1.16.0/go.mod h1:d2CevwTG4yedZilwe+v3E3ZBDRMobQfSG/a6cCCN5R4= -cloud.google.com/go/datacatalog v1.17.1/go.mod h1:nCSYFHgtxh2MiEktWIz71s/X+7ds/UT9kp0PC7waCzE= -cloud.google.com/go/datacatalog v1.18.0/go.mod h1:nCSYFHgtxh2MiEktWIz71s/X+7ds/UT9kp0PC7waCzE= -cloud.google.com/go/datacatalog v1.18.1/go.mod h1:TzAWaz+ON1tkNr4MOcak8EBHX7wIRX/gZKM+yTVsv+A= -cloud.google.com/go/dataflow v0.6.0/go.mod h1:9QwV89cGoxjjSR9/r7eFDqqjtvbKxAK2BaYU6PVk9UM= -cloud.google.com/go/dataflow v0.7.0/go.mod h1:PX526vb4ijFMesO1o202EaUmouZKBpjHsTlCtB4parQ= -cloud.google.com/go/dataflow v0.8.0/go.mod h1:Rcf5YgTKPtQyYz8bLYhFoIV/vP39eL7fWNcSOyFfLJE= -cloud.google.com/go/dataflow v0.9.1/go.mod h1:Wp7s32QjYuQDWqJPFFlnBKhkAtiFpMTdg00qGbnIHVw= -cloud.google.com/go/dataflow v0.9.2/go.mod h1:vBfdBZ/ejlTaYIGB3zB4T08UshH70vbtZeMD+urnUSo= -cloud.google.com/go/dataform v0.3.0/go.mod h1:cj8uNliRlHpa6L3yVhDOBrUXH+BPAO1+KFMQQNSThKo= -cloud.google.com/go/dataform v0.4.0/go.mod h1:fwV6Y4Ty2yIFL89huYlEkwUPtS7YZinZbzzj5S9FzCE= -cloud.google.com/go/dataform v0.5.0/go.mod h1:GFUYRe8IBa2hcomWplodVmUx/iTL0FrsauObOM3Ipr0= -cloud.google.com/go/dataform v0.6.0/go.mod h1:QPflImQy33e29VuapFdf19oPbE4aYTJxr31OAPV+ulA= -cloud.google.com/go/dataform v0.7.0/go.mod h1:7NulqnVozfHvWUBpMDfKMUESr+85aJsC/2O0o3jWPDE= -cloud.google.com/go/dataform v0.8.1/go.mod h1:3BhPSiw8xmppbgzeBbmDvmSWlwouuJkXsXsb8UBih9M= -cloud.google.com/go/dataform v0.8.2/go.mod h1:X9RIqDs6NbGPLR80tnYoPNiO1w0wenKTb8PxxlhTMKM= -cloud.google.com/go/datafusion v1.4.0/go.mod h1:1Zb6VN+W6ALo85cXnM1IKiPw+yQMKMhB9TsTSRDo/38= -cloud.google.com/go/datafusion v1.5.0/go.mod h1:Kz+l1FGHB0J+4XF2fud96WMmRiq/wj8N9u007vyXZ2w= -cloud.google.com/go/datafusion v1.6.0/go.mod h1:WBsMF8F1RhSXvVM8rCV3AeyWVxcC2xY6vith3iw3S+8= -cloud.google.com/go/datafusion v1.7.1/go.mod h1:KpoTBbFmoToDExJUso/fcCiguGDk7MEzOWXUsJo0wsI= -cloud.google.com/go/datafusion v1.7.2/go.mod h1:62K2NEC6DRlpNmI43WHMWf9Vg/YvN6QVi8EVwifElI0= -cloud.google.com/go/datalabeling v0.5.0/go.mod h1:TGcJ0G2NzcsXSE/97yWjIZO0bXj0KbVlINXMG9ud42I= -cloud.google.com/go/datalabeling v0.6.0/go.mod h1:WqdISuk/+WIGeMkpw/1q7bK/tFEZxsrFJOJdY2bXvTQ= -cloud.google.com/go/datalabeling v0.7.0/go.mod h1:WPQb1y08RJbmpM3ww0CSUAGweL0SxByuW2E+FU+wXcM= -cloud.google.com/go/datalabeling v0.8.1/go.mod h1:XS62LBSVPbYR54GfYQsPXZjTW8UxCK2fkDciSrpRFdY= -cloud.google.com/go/datalabeling v0.8.2/go.mod h1:cyDvGHuJWu9U/cLDA7d8sb9a0tWLEletStu2sTmg3BE= -cloud.google.com/go/dataplex v1.3.0/go.mod h1:hQuRtDg+fCiFgC8j0zV222HvzFQdRd+SVX8gdmFcZzA= -cloud.google.com/go/dataplex v1.4.0/go.mod h1:X51GfLXEMVJ6UN47ESVqvlsRplbLhcsAt0kZCCKsU0A= -cloud.google.com/go/dataplex v1.5.2/go.mod h1:cVMgQHsmfRoI5KFYq4JtIBEUbYwc3c7tXmIDhRmNNVQ= -cloud.google.com/go/dataplex v1.6.0/go.mod h1:bMsomC/aEJOSpHXdFKFGQ1b0TDPIeL28nJObeO1ppRs= -cloud.google.com/go/dataplex v1.8.1/go.mod h1:7TyrDT6BCdI8/38Uvp0/ZxBslOslP2X2MPDucliyvSE= -cloud.google.com/go/dataplex v1.9.0/go.mod h1:7TyrDT6BCdI8/38Uvp0/ZxBslOslP2X2MPDucliyvSE= -cloud.google.com/go/dataplex v1.9.1/go.mod h1:7TyrDT6BCdI8/38Uvp0/ZxBslOslP2X2MPDucliyvSE= -cloud.google.com/go/dataplex v1.10.1/go.mod h1:1MzmBv8FvjYfc7vDdxhnLFNskikkB+3vl475/XdCDhs= -cloud.google.com/go/dataproc v1.7.0/go.mod h1:CKAlMjII9H90RXaMpSxQ8EU6dQx6iAYNPcYPOkSbi8s= -cloud.google.com/go/dataproc v1.8.0/go.mod h1:5OW+zNAH0pMpw14JVrPONsxMQYMBqJuzORhIBfBn9uI= -cloud.google.com/go/dataproc v1.12.0/go.mod h1:zrF3aX0uV3ikkMz6z4uBbIKyhRITnxvr4i3IjKsKrw4= -cloud.google.com/go/dataproc/v2 v2.0.1/go.mod h1:7Ez3KRHdFGcfY7GcevBbvozX+zyWGcwLJvvAMwCaoZ4= -cloud.google.com/go/dataproc/v2 v2.2.0/go.mod h1:lZR7AQtwZPvmINx5J87DSOOpTfof9LVZju6/Qo4lmcY= -cloud.google.com/go/dataproc/v2 v2.2.1/go.mod h1:QdAJLaBjh+l4PVlVZcmrmhGccosY/omC1qwfQ61Zv/o= -cloud.google.com/go/dataqna v0.5.0/go.mod h1:90Hyk596ft3zUQ8NkFfvICSIfHFh1Bc7C4cK3vbhkeo= -cloud.google.com/go/dataqna v0.6.0/go.mod h1:1lqNpM7rqNLVgWBJyk5NF6Uen2PHym0jtVJonplVsDA= -cloud.google.com/go/dataqna v0.7.0/go.mod h1:Lx9OcIIeqCrw1a6KdO3/5KMP1wAmTc0slZWwP12Qq3c= -cloud.google.com/go/dataqna v0.8.1/go.mod h1:zxZM0Bl6liMePWsHA8RMGAfmTG34vJMapbHAxQ5+WA8= -cloud.google.com/go/dataqna v0.8.2/go.mod h1:KNEqgx8TTmUipnQsScOoDpq/VlXVptUqVMZnt30WAPs= 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/datastore v1.10.0/go.mod h1:PC5UzAmDEkAmkfaknstTYbNpgE49HAgW2J1gcgUfmdM= -cloud.google.com/go/datastore v1.11.0/go.mod h1:TvGxBIHCS50u8jzG+AW/ppf87v1of8nwzFNgEZU1D3c= -cloud.google.com/go/datastore v1.12.0/go.mod h1:KjdB88W897MRITkvWWJrg2OUtrR5XVj1EoLgSp6/N70= -cloud.google.com/go/datastore v1.12.1/go.mod h1:KjdB88W897MRITkvWWJrg2OUtrR5XVj1EoLgSp6/N70= -cloud.google.com/go/datastore v1.13.0/go.mod h1:KjdB88W897MRITkvWWJrg2OUtrR5XVj1EoLgSp6/N70= -cloud.google.com/go/datastore v1.14.0/go.mod h1:GAeStMBIt9bPS7jMJA85kgkpsMkvseWWXiaHya9Jes8= -cloud.google.com/go/datastore v1.15.0/go.mod h1:GAeStMBIt9bPS7jMJA85kgkpsMkvseWWXiaHya9Jes8= -cloud.google.com/go/datastream v1.2.0/go.mod h1:i/uTP8/fZwgATHS/XFu0TcNUhuA0twZxxQ3EyCUQMwo= -cloud.google.com/go/datastream v1.3.0/go.mod h1:cqlOX8xlyYF/uxhiKn6Hbv6WjwPPuI9W2M9SAXwaLLQ= -cloud.google.com/go/datastream v1.4.0/go.mod h1:h9dpzScPhDTs5noEMQVWP8Wx8AFBRyS0s8KWPx/9r0g= -cloud.google.com/go/datastream v1.5.0/go.mod h1:6TZMMNPwjUqZHBKPQ1wwXpb0d5VDVPl2/XoS5yi88q4= -cloud.google.com/go/datastream v1.6.0/go.mod h1:6LQSuswqLa7S4rPAOZFVjHIG3wJIjZcZrw8JDEDJuIs= -cloud.google.com/go/datastream v1.7.0/go.mod h1:uxVRMm2elUSPuh65IbZpzJNMbuzkcvu5CjMqVIUHrww= -cloud.google.com/go/datastream v1.9.1/go.mod h1:hqnmr8kdUBmrnk65k5wNRoHSCYksvpdZIcZIEl8h43Q= -cloud.google.com/go/datastream v1.10.0/go.mod h1:hqnmr8kdUBmrnk65k5wNRoHSCYksvpdZIcZIEl8h43Q= -cloud.google.com/go/datastream v1.10.1/go.mod h1:7ngSYwnw95YFyTd5tOGBxHlOZiL+OtpjheqU7t2/s/c= -cloud.google.com/go/deploy v1.4.0/go.mod h1:5Xghikd4VrmMLNaF6FiRFDlHb59VM59YoDQnOUdsH/c= -cloud.google.com/go/deploy v1.5.0/go.mod h1:ffgdD0B89tToyW/U/D2eL0jN2+IEV/3EMuXHA0l4r+s= -cloud.google.com/go/deploy v1.6.0/go.mod h1:f9PTHehG/DjCom3QH0cntOVRm93uGBDt2vKzAPwpXQI= -cloud.google.com/go/deploy v1.8.0/go.mod h1:z3myEJnA/2wnB4sgjqdMfgxCA0EqC3RBTNcVPs93mtQ= -cloud.google.com/go/deploy v1.11.0/go.mod h1:tKuSUV5pXbn67KiubiUNUejqLs4f5cxxiCNCeyl0F2g= -cloud.google.com/go/deploy v1.13.0/go.mod h1:tKuSUV5pXbn67KiubiUNUejqLs4f5cxxiCNCeyl0F2g= -cloud.google.com/go/deploy v1.13.1/go.mod h1:8jeadyLkH9qu9xgO3hVWw8jVr29N1mnW42gRJT8GY6g= -cloud.google.com/go/dialogflow v1.15.0/go.mod h1:HbHDWs33WOGJgn6rfzBW1Kv807BE3O1+xGbn59zZWI4= -cloud.google.com/go/dialogflow v1.16.1/go.mod h1:po6LlzGfK+smoSmTBnbkIZY2w8ffjz/RcGSS+sh1el0= -cloud.google.com/go/dialogflow v1.17.0/go.mod h1:YNP09C/kXA1aZdBgC/VtXX74G/TKn7XVCcVumTflA+8= -cloud.google.com/go/dialogflow v1.18.0/go.mod h1:trO7Zu5YdyEuR+BhSNOqJezyFQ3aUzz0njv7sMx/iek= -cloud.google.com/go/dialogflow v1.19.0/go.mod h1:JVmlG1TwykZDtxtTXujec4tQ+D8SBFMoosgy+6Gn0s0= -cloud.google.com/go/dialogflow v1.29.0/go.mod h1:b+2bzMe+k1s9V+F2jbJwpHPzrnIyHihAdRFMtn2WXuM= -cloud.google.com/go/dialogflow v1.31.0/go.mod h1:cuoUccuL1Z+HADhyIA7dci3N5zUssgpBJmCzI6fNRB4= -cloud.google.com/go/dialogflow v1.32.0/go.mod h1:jG9TRJl8CKrDhMEcvfcfFkkpp8ZhgPz3sBGmAUYJ2qE= -cloud.google.com/go/dialogflow v1.38.0/go.mod h1:L7jnH+JL2mtmdChzAIcXQHXMvQkE3U4hTaNltEuxXn4= -cloud.google.com/go/dialogflow v1.40.0/go.mod h1:L7jnH+JL2mtmdChzAIcXQHXMvQkE3U4hTaNltEuxXn4= -cloud.google.com/go/dialogflow v1.43.0/go.mod h1:pDUJdi4elL0MFmt1REMvFkdsUTYSHq+rTCS8wg0S3+M= -cloud.google.com/go/dialogflow v1.44.0/go.mod h1:pDUJdi4elL0MFmt1REMvFkdsUTYSHq+rTCS8wg0S3+M= -cloud.google.com/go/dialogflow v1.44.1/go.mod h1:n/h+/N2ouKOO+rbe/ZnI186xImpqvCVj2DdsWS/0EAk= -cloud.google.com/go/dlp v1.6.0/go.mod h1:9eyB2xIhpU0sVwUixfBubDoRwP+GjeUoxxeueZmqvmM= -cloud.google.com/go/dlp v1.7.0/go.mod h1:68ak9vCiMBjbasxeVD17hVPxDEck+ExiHavX8kiHG+Q= -cloud.google.com/go/dlp v1.9.0/go.mod h1:qdgmqgTyReTz5/YNSSuueR8pl7hO0o9bQ39ZhtgkWp4= -cloud.google.com/go/dlp v1.10.1/go.mod h1:IM8BWz1iJd8njcNcG0+Kyd9OPnqnRNkDV8j42VT5KOI= -cloud.google.com/go/dlp v1.10.2/go.mod h1:ZbdKIhcnyhILgccwVDzkwqybthh7+MplGC3kZVZsIOQ= -cloud.google.com/go/documentai v1.7.0/go.mod h1:lJvftZB5NRiFSX4moiye1SMxHx0Bc3x1+p9e/RfXYiU= -cloud.google.com/go/documentai v1.8.0/go.mod h1:xGHNEB7CtsnySCNrCFdCyyMz44RhFEEX2Q7UD0c5IhU= -cloud.google.com/go/documentai v1.9.0/go.mod h1:FS5485S8R00U10GhgBC0aNGrJxBP8ZVpEeJ7PQDZd6k= -cloud.google.com/go/documentai v1.10.0/go.mod h1:vod47hKQIPeCfN2QS/jULIvQTugbmdc0ZvxxfQY1bg4= -cloud.google.com/go/documentai v1.16.0/go.mod h1:o0o0DLTEZ+YnJZ+J4wNfTxmDVyrkzFvttBXXtYRMHkM= -cloud.google.com/go/documentai v1.18.0/go.mod h1:F6CK6iUH8J81FehpskRmhLq/3VlwQvb7TvwOceQ2tbs= -cloud.google.com/go/documentai v1.20.0/go.mod h1:yJkInoMcK0qNAEdRnqY/D5asy73tnPe88I1YTZT+a8E= -cloud.google.com/go/documentai v1.22.0/go.mod h1:yJkInoMcK0qNAEdRnqY/D5asy73tnPe88I1YTZT+a8E= -cloud.google.com/go/documentai v1.22.1/go.mod h1:LKs22aDHbJv7ufXuPypzRO7rG3ALLJxzdCXDPutw4Qc= -cloud.google.com/go/documentai v1.23.0/go.mod h1:LKs22aDHbJv7ufXuPypzRO7rG3ALLJxzdCXDPutw4Qc= -cloud.google.com/go/documentai v1.23.2/go.mod h1:Q/wcRT+qnuXOpjAkvOV4A+IeQl04q2/ReT7SSbytLSo= -cloud.google.com/go/domains v0.6.0/go.mod h1:T9Rz3GasrpYk6mEGHh4rymIhjlnIuB4ofT1wTxDeT4Y= -cloud.google.com/go/domains v0.7.0/go.mod h1:PtZeqS1xjnXuRPKE/88Iru/LdfoRyEHYA9nFQf4UKpg= -cloud.google.com/go/domains v0.8.0/go.mod h1:M9i3MMDzGFXsydri9/vW+EWz9sWb4I6WyHqdlAk0idE= -cloud.google.com/go/domains v0.9.1/go.mod h1:aOp1c0MbejQQ2Pjf1iJvnVyT+z6R6s8pX66KaCSDYfE= -cloud.google.com/go/domains v0.9.2/go.mod h1:3YvXGYzZG1Temjbk7EyGCuGGiXHJwVNmwIf+E/cUp5I= -cloud.google.com/go/edgecontainer v0.1.0/go.mod h1:WgkZ9tp10bFxqO8BLPqv2LlfmQF1X8lZqwW4r1BTajk= -cloud.google.com/go/edgecontainer v0.2.0/go.mod h1:RTmLijy+lGpQ7BXuTDa4C4ssxyXT34NIuHIgKuP4s5w= -cloud.google.com/go/edgecontainer v0.3.0/go.mod h1:FLDpP4nykgwwIfcLt6zInhprzw0lEi2P1fjO6Ie0qbc= -cloud.google.com/go/edgecontainer v1.0.0/go.mod h1:cttArqZpBB2q58W/upSG++ooo6EsblxDIolxa3jSjbY= -cloud.google.com/go/edgecontainer v1.1.1/go.mod h1:O5bYcS//7MELQZs3+7mabRqoWQhXCzenBu0R8bz2rwk= -cloud.google.com/go/edgecontainer v1.1.2/go.mod h1:wQRjIzqxEs9e9wrtle4hQPSR1Y51kqN75dgF7UllZZ4= -cloud.google.com/go/errorreporting v0.3.0/go.mod h1:xsP2yaAp+OAW4OIm60An2bbLpqIhKXdWR/tawvl7QzU= -cloud.google.com/go/essentialcontacts v1.3.0/go.mod h1:r+OnHa5jfj90qIfZDO/VztSFqbQan7HV75p8sA+mdGI= -cloud.google.com/go/essentialcontacts v1.4.0/go.mod h1:8tRldvHYsmnBCHdFpvU+GL75oWiBKl80BiqlFh9tp+8= -cloud.google.com/go/essentialcontacts v1.5.0/go.mod h1:ay29Z4zODTuwliK7SnX8E86aUF2CTzdNtvv42niCX0M= -cloud.google.com/go/essentialcontacts v1.6.2/go.mod h1:T2tB6tX+TRak7i88Fb2N9Ok3PvY3UNbUsMag9/BARh4= -cloud.google.com/go/essentialcontacts v1.6.3/go.mod h1:yiPCD7f2TkP82oJEFXFTou8Jl8L6LBRPeBEkTaO0Ggo= -cloud.google.com/go/eventarc v1.7.0/go.mod h1:6ctpF3zTnaQCxUjHUdcfgcA1A2T309+omHZth7gDfmc= -cloud.google.com/go/eventarc v1.8.0/go.mod h1:imbzxkyAU4ubfsaKYdQg04WS1NvncblHEup4kvF+4gw= -cloud.google.com/go/eventarc v1.10.0/go.mod h1:u3R35tmZ9HvswGRBnF48IlYgYeBcPUCjkr4BTdem2Kw= -cloud.google.com/go/eventarc v1.11.0/go.mod h1:PyUjsUKPWoRBCHeOxZd/lbOOjahV41icXyUY5kSTvVY= -cloud.google.com/go/eventarc v1.12.1/go.mod h1:mAFCW6lukH5+IZjkvrEss+jmt2kOdYlN8aMx3sRJiAI= -cloud.google.com/go/eventarc v1.13.0/go.mod h1:mAFCW6lukH5+IZjkvrEss+jmt2kOdYlN8aMx3sRJiAI= -cloud.google.com/go/eventarc v1.13.1/go.mod h1:EqBxmGHFrruIara4FUQ3RHlgfCn7yo1HYsu2Hpt/C3Y= -cloud.google.com/go/filestore v1.3.0/go.mod h1:+qbvHGvXU1HaKX2nD0WEPo92TP/8AQuCVEBXNY9z0+w= -cloud.google.com/go/filestore v1.4.0/go.mod h1:PaG5oDfo9r224f8OYXURtAsY+Fbyq/bLYoINEK8XQAI= -cloud.google.com/go/filestore v1.5.0/go.mod h1:FqBXDWBp4YLHqRnVGveOkHDf8svj9r5+mUDLupOWEDs= -cloud.google.com/go/filestore v1.6.0/go.mod h1:di5unNuss/qfZTw2U9nhFqo8/ZDSc466dre85Kydllg= -cloud.google.com/go/filestore v1.7.1/go.mod h1:y10jsorq40JJnjR/lQ8AfFbbcGlw3g+Dp8oN7i7FjV4= -cloud.google.com/go/filestore v1.7.2/go.mod h1:TYOlyJs25f/omgj+vY7/tIG/E7BX369triSPzE4LdgE= -cloud.google.com/go/firestore v1.9.0/go.mod h1:HMkjKHNTtRyZNiMzu7YAsLr9K3X2udY2AMwDaMEQiiE= -cloud.google.com/go/firestore v1.11.0/go.mod h1:b38dKhgzlmNNGTNZZwe7ZRFEuRab1Hay3/DBsIGKKy4= -cloud.google.com/go/firestore v1.12.0/go.mod h1:b38dKhgzlmNNGTNZZwe7ZRFEuRab1Hay3/DBsIGKKy4= -cloud.google.com/go/firestore v1.13.0/go.mod h1:QojqqOh8IntInDUSTAh0c8ZsPYAr68Ma8c5DWOy8xb8= -cloud.google.com/go/functions v1.6.0/go.mod h1:3H1UA3qiIPRWD7PeZKLvHZ9SaQhR26XIJcC0A5GbvAk= -cloud.google.com/go/functions v1.7.0/go.mod h1:+d+QBcWM+RsrgZfV9xo6KfA1GlzJfxcfZcRPEhDDfzg= -cloud.google.com/go/functions v1.8.0/go.mod h1:RTZ4/HsQjIqIYP9a9YPbU+QFoQsAlYgrwOXJWHn1POY= -cloud.google.com/go/functions v1.9.0/go.mod h1:Y+Dz8yGguzO3PpIjhLTbnqV1CWmgQ5UwtlpzoyquQ08= -cloud.google.com/go/functions v1.10.0/go.mod h1:0D3hEOe3DbEvCXtYOZHQZmD+SzYsi1YbI7dGvHfldXw= -cloud.google.com/go/functions v1.12.0/go.mod h1:AXWGrF3e2C/5ehvwYo/GH6O5s09tOPksiKhz+hH8WkA= -cloud.google.com/go/functions v1.13.0/go.mod h1:EU4O007sQm6Ef/PwRsI8N2umygGqPBS/IZQKBQBcJ3c= -cloud.google.com/go/functions v1.15.1/go.mod h1:P5yNWUTkyU+LvW/S9O6V+V423VZooALQlqoXdoPz5AE= -cloud.google.com/go/functions v1.15.2/go.mod h1:CHAjtcR6OU4XF2HuiVeriEdELNcnvRZSk1Q8RMqy4lE= -cloud.google.com/go/gaming v1.5.0/go.mod h1:ol7rGcxP/qHTRQE/RO4bxkXq+Fix0j6D4LFPzYTIrDM= -cloud.google.com/go/gaming v1.6.0/go.mod h1:YMU1GEvA39Qt3zWGyAVA9bpYz/yAhTvaQ1t2sK4KPUA= -cloud.google.com/go/gaming v1.7.0/go.mod h1:LrB8U7MHdGgFG851iHAfqUdLcKBdQ55hzXy9xBJz0+w= -cloud.google.com/go/gaming v1.8.0/go.mod h1:xAqjS8b7jAVW0KFYeRUxngo9My3f33kFmua++Pi+ggM= -cloud.google.com/go/gaming v1.9.0/go.mod h1:Fc7kEmCObylSWLO334NcO+O9QMDyz+TKC4v1D7X+Bc0= -cloud.google.com/go/gaming v1.10.1/go.mod h1:XQQvtfP8Rb9Rxnxm5wFVpAp9zCQkJi2bLIb7iHGwB3s= -cloud.google.com/go/gkebackup v0.2.0/go.mod h1:XKvv/4LfG829/B8B7xRkk8zRrOEbKtEam6yNfuQNH60= -cloud.google.com/go/gkebackup v0.3.0/go.mod h1:n/E671i1aOQvUxT541aTkCwExO/bTer2HDlj4TsBRAo= -cloud.google.com/go/gkebackup v0.4.0/go.mod h1:byAyBGUwYGEEww7xsbnUTBHIYcOPy/PgUWUtOeRm9Vg= -cloud.google.com/go/gkebackup v1.3.0/go.mod h1:vUDOu++N0U5qs4IhG1pcOnD1Mac79xWy6GoBFlWCWBU= -cloud.google.com/go/gkebackup v1.3.1/go.mod h1:vUDOu++N0U5qs4IhG1pcOnD1Mac79xWy6GoBFlWCWBU= -cloud.google.com/go/gkebackup v1.3.2/go.mod h1:OMZbXzEJloyXMC7gqdSB+EOEQ1AKcpGYvO3s1ec5ixk= -cloud.google.com/go/gkeconnect v0.5.0/go.mod h1:c5lsNAg5EwAy7fkqX/+goqFsU1Da/jQFqArp+wGNr/o= -cloud.google.com/go/gkeconnect v0.6.0/go.mod h1:Mln67KyU/sHJEBY8kFZ0xTeyPtzbq9StAVvEULYK16A= -cloud.google.com/go/gkeconnect v0.7.0/go.mod h1:SNfmVqPkaEi3bF/B3CNZOAYPYdg7sU+obZ+QTky2Myw= -cloud.google.com/go/gkeconnect v0.8.1/go.mod h1:KWiK1g9sDLZqhxB2xEuPV8V9NYzrqTUmQR9shJHpOZw= -cloud.google.com/go/gkeconnect v0.8.2/go.mod h1:6nAVhwchBJYgQCXD2pHBFQNiJNyAd/wyxljpaa6ZPrY= -cloud.google.com/go/gkehub v0.9.0/go.mod h1:WYHN6WG8w9bXU0hqNxt8rm5uxnk8IH+lPY9J2TV7BK0= -cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y977wO+hBH0= -cloud.google.com/go/gkehub v0.11.0/go.mod h1:JOWHlmN+GHyIbuWQPl47/C2RFhnFKH38jH9Ascu3n0E= -cloud.google.com/go/gkehub v0.12.0/go.mod h1:djiIwwzTTBrF5NaXCGv3mf7klpEMcST17VBTVVDcuaw= -cloud.google.com/go/gkehub v0.14.1/go.mod h1:VEXKIJZ2avzrbd7u+zeMtW00Y8ddk/4V9511C9CQGTY= -cloud.google.com/go/gkehub v0.14.2/go.mod h1:iyjYH23XzAxSdhrbmfoQdePnlMj2EWcvnR+tHdBQsCY= -cloud.google.com/go/gkemulticloud v0.3.0/go.mod h1:7orzy7O0S+5kq95e4Hpn7RysVA7dPs8W/GgfUtsPbrA= -cloud.google.com/go/gkemulticloud v0.4.0/go.mod h1:E9gxVBnseLWCk24ch+P9+B2CoDFJZTyIgLKSalC7tuI= -cloud.google.com/go/gkemulticloud v0.5.0/go.mod h1:W0JDkiyi3Tqh0TJr//y19wyb1yf8llHVto2Htf2Ja3Y= -cloud.google.com/go/gkemulticloud v0.6.1/go.mod h1:kbZ3HKyTsiwqKX7Yw56+wUGwwNZViRnxWK2DVknXWfw= -cloud.google.com/go/gkemulticloud v1.0.0/go.mod h1:kbZ3HKyTsiwqKX7Yw56+wUGwwNZViRnxWK2DVknXWfw= -cloud.google.com/go/gkemulticloud v1.0.1/go.mod h1:AcrGoin6VLKT/fwZEYuqvVominLriQBCKmbjtnbMjG8= -cloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc= -cloud.google.com/go/grafeas v0.3.0/go.mod h1:P7hgN24EyONOTMyeJH6DxG4zD7fwiYa5Q6GUgyFSOU8= -cloud.google.com/go/gsuiteaddons v1.3.0/go.mod h1:EUNK/J1lZEZO8yPtykKxLXI6JSVN2rg9bN8SXOa0bgM= -cloud.google.com/go/gsuiteaddons v1.4.0/go.mod h1:rZK5I8hht7u7HxFQcFei0+AtfS9uSushomRlg+3ua1o= -cloud.google.com/go/gsuiteaddons v1.5.0/go.mod h1:TFCClYLd64Eaa12sFVmUyG62tk4mdIsI7pAnSXRkcFo= -cloud.google.com/go/gsuiteaddons v1.6.1/go.mod h1:CodrdOqRZcLp5WOwejHWYBjZvfY0kOphkAKpF/3qdZY= -cloud.google.com/go/gsuiteaddons v1.6.2/go.mod h1:K65m9XSgs8hTF3X9nNTPi8IQueljSdYo9F+Mi+s4MyU= -cloud.google.com/go/iam v0.1.0/go.mod h1:vcUNEa0pEm0qRVpmWepWaFMIAI8/hjB9mO8rNCJtF6c= -cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= -cloud.google.com/go/iam v0.5.0/go.mod h1:wPU9Vt0P4UmCux7mqtRu6jcpPAb74cP1fh50J3QpkUc= -cloud.google.com/go/iam v0.6.0/go.mod h1:+1AH33ueBne5MzYccyMHtEKqLE4/kJOibtffMHDMFMc= -cloud.google.com/go/iam v0.7.0/go.mod h1:H5Br8wRaDGNc8XP3keLc4unfUUZeyH3Sfl9XpQEYOeg= -cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE= -cloud.google.com/go/iam v0.11.0/go.mod h1:9PiLDanza5D+oWFZiH1uG+RnRCfEGKoyl6yo4cgWZGY= -cloud.google.com/go/iam v0.12.0/go.mod h1:knyHGviacl11zrtZUoDuYpDgLjvr28sLQaG0YB2GYAY= -cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0= -cloud.google.com/go/iam v1.0.1/go.mod h1:yR3tmSL8BcZB4bxByRv2jkSIahVmCtfKZwLYGBalRE8= -cloud.google.com/go/iam v1.1.0/go.mod h1:nxdHjaKfCr7fNYx/HJMM8LgiMugmveWlkatear5gVyk= -cloud.google.com/go/iam v1.1.1/go.mod h1:A5avdyVL2tCppe4unb0951eI9jreack+RJ0/d+KUZOU= -cloud.google.com/go/iam v1.1.2/go.mod h1:A5avdyVL2tCppe4unb0951eI9jreack+RJ0/d+KUZOU= -cloud.google.com/go/iam v1.1.3/go.mod h1:3khUlaBXfPKKe7huYgEpDn6FtgRyMEqbkvBxrQyY5SE= cloud.google.com/go/iam v1.1.6 h1:bEa06k05IO4f4uJonbB5iAgKTPpABy1ayxaIZV/GHVc= cloud.google.com/go/iam v1.1.6/go.mod h1:O0zxdPeGBoFdWW3HWmBxJsk0pfvNM/p/qa82rWOGTwI= -cloud.google.com/go/iap v1.4.0/go.mod h1:RGFwRJdihTINIe4wZ2iCP0zF/qu18ZwyKxrhMhygBEc= -cloud.google.com/go/iap v1.5.0/go.mod h1:UH/CGgKd4KyohZL5Pt0jSKE4m3FR51qg6FKQ/z/Ix9A= -cloud.google.com/go/iap v1.6.0/go.mod h1:NSuvI9C/j7UdjGjIde7t7HBz+QTwBcapPE07+sSRcLk= -cloud.google.com/go/iap v1.7.0/go.mod h1:beqQx56T9O1G1yNPph+spKpNibDlYIiIixiqsQXxLIo= -cloud.google.com/go/iap v1.7.1/go.mod h1:WapEwPc7ZxGt2jFGB/C/bm+hP0Y6NXzOYGjpPnmMS74= -cloud.google.com/go/iap v1.8.1/go.mod h1:sJCbeqg3mvWLqjZNsI6dfAtbbV1DL2Rl7e1mTyXYREQ= -cloud.google.com/go/iap v1.9.0/go.mod h1:01OFxd1R+NFrg78S+hoPV5PxEzv22HXaNqUUlmNHFuY= -cloud.google.com/go/iap v1.9.1/go.mod h1:SIAkY7cGMLohLSdBR25BuIxO+I4fXJiL06IBL7cy/5Q= -cloud.google.com/go/ids v1.1.0/go.mod h1:WIuwCaYVOzHIj2OhN9HAwvW+DBdmUAdcWlFxRl+KubM= -cloud.google.com/go/ids v1.2.0/go.mod h1:5WXvp4n25S0rA/mQWAg1YEEBBq6/s+7ml1RDCW1IrcY= -cloud.google.com/go/ids v1.3.0/go.mod h1:JBdTYwANikFKaDP6LtW5JAi4gubs57SVNQjemdt6xV4= -cloud.google.com/go/ids v1.4.1/go.mod h1:np41ed8YMU8zOgv53MMMoCntLTn2lF+SUzlM+O3u/jw= -cloud.google.com/go/ids v1.4.2/go.mod h1:3vw8DX6YddRu9BncxuzMyWn0g8+ooUjI2gslJ7FH3vk= -cloud.google.com/go/iot v1.3.0/go.mod h1:r7RGh2B61+B8oz0AGE+J72AhA0G7tdXItODWsaA2oLs= -cloud.google.com/go/iot v1.4.0/go.mod h1:dIDxPOn0UvNDUMD8Ger7FIaTuvMkj+aGk94RPP0iV+g= -cloud.google.com/go/iot v1.5.0/go.mod h1:mpz5259PDl3XJthEmh9+ap0affn/MqNSP4My77Qql9o= -cloud.google.com/go/iot v1.6.0/go.mod h1:IqdAsmE2cTYYNO1Fvjfzo9po179rAtJeVGUvkLN3rLE= -cloud.google.com/go/iot v1.7.1/go.mod h1:46Mgw7ev1k9KqK1ao0ayW9h0lI+3hxeanz+L1zmbbbk= -cloud.google.com/go/iot v1.7.2/go.mod h1:q+0P5zr1wRFpw7/MOgDXrG/HVA+l+cSwdObffkrpnSg= -cloud.google.com/go/kms v1.4.0/go.mod h1:fajBHndQ+6ubNw6Ss2sSd+SWvjL26RNo/dr7uxsnnOA= -cloud.google.com/go/kms v1.5.0/go.mod h1:QJS2YY0eJGBg3mnDfuaCyLauWwBJiHRboYxJ++1xJNg= -cloud.google.com/go/kms v1.6.0/go.mod h1:Jjy850yySiasBUDi6KFUwUv2n1+o7QZFyuUJg6OgjA0= -cloud.google.com/go/kms v1.8.0/go.mod h1:4xFEhYFqvW+4VMELtZyxomGSYtSQKzM178ylFW4jMAg= -cloud.google.com/go/kms v1.9.0/go.mod h1:qb1tPTgfF9RQP8e1wq4cLFErVuTJv7UsSC915J8dh3w= -cloud.google.com/go/kms v1.10.0/go.mod h1:ng3KTUtQQU9bPX3+QGLsflZIHlkbn8amFAMY63m8d24= -cloud.google.com/go/kms v1.10.1/go.mod h1:rIWk/TryCkR59GMC3YtHtXeLzd634lBbKenvyySAyYI= -cloud.google.com/go/kms v1.11.0/go.mod h1:hwdiYC0xjnWsKQQCQQmIQnS9asjYVSK6jtXm+zFqXLM= -cloud.google.com/go/kms v1.12.1/go.mod h1:c9J991h5DTl+kg7gi3MYomh12YEENGrf48ee/N/2CDM= -cloud.google.com/go/kms v1.15.0/go.mod h1:c9J991h5DTl+kg7gi3MYomh12YEENGrf48ee/N/2CDM= -cloud.google.com/go/kms v1.15.2/go.mod h1:3hopT4+7ooWRCjc2DxgnpESFxhIraaI2IpAVUEhbT/w= -cloud.google.com/go/kms v1.15.3/go.mod h1:AJdXqHxS2GlPyduM99s9iGqi2nwbviBbhV/hdmt4iOQ= -cloud.google.com/go/kms v1.15.5/go.mod h1:cU2H5jnp6G2TDpUGZyqTCoy1n16fbubHZjmVXSMtwDI= -cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic= -cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI= -cloud.google.com/go/language v1.7.0/go.mod h1:DJ6dYN/W+SQOjF8e1hLQXMF21AkH2w9wiPzPCJa2MIE= -cloud.google.com/go/language v1.8.0/go.mod h1:qYPVHf7SPoNNiCL2Dr0FfEFNil1qi3pQEyygwpgVKB8= -cloud.google.com/go/language v1.9.0/go.mod h1:Ns15WooPM5Ad/5no/0n81yUetis74g3zrbeJBE+ptUY= -cloud.google.com/go/language v1.10.1/go.mod h1:CPp94nsdVNiQEt1CNjF5WkTcisLiHPyIbMhvR8H2AW0= -cloud.google.com/go/language v1.11.0/go.mod h1:uDx+pFDdAKTY8ehpWbiXyQdz8tDSYLJbQcXsCkjYyvQ= -cloud.google.com/go/language v1.11.1/go.mod h1:Xyid9MG9WOX3utvDbpX7j3tXDmmDooMyMDqgUVpH17U= -cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8= -cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08= -cloud.google.com/go/lifesciences v0.8.0/go.mod h1:lFxiEOMqII6XggGbOnKiyZ7IBwoIqA84ClvoezaA/bo= -cloud.google.com/go/lifesciences v0.9.1/go.mod h1:hACAOd1fFbCGLr/+weUKRAJas82Y4vrL3O5326N//Wc= -cloud.google.com/go/lifesciences v0.9.2/go.mod h1:QHEOO4tDzcSAzeJg7s2qwnLM2ji8IRpQl4p6m5Z9yTA= -cloud.google.com/go/logging v1.6.1/go.mod h1:5ZO0mHHbvm8gEmeEUHrmDlTDSu5imF6MUP9OfilNXBw= -cloud.google.com/go/logging v1.7.0/go.mod h1:3xjP2CjkM3ZkO73aj4ASA5wRPGGCRrPIAeNqVNkzY8M= -cloud.google.com/go/logging v1.8.1/go.mod h1:TJjR+SimHwuC8MZ9cjByQulAMgni+RkXeI3wwctHJEI= -cloud.google.com/go/longrunning v0.1.1/go.mod h1:UUFxuDWkv22EuY93jjmDMFT5GPQKeFVJBIF6QlTqdsE= -cloud.google.com/go/longrunning v0.3.0/go.mod h1:qth9Y41RRSUE69rDcOn6DdK3HfQfsUI0YSmW3iIlLJc= -cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo= -cloud.google.com/go/longrunning v0.4.2/go.mod h1:OHrnaYyLUV6oqwh0xiS7e5sLQhP1m0QU9R+WhGDMgIQ= -cloud.google.com/go/longrunning v0.5.0/go.mod h1:0JNuqRShmscVAhIACGtskSAWtqtOoPkwP0YF1oVEchc= -cloud.google.com/go/longrunning v0.5.1/go.mod h1:spvimkwdz6SPWKEt/XBij79E9fiTkHSQl/fRUUQJYJc= -cloud.google.com/go/longrunning v0.5.2/go.mod h1:nqo6DQbNV2pXhGDbDMoN2bWz68MjZUzqv2YttZiveCs= -cloud.google.com/go/managedidentities v1.3.0/go.mod h1:UzlW3cBOiPrzucO5qWkNkh0w33KFtBJU281hacNvsdE= -cloud.google.com/go/managedidentities v1.4.0/go.mod h1:NWSBYbEMgqmbZsLIyKvxrYbtqOsxY1ZrGM+9RgDqInM= -cloud.google.com/go/managedidentities v1.5.0/go.mod h1:+dWcZ0JlUmpuxpIDfyP5pP5y0bLdRwOS4Lp7gMni/LA= -cloud.google.com/go/managedidentities v1.6.1/go.mod h1:h/irGhTN2SkZ64F43tfGPMbHnypMbu4RB3yl8YcuEak= -cloud.google.com/go/managedidentities v1.6.2/go.mod h1:5c2VG66eCa0WIq6IylRk3TBW83l161zkFvCj28X7jn8= -cloud.google.com/go/maps v0.1.0/go.mod h1:BQM97WGyfw9FWEmQMpZ5T6cpovXXSd1cGmFma94eubI= -cloud.google.com/go/maps v0.6.0/go.mod h1:o6DAMMfb+aINHz/p/jbcY+mYeXBoZoxTfdSQ8VAJaCw= -cloud.google.com/go/maps v0.7.0/go.mod h1:3GnvVl3cqeSvgMcpRlQidXsPYuDGQ8naBis7MVzpXsY= -cloud.google.com/go/maps v1.3.0/go.mod h1:6mWTUv+WhnOwAgjVsSW2QPPECmW+s3PcRyOa9vgG/5s= -cloud.google.com/go/maps v1.4.0/go.mod h1:6mWTUv+WhnOwAgjVsSW2QPPECmW+s3PcRyOa9vgG/5s= -cloud.google.com/go/maps v1.4.1/go.mod h1:BxSa0BnW1g2U2gNdbq5zikLlHUuHW0GFWh7sgML2kIY= -cloud.google.com/go/mediatranslation v0.5.0/go.mod h1:jGPUhGTybqsPQn91pNXw0xVHfuJ3leR1wj37oU3y1f4= -cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w= -cloud.google.com/go/mediatranslation v0.7.0/go.mod h1:LCnB/gZr90ONOIQLgSXagp8XUW1ODs2UmUMvcgMfI2I= -cloud.google.com/go/mediatranslation v0.8.1/go.mod h1:L/7hBdEYbYHQJhX2sldtTO5SZZ1C1vkapubj0T2aGig= -cloud.google.com/go/mediatranslation v0.8.2/go.mod h1:c9pUaDRLkgHRx3irYE5ZC8tfXGrMYwNZdmDqKMSfFp8= -cloud.google.com/go/memcache v1.4.0/go.mod h1:rTOfiGZtJX1AaFUrOgsMHX5kAzaTQ8azHiuDoTPzNsE= -cloud.google.com/go/memcache v1.5.0/go.mod h1:dk3fCK7dVo0cUU2c36jKb4VqKPS22BTkf81Xq617aWM= -cloud.google.com/go/memcache v1.6.0/go.mod h1:XS5xB0eQZdHtTuTF9Hf8eJkKtR3pVRCcvJwtm68T3rA= -cloud.google.com/go/memcache v1.7.0/go.mod h1:ywMKfjWhNtkQTxrWxCkCFkoPjLHPW6A7WOTVI8xy3LY= -cloud.google.com/go/memcache v1.9.0/go.mod h1:8oEyzXCu+zo9RzlEaEjHl4KkgjlNDaXbCQeQWlzNFJM= -cloud.google.com/go/memcache v1.10.1/go.mod h1:47YRQIarv4I3QS5+hoETgKO40InqzLP6kpNLvyXuyaA= -cloud.google.com/go/memcache v1.10.2/go.mod h1:f9ZzJHLBrmd4BkguIAa/l/Vle6uTHzHokdnzSWOdQ6A= -cloud.google.com/go/metastore v1.5.0/go.mod h1:2ZNrDcQwghfdtCwJ33nM0+GrBGlVuh8rakL3vdPY3XY= -cloud.google.com/go/metastore v1.6.0/go.mod h1:6cyQTls8CWXzk45G55x57DVQ9gWg7RiH65+YgPsNh9s= -cloud.google.com/go/metastore v1.7.0/go.mod h1:s45D0B4IlsINu87/AsWiEVYbLaIMeUSoxlKKDqBGFS8= -cloud.google.com/go/metastore v1.8.0/go.mod h1:zHiMc4ZUpBiM7twCIFQmJ9JMEkDSyZS9U12uf7wHqSI= -cloud.google.com/go/metastore v1.10.0/go.mod h1:fPEnH3g4JJAk+gMRnrAnoqyv2lpUCqJPWOodSaf45Eo= -cloud.google.com/go/metastore v1.11.1/go.mod h1:uZuSo80U3Wd4zi6C22ZZliOUJ3XeM/MlYi/z5OAOWRA= -cloud.google.com/go/metastore v1.12.0/go.mod h1:uZuSo80U3Wd4zi6C22ZZliOUJ3XeM/MlYi/z5OAOWRA= -cloud.google.com/go/metastore v1.13.0/go.mod h1:URDhpG6XLeh5K+Glq0NOt74OfrPKTwS62gEPZzb5SOk= -cloud.google.com/go/metastore v1.13.1/go.mod h1:IbF62JLxuZmhItCppcIfzBBfUFq0DIB9HPDoLgWrVOU= -cloud.google.com/go/monitoring v1.7.0/go.mod h1:HpYse6kkGo//7p6sT0wsIC6IBDET0RhIsnmlA53dvEk= -cloud.google.com/go/monitoring v1.8.0/go.mod h1:E7PtoMJ1kQXWxPjB6mv2fhC5/15jInuulFdYYtlcvT4= -cloud.google.com/go/monitoring v1.12.0/go.mod h1:yx8Jj2fZNEkL/GYZyTLS4ZtZEZN8WtDEiEqG4kLK50w= -cloud.google.com/go/monitoring v1.13.0/go.mod h1:k2yMBAB1H9JT/QETjNkgdCGD9bPF712XiLTVr+cBrpw= -cloud.google.com/go/monitoring v1.15.1/go.mod h1:lADlSAlFdbqQuwwpaImhsJXu1QSdd3ojypXrFSMr2rM= -cloud.google.com/go/monitoring v1.16.0/go.mod h1:Ptp15HgAyM1fNICAojDMoNc/wUmn67mLHQfyqbw+poY= -cloud.google.com/go/monitoring v1.16.1/go.mod h1:6HsxddR+3y9j+o/cMJH6q/KJ/CBTvM/38L/1m7bTRJ4= -cloud.google.com/go/networkconnectivity v1.4.0/go.mod h1:nOl7YL8odKyAOtzNX73/M5/mGZgqqMeryi6UPZTk/rA= -cloud.google.com/go/networkconnectivity v1.5.0/go.mod h1:3GzqJx7uhtlM3kln0+x5wyFvuVH1pIBJjhCpjzSt75o= -cloud.google.com/go/networkconnectivity v1.6.0/go.mod h1:OJOoEXW+0LAxHh89nXd64uGG+FbQoeH8DtxCHVOMlaM= -cloud.google.com/go/networkconnectivity v1.7.0/go.mod h1:RMuSbkdbPwNMQjB5HBWD5MpTBnNm39iAVpC3TmsExt8= -cloud.google.com/go/networkconnectivity v1.10.0/go.mod h1:UP4O4sWXJG13AqrTdQCD9TnLGEbtNRqjuaaA7bNjF5E= -cloud.google.com/go/networkconnectivity v1.11.0/go.mod h1:iWmDD4QF16VCDLXUqvyspJjIEtBR/4zq5hwnY2X3scM= -cloud.google.com/go/networkconnectivity v1.12.1/go.mod h1:PelxSWYM7Sh9/guf8CFhi6vIqf19Ir/sbfZRUwXh92E= -cloud.google.com/go/networkconnectivity v1.13.0/go.mod h1:SAnGPes88pl7QRLUen2HmcBSE9AowVAcdug8c0RSBFk= -cloud.google.com/go/networkconnectivity v1.14.0/go.mod h1:SAnGPes88pl7QRLUen2HmcBSE9AowVAcdug8c0RSBFk= -cloud.google.com/go/networkconnectivity v1.14.1/go.mod h1:LyGPXR742uQcDxZ/wv4EI0Vu5N6NKJ77ZYVnDe69Zug= -cloud.google.com/go/networkmanagement v1.4.0/go.mod h1:Q9mdLLRn60AsOrPc8rs8iNV6OHXaGcDdsIQe1ohekq8= -cloud.google.com/go/networkmanagement v1.5.0/go.mod h1:ZnOeZ/evzUdUsnvRt792H0uYEnHQEMaz+REhhzJRcf4= -cloud.google.com/go/networkmanagement v1.6.0/go.mod h1:5pKPqyXjB/sgtvB5xqOemumoQNB7y95Q7S+4rjSOPYY= -cloud.google.com/go/networkmanagement v1.8.0/go.mod h1:Ho/BUGmtyEqrttTgWEe7m+8vDdK74ibQc+Be0q7Fof0= -cloud.google.com/go/networkmanagement v1.9.0/go.mod h1:UTUaEU9YwbCAhhz3jEOHr+2/K/MrBk2XxOLS89LQzFw= -cloud.google.com/go/networkmanagement v1.9.1/go.mod h1:CCSYgrQQvW73EJawO2QamemYcOb57LvrDdDU51F0mcI= -cloud.google.com/go/networksecurity v0.5.0/go.mod h1:xS6fOCoqpVC5zx15Z/MqkfDwH4+m/61A3ODiDV1xmiQ= -cloud.google.com/go/networksecurity v0.6.0/go.mod h1:Q5fjhTr9WMI5mbpRYEbiexTzROf7ZbDzvzCrNl14nyU= -cloud.google.com/go/networksecurity v0.7.0/go.mod h1:mAnzoxx/8TBSyXEeESMy9OOYwo1v+gZ5eMRnsT5bC8k= -cloud.google.com/go/networksecurity v0.8.0/go.mod h1:B78DkqsxFG5zRSVuwYFRZ9Xz8IcQ5iECsNrPn74hKHU= -cloud.google.com/go/networksecurity v0.9.1/go.mod h1:MCMdxOKQ30wsBI1eI659f9kEp4wuuAueoC9AJKSPWZQ= -cloud.google.com/go/networksecurity v0.9.2/go.mod h1:jG0SeAttWzPMUILEHDUvFYdQTl8L/E/KC8iZDj85lEI= -cloud.google.com/go/notebooks v1.2.0/go.mod h1:9+wtppMfVPUeJ8fIWPOq1UnATHISkGXGqTkxeieQ6UY= -cloud.google.com/go/notebooks v1.3.0/go.mod h1:bFR5lj07DtCPC7YAAJ//vHskFBxA5JzYlH68kXVdk34= -cloud.google.com/go/notebooks v1.4.0/go.mod h1:4QPMngcwmgb6uw7Po99B2xv5ufVoIQ7nOGDyL4P8AgA= -cloud.google.com/go/notebooks v1.5.0/go.mod h1:q8mwhnP9aR8Hpfnrc5iN5IBhrXUy8S2vuYs+kBJ/gu0= -cloud.google.com/go/notebooks v1.7.0/go.mod h1:PVlaDGfJgj1fl1S3dUwhFMXFgfYGhYQt2164xOMONmE= -cloud.google.com/go/notebooks v1.8.0/go.mod h1:Lq6dYKOYOWUCTvw5t2q1gp1lAp0zxAxRycayS0iJcqQ= -cloud.google.com/go/notebooks v1.9.1/go.mod h1:zqG9/gk05JrzgBt4ghLzEepPHNwE5jgPcHZRKhlC1A8= -cloud.google.com/go/notebooks v1.10.0/go.mod h1:SOPYMZnttHxqot0SGSFSkRrwE29eqnKPBJFqgWmiK2k= -cloud.google.com/go/notebooks v1.10.1/go.mod h1:5PdJc2SgAybE76kFQCWrTfJolCOUQXF97e+gteUUA6A= -cloud.google.com/go/optimization v1.1.0/go.mod h1:5po+wfvX5AQlPznyVEZjGJTMr4+CAkJf2XSTQOOl9l4= -cloud.google.com/go/optimization v1.2.0/go.mod h1:Lr7SOHdRDENsh+WXVmQhQTrzdu9ybg0NecjHidBq6xs= -cloud.google.com/go/optimization v1.3.1/go.mod h1:IvUSefKiwd1a5p0RgHDbWCIbDFgKuEdB+fPPuP0IDLI= -cloud.google.com/go/optimization v1.4.1/go.mod h1:j64vZQP7h9bO49m2rVaTVoNM0vEBEN5eKPUPbZyXOrk= -cloud.google.com/go/optimization v1.5.0/go.mod h1:evo1OvTxeBRBu6ydPlrIRizKY/LJKo/drDMMRKqGEUU= -cloud.google.com/go/optimization v1.5.1/go.mod h1:NC0gnUD5MWVAF7XLdoYVPmYYVth93Q6BUzqAq3ZwtV8= -cloud.google.com/go/orchestration v1.3.0/go.mod h1:Sj5tq/JpWiB//X/q3Ngwdl5K7B7Y0KZ7bfv0wL6fqVA= -cloud.google.com/go/orchestration v1.4.0/go.mod h1:6W5NLFWs2TlniBphAViZEVhrXRSMgUGDfW7vrWKvsBk= -cloud.google.com/go/orchestration v1.6.0/go.mod h1:M62Bevp7pkxStDfFfTuCOaXgaaqRAga1yKyoMtEoWPQ= -cloud.google.com/go/orchestration v1.8.1/go.mod h1:4sluRF3wgbYVRqz7zJ1/EUNc90TTprliq9477fGobD8= -cloud.google.com/go/orchestration v1.8.2/go.mod h1:T1cP+6WyTmh6LSZzeUhvGf0uZVmJyTx7t8z7Vg87+A0= -cloud.google.com/go/orgpolicy v1.4.0/go.mod h1:xrSLIV4RePWmP9P3tBl8S93lTmlAxjm06NSm2UTmKvE= -cloud.google.com/go/orgpolicy v1.5.0/go.mod h1:hZEc5q3wzwXJaKrsx5+Ewg0u1LxJ51nNFlext7Tanwc= -cloud.google.com/go/orgpolicy v1.10.0/go.mod h1:w1fo8b7rRqlXlIJbVhOMPrwVljyuW5mqssvBtU18ONc= -cloud.google.com/go/orgpolicy v1.11.0/go.mod h1:2RK748+FtVvnfuynxBzdnyu7sygtoZa1za/0ZfpOs1M= -cloud.google.com/go/orgpolicy v1.11.1/go.mod h1:8+E3jQcpZJQliP+zaFfayC2Pg5bmhuLK755wKhIIUCE= -cloud.google.com/go/orgpolicy v1.11.2/go.mod h1:biRDpNwfyytYnmCRWZWxrKF22Nkz9eNVj9zyaBdpm1o= -cloud.google.com/go/osconfig v1.7.0/go.mod h1:oVHeCeZELfJP7XLxcBGTMBvRO+1nQ5tFG9VQTmYS2Fs= -cloud.google.com/go/osconfig v1.8.0/go.mod h1:EQqZLu5w5XA7eKizepumcvWx+m8mJUhEwiPqWiZeEdg= -cloud.google.com/go/osconfig v1.9.0/go.mod h1:Yx+IeIZJ3bdWmzbQU4fxNl8xsZ4amB+dygAwFPlvnNo= -cloud.google.com/go/osconfig v1.10.0/go.mod h1:uMhCzqC5I8zfD9zDEAfvgVhDS8oIjySWh+l4WK6GnWw= -cloud.google.com/go/osconfig v1.11.0/go.mod h1:aDICxrur2ogRd9zY5ytBLV89KEgT2MKB2L/n6x1ooPw= -cloud.google.com/go/osconfig v1.12.0/go.mod h1:8f/PaYzoS3JMVfdfTubkowZYGmAhUCjjwnjqWI7NVBc= -cloud.google.com/go/osconfig v1.12.1/go.mod h1:4CjBxND0gswz2gfYRCUoUzCm9zCABp91EeTtWXyz0tE= -cloud.google.com/go/osconfig v1.12.2/go.mod h1:eh9GPaMZpI6mEJEuhEjUJmaxvQ3gav+fFEJon1Y8Iw0= -cloud.google.com/go/oslogin v1.4.0/go.mod h1:YdgMXWRaElXz/lDk1Na6Fh5orF7gvmJ0FGLIs9LId4E= -cloud.google.com/go/oslogin v1.5.0/go.mod h1:D260Qj11W2qx/HVF29zBg+0fd6YCSjSqLUkY/qEenQU= -cloud.google.com/go/oslogin v1.6.0/go.mod h1:zOJ1O3+dTU8WPlGEkFSh7qeHPPSoxrcMbbK1Nm2iX70= -cloud.google.com/go/oslogin v1.7.0/go.mod h1:e04SN0xO1UNJ1M5GP0vzVBFicIe4O53FOfcixIqTyXo= -cloud.google.com/go/oslogin v1.9.0/go.mod h1:HNavntnH8nzrn8JCTT5fj18FuJLFJc4NaZJtBnQtKFs= -cloud.google.com/go/oslogin v1.10.1/go.mod h1:x692z7yAue5nE7CsSnoG0aaMbNoRJRXO4sn73R+ZqAs= -cloud.google.com/go/oslogin v1.11.0/go.mod h1:8GMTJs4X2nOAUVJiPGqIWVcDaF0eniEto3xlOxaboXE= -cloud.google.com/go/oslogin v1.11.1/go.mod h1:OhD2icArCVNUxKqtK0mcSmKL7lgr0LVlQz+v9s1ujTg= -cloud.google.com/go/phishingprotection v0.5.0/go.mod h1:Y3HZknsK9bc9dMi+oE8Bim0lczMU6hrX0UpADuMefr0= -cloud.google.com/go/phishingprotection v0.6.0/go.mod h1:9Y3LBLgy0kDTcYET8ZH3bq/7qni15yVUoAxiFxnlSUA= -cloud.google.com/go/phishingprotection v0.7.0/go.mod h1:8qJI4QKHoda/sb/7/YmMQ2omRLSLYSu9bU0EKCNI+Lk= -cloud.google.com/go/phishingprotection v0.8.1/go.mod h1:AxonW7GovcA8qdEk13NfHq9hNx5KPtfxXNeUxTDxB6I= -cloud.google.com/go/phishingprotection v0.8.2/go.mod h1:LhJ91uyVHEYKSKcMGhOa14zMMWfbEdxG032oT6ECbC8= -cloud.google.com/go/policytroubleshooter v1.3.0/go.mod h1:qy0+VwANja+kKrjlQuOzmlvscn4RNsAc0e15GGqfMxg= -cloud.google.com/go/policytroubleshooter v1.4.0/go.mod h1:DZT4BcRw3QoO8ota9xw/LKtPa8lKeCByYeKTIf/vxdE= -cloud.google.com/go/policytroubleshooter v1.5.0/go.mod h1:Rz1WfV+1oIpPdN2VvvuboLVRsB1Hclg3CKQ53j9l8vw= -cloud.google.com/go/policytroubleshooter v1.6.0/go.mod h1:zYqaPTsmfvpjm5ULxAyD/lINQxJ0DDsnWOP/GZ7xzBc= -cloud.google.com/go/policytroubleshooter v1.7.1/go.mod h1:0NaT5v3Ag1M7U5r0GfDCpUFkWd9YqpubBWsQlhanRv0= -cloud.google.com/go/policytroubleshooter v1.8.0/go.mod h1:tmn5Ir5EToWe384EuboTcVQT7nTag2+DuH3uHmKd1HU= -cloud.google.com/go/policytroubleshooter v1.9.0/go.mod h1:+E2Lga7TycpeSTj2FsH4oXxTnrbHJGRlKhVZBLGgU64= -cloud.google.com/go/policytroubleshooter v1.9.1/go.mod h1:MYI8i0bCrL8cW+VHN1PoiBTyNZTstCg2WUw2eVC4c4U= -cloud.google.com/go/privatecatalog v0.5.0/go.mod h1:XgosMUvvPyxDjAVNDYxJ7wBW8//hLDDYmnsNcMGq1K0= -cloud.google.com/go/privatecatalog v0.6.0/go.mod h1:i/fbkZR0hLN29eEWiiwue8Pb+GforiEIBnV9yrRUOKI= -cloud.google.com/go/privatecatalog v0.7.0/go.mod h1:2s5ssIFO69F5csTXcwBP7NPFTZvps26xGzvQ2PQaBYg= -cloud.google.com/go/privatecatalog v0.8.0/go.mod h1:nQ6pfaegeDAq/Q5lrfCQzQLhubPiZhSaNhIgfJlnIXs= -cloud.google.com/go/privatecatalog v0.9.1/go.mod h1:0XlDXW2unJXdf9zFz968Hp35gl/bhF4twwpXZAW50JA= -cloud.google.com/go/privatecatalog v0.9.2/go.mod h1:RMA4ATa8IXfzvjrhhK8J6H4wwcztab+oZph3c6WmtFc= cloud.google.com/go/profiler v0.3.0 h1:R6y/xAeifaUXxd2x6w+jIwKxoKl8Cv5HJvcvASTPWJo= cloud.google.com/go/profiler v0.3.0/go.mod h1:9wYk9eY4iZHsev8TQb61kh3wiOiSyz/xOYixWPzweCU= 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/pubsub v1.26.0/go.mod h1:QgBH3U/jdJy/ftjPhTkyXNj543Tin1pRYcdcPRnFIRI= -cloud.google.com/go/pubsub v1.27.1/go.mod h1:hQN39ymbV9geqBnfQq6Xf63yNhUAhv9CZhzp5O6qsW0= -cloud.google.com/go/pubsub v1.28.0/go.mod h1:vuXFpwaVoIPQMGXqRyUQigu/AX1S3IWugR9xznmcXX8= -cloud.google.com/go/pubsub v1.30.0/go.mod h1:qWi1OPS0B+b5L+Sg6Gmc9zD1Y+HaM0MdUr7LsupY1P4= -cloud.google.com/go/pubsub v1.32.0/go.mod h1:f+w71I33OMyxf9VpMVcZbnG5KSUkCOUHYpFd5U1GdRc= -cloud.google.com/go/pubsub v1.33.0/go.mod h1:f+w71I33OMyxf9VpMVcZbnG5KSUkCOUHYpFd5U1GdRc= -cloud.google.com/go/pubsublite v1.5.0/go.mod h1:xapqNQ1CuLfGi23Yda/9l4bBCKz/wC3KIJ5gKcxveZg= -cloud.google.com/go/pubsublite v1.6.0/go.mod h1:1eFCS0U11xlOuMFV/0iBqw3zP12kddMeCbj/F3FSj9k= -cloud.google.com/go/pubsublite v1.7.0/go.mod h1:8hVMwRXfDfvGm3fahVbtDbiLePT3gpoiJYJY+vxWxVM= -cloud.google.com/go/pubsublite v1.8.1/go.mod h1:fOLdU4f5xldK4RGJrBMm+J7zMWNj/k4PxwEZXy39QS0= -cloud.google.com/go/recaptchaenterprise v1.3.1/go.mod h1:OdD+q+y4XGeAlxRaMn1Y7/GveP6zmq76byL6tjPE7d4= -cloud.google.com/go/recaptchaenterprise/v2 v2.1.0/go.mod h1:w9yVqajwroDNTfGuhmOjPDN//rZGySaf6PtFVcSCa7o= -cloud.google.com/go/recaptchaenterprise/v2 v2.2.0/go.mod h1:/Zu5jisWGeERrd5HnlS3EUGb/D335f9k51B/FVil0jk= -cloud.google.com/go/recaptchaenterprise/v2 v2.3.0/go.mod h1:O9LwGCjrhGHBQET5CA7dd5NwwNQUErSgEDit1DLNTdo= -cloud.google.com/go/recaptchaenterprise/v2 v2.4.0/go.mod h1:Am3LHfOuBstrLrNCBrlI5sbwx9LBg3te2N6hGvHn2mE= -cloud.google.com/go/recaptchaenterprise/v2 v2.5.0/go.mod h1:O8LzcHXN3rz0j+LBC91jrwI3R+1ZSZEWrfL7XHgNo9U= -cloud.google.com/go/recaptchaenterprise/v2 v2.6.0/go.mod h1:RPauz9jeLtB3JVzg6nCbe12qNoaa8pXc4d/YukAmcnA= -cloud.google.com/go/recaptchaenterprise/v2 v2.7.0/go.mod h1:19wVj/fs5RtYtynAPJdDTb69oW0vNHYDBTbB4NvMD9c= -cloud.google.com/go/recaptchaenterprise/v2 v2.7.2/go.mod h1:kR0KjsJS7Jt1YSyWFkseQ756D45kaYNTlDPPaRAvDBU= -cloud.google.com/go/recaptchaenterprise/v2 v2.8.0/go.mod h1:QuE8EdU9dEnesG8/kG3XuJyNsjEqMlMzg3v3scCJ46c= -cloud.google.com/go/recaptchaenterprise/v2 v2.8.1/go.mod h1:JZYZJOeZjgSSTGP4uz7NlQ4/d1w5hGmksVgM0lbEij0= -cloud.google.com/go/recommendationengine v0.5.0/go.mod h1:E5756pJcVFeVgaQv3WNpImkFP8a+RptV6dDLGPILjvg= -cloud.google.com/go/recommendationengine v0.6.0/go.mod h1:08mq2umu9oIqc7tDy8sx+MNJdLG0fUi3vaSVbztHgJ4= -cloud.google.com/go/recommendationengine v0.7.0/go.mod h1:1reUcE3GIu6MeBz/h5xZJqNLuuVjNg1lmWMPyjatzac= -cloud.google.com/go/recommendationengine v0.8.1/go.mod h1:MrZihWwtFYWDzE6Hz5nKcNz3gLizXVIDI/o3G1DLcrE= -cloud.google.com/go/recommendationengine v0.8.2/go.mod h1:QIybYHPK58qir9CV2ix/re/M//Ty10OxjnnhWdaKS1Y= -cloud.google.com/go/recommender v1.5.0/go.mod h1:jdoeiBIVrJe9gQjwd759ecLJbxCDED4A6p+mqoqDvTg= -cloud.google.com/go/recommender v1.6.0/go.mod h1:+yETpm25mcoiECKh9DEScGzIRyDKpZ0cEhWGo+8bo+c= -cloud.google.com/go/recommender v1.7.0/go.mod h1:XLHs/W+T8olwlGOgfQenXBTbIseGclClff6lhFVe9Bs= -cloud.google.com/go/recommender v1.8.0/go.mod h1:PkjXrTT05BFKwxaUxQmtIlrtj0kph108r02ZZQ5FE70= -cloud.google.com/go/recommender v1.9.0/go.mod h1:PnSsnZY7q+VL1uax2JWkt/UegHssxjUVVCrX52CuEmQ= -cloud.google.com/go/recommender v1.10.1/go.mod h1:XFvrE4Suqn5Cq0Lf+mCP6oBHD/yRMA8XxP5sb7Q7gpA= -cloud.google.com/go/recommender v1.11.0/go.mod h1:kPiRQhPyTJ9kyXPCG6u/dlPLbYfFlkwHNRwdzPVAoII= -cloud.google.com/go/recommender v1.11.1/go.mod h1:sGwFFAyI57v2Hc5LbIj+lTwXipGu9NW015rkaEM5B18= -cloud.google.com/go/redis v1.7.0/go.mod h1:V3x5Jq1jzUcg+UNsRvdmsfuFnit1cfe3Z/PGyq/lm4Y= -cloud.google.com/go/redis v1.8.0/go.mod h1:Fm2szCDavWzBk2cDKxrkmWBqoCiL1+Ctwq7EyqBCA/A= -cloud.google.com/go/redis v1.9.0/go.mod h1:HMYQuajvb2D0LvMgZmLDZW8V5aOC/WxstZHiy4g8OiA= -cloud.google.com/go/redis v1.10.0/go.mod h1:ThJf3mMBQtW18JzGgh41/Wld6vnDDc/F/F35UolRZPM= -cloud.google.com/go/redis v1.11.0/go.mod h1:/X6eicana+BWcUda5PpwZC48o37SiFVTFSs0fWAJ7uQ= -cloud.google.com/go/redis v1.13.1/go.mod h1:VP7DGLpE91M6bcsDdMuyCm2hIpB6Vp2hI090Mfd1tcg= -cloud.google.com/go/redis v1.13.2/go.mod h1:0Hg7pCMXS9uz02q+LoEVl5dNHUkIQv+C/3L76fandSA= -cloud.google.com/go/resourcemanager v1.3.0/go.mod h1:bAtrTjZQFJkiWTPDb1WBjzvc6/kifjj4QBYuKCCoqKA= -cloud.google.com/go/resourcemanager v1.4.0/go.mod h1:MwxuzkumyTX7/a3n37gmsT3py7LIXwrShilPh3P1tR0= -cloud.google.com/go/resourcemanager v1.5.0/go.mod h1:eQoXNAiAvCf5PXxWxXjhKQoTMaUSNrEfg+6qdf/wots= -cloud.google.com/go/resourcemanager v1.6.0/go.mod h1:YcpXGRs8fDzcUl1Xw8uOVmI8JEadvhRIkoXXUNVYcVo= -cloud.google.com/go/resourcemanager v1.7.0/go.mod h1:HlD3m6+bwhzj9XCouqmeiGuni95NTrExfhoSrkC/3EI= -cloud.google.com/go/resourcemanager v1.9.1/go.mod h1:dVCuosgrh1tINZ/RwBufr8lULmWGOkPS8gL5gqyjdT8= -cloud.google.com/go/resourcemanager v1.9.2/go.mod h1:OujkBg1UZg5lX2yIyMo5Vz9O5hf7XQOSV7WxqxxMtQE= -cloud.google.com/go/resourcesettings v1.3.0/go.mod h1:lzew8VfESA5DQ8gdlHwMrqZs1S9V87v3oCnKCWoOuQU= -cloud.google.com/go/resourcesettings v1.4.0/go.mod h1:ldiH9IJpcrlC3VSuCGvjR5of/ezRrOxFtpJoJo5SmXg= -cloud.google.com/go/resourcesettings v1.5.0/go.mod h1:+xJF7QSG6undsQDfsCJyqWXyBwUoJLhetkRMDRnIoXA= -cloud.google.com/go/resourcesettings v1.6.1/go.mod h1:M7mk9PIZrC5Fgsu1kZJci6mpgN8o0IUzVx3eJU3y4Jw= -cloud.google.com/go/resourcesettings v1.6.2/go.mod h1:mJIEDd9MobzunWMeniaMp6tzg4I2GvD3TTmPkc8vBXk= -cloud.google.com/go/retail v1.8.0/go.mod h1:QblKS8waDmNUhghY2TI9O3JLlFk8jybHeV4BF19FrE4= -cloud.google.com/go/retail v1.9.0/go.mod h1:g6jb6mKuCS1QKnH/dpu7isX253absFl6iE92nHwlBUY= -cloud.google.com/go/retail v1.10.0/go.mod h1:2gDk9HsL4HMS4oZwz6daui2/jmKvqShXKQuB2RZ+cCc= -cloud.google.com/go/retail v1.11.0/go.mod h1:MBLk1NaWPmh6iVFSz9MeKG/Psyd7TAgm6y/9L2B4x9Y= -cloud.google.com/go/retail v1.12.0/go.mod h1:UMkelN/0Z8XvKymXFbD4EhFJlYKRx1FGhQkVPU5kF14= -cloud.google.com/go/retail v1.14.1/go.mod h1:y3Wv3Vr2k54dLNIrCzenyKG8g8dhvhncT2NcNjb/6gE= -cloud.google.com/go/retail v1.14.2/go.mod h1:W7rrNRChAEChX336QF7bnMxbsjugcOCPU44i5kbLiL8= -cloud.google.com/go/run v0.2.0/go.mod h1:CNtKsTA1sDcnqqIFR3Pb5Tq0usWxJJvsWOCPldRU3Do= -cloud.google.com/go/run v0.3.0/go.mod h1:TuyY1+taHxTjrD0ZFk2iAR+xyOXEA0ztb7U3UNA0zBo= -cloud.google.com/go/run v0.8.0/go.mod h1:VniEnuBwqjigv0A7ONfQUaEItaiCRVujlMqerPPiktM= -cloud.google.com/go/run v0.9.0/go.mod h1:Wwu+/vvg8Y+JUApMwEDfVfhetv30hCG4ZwDR/IXl2Qg= -cloud.google.com/go/run v1.2.0/go.mod h1:36V1IlDzQ0XxbQjUx6IYbw8H3TJnWvhii963WW3B/bo= -cloud.google.com/go/run v1.3.0/go.mod h1:S/osX/4jIPZGg+ssuqh6GNgg7syixKe3YnprwehzHKU= -cloud.google.com/go/run v1.3.1/go.mod h1:cymddtZOzdwLIAsmS6s+Asl4JoXIDm/K1cpZTxV4Q5s= -cloud.google.com/go/scheduler v1.4.0/go.mod h1:drcJBmxF3aqZJRhmkHQ9b3uSSpQoltBPGPxGAWROx6s= -cloud.google.com/go/scheduler v1.5.0/go.mod h1:ri073ym49NW3AfT6DZi21vLZrG07GXr5p3H1KxN5QlI= -cloud.google.com/go/scheduler v1.6.0/go.mod h1:SgeKVM7MIwPn3BqtcBntpLyrIJftQISRrYB5ZtT+KOk= -cloud.google.com/go/scheduler v1.7.0/go.mod h1:jyCiBqWW956uBjjPMMuX09n3x37mtyPJegEWKxRsn44= -cloud.google.com/go/scheduler v1.8.0/go.mod h1:TCET+Y5Gp1YgHT8py4nlg2Sew8nUHMqcpousDgXJVQc= -cloud.google.com/go/scheduler v1.9.0/go.mod h1:yexg5t+KSmqu+njTIh3b7oYPheFtBWGcbVUYF1GGMIc= -cloud.google.com/go/scheduler v1.10.1/go.mod h1:R63Ldltd47Bs4gnhQkmNDse5w8gBRrhObZ54PxgR2Oo= -cloud.google.com/go/scheduler v1.10.2/go.mod h1:O3jX6HRH5eKCA3FutMw375XHZJudNIKVonSCHv7ropY= -cloud.google.com/go/secretmanager v1.6.0/go.mod h1:awVa/OXF6IiyaU1wQ34inzQNc4ISIDIrId8qE5QGgKA= -cloud.google.com/go/secretmanager v1.8.0/go.mod h1:hnVgi/bN5MYHd3Gt0SPuTPPp5ENina1/LxM+2W9U9J4= -cloud.google.com/go/secretmanager v1.9.0/go.mod h1:b71qH2l1yHmWQHt9LC80akm86mX8AL6X1MA01dW8ht4= -cloud.google.com/go/secretmanager v1.10.0/go.mod h1:MfnrdvKMPNra9aZtQFvBcvRU54hbPD8/HayQdlUgJpU= -cloud.google.com/go/secretmanager v1.11.1/go.mod h1:znq9JlXgTNdBeQk9TBW/FnR/W4uChEKGeqQWAJ8SXFw= -cloud.google.com/go/secretmanager v1.11.2/go.mod h1:MQm4t3deoSub7+WNwiC4/tRYgDBHJgJPvswqQVB1Vss= -cloud.google.com/go/security v1.5.0/go.mod h1:lgxGdyOKKjHL4YG3/YwIL2zLqMFCKs0UbQwgyZmfJl4= -cloud.google.com/go/security v1.7.0/go.mod h1:mZklORHl6Bg7CNnnjLH//0UlAlaXqiG7Lb9PsPXLfD0= -cloud.google.com/go/security v1.8.0/go.mod h1:hAQOwgmaHhztFhiQ41CjDODdWP0+AE1B3sX4OFlq+GU= -cloud.google.com/go/security v1.9.0/go.mod h1:6Ta1bO8LXI89nZnmnsZGp9lVoVWXqsVbIq/t9dzI+2Q= -cloud.google.com/go/security v1.10.0/go.mod h1:QtOMZByJVlibUT2h9afNDWRZ1G96gVywH8T5GUSb9IA= -cloud.google.com/go/security v1.12.0/go.mod h1:rV6EhrpbNHrrxqlvW0BWAIawFWq3X90SduMJdFwtLB8= -cloud.google.com/go/security v1.13.0/go.mod h1:Q1Nvxl1PAgmeW0y3HTt54JYIvUdtcpYKVfIB8AOMZ+0= -cloud.google.com/go/security v1.15.1/go.mod h1:MvTnnbsWnehoizHi09zoiZob0iCHVcL4AUBj76h9fXA= -cloud.google.com/go/security v1.15.2/go.mod h1:2GVE/v1oixIRHDaClVbHuPcZwAqFM28mXuAKCfMgYIg= -cloud.google.com/go/securitycenter v1.13.0/go.mod h1:cv5qNAqjY84FCN6Y9z28WlkKXyWsgLO832YiWwkCWcU= -cloud.google.com/go/securitycenter v1.14.0/go.mod h1:gZLAhtyKv85n52XYWt6RmeBdydyxfPeTrpToDPw4Auc= -cloud.google.com/go/securitycenter v1.15.0/go.mod h1:PeKJ0t8MoFmmXLXWm41JidyzI3PJjd8sXWaVqg43WWk= -cloud.google.com/go/securitycenter v1.16.0/go.mod h1:Q9GMaLQFUD+5ZTabrbujNWLtSLZIZF7SAR0wWECrjdk= -cloud.google.com/go/securitycenter v1.18.1/go.mod h1:0/25gAzCM/9OL9vVx4ChPeM/+DlfGQJDwBy/UC8AKK0= -cloud.google.com/go/securitycenter v1.19.0/go.mod h1:LVLmSg8ZkkyaNy4u7HCIshAngSQ8EcIRREP3xBnyfag= -cloud.google.com/go/securitycenter v1.23.0/go.mod h1:8pwQ4n+Y9WCWM278R8W3nF65QtY172h4S8aXyI9/hsQ= -cloud.google.com/go/securitycenter v1.23.1/go.mod h1:w2HV3Mv/yKhbXKwOCu2i8bCuLtNP1IMHuiYQn4HJq5s= -cloud.google.com/go/servicecontrol v1.4.0/go.mod h1:o0hUSJ1TXJAmi/7fLJAedOovnujSEvjKCAFNXPQ1RaU= -cloud.google.com/go/servicecontrol v1.5.0/go.mod h1:qM0CnXHhyqKVuiZnGKrIurvVImCs8gmqWsDoqe9sU1s= -cloud.google.com/go/servicecontrol v1.10.0/go.mod h1:pQvyvSRh7YzUF2efw7H87V92mxU8FnFDawMClGCNuAA= -cloud.google.com/go/servicecontrol v1.11.0/go.mod h1:kFmTzYzTUIuZs0ycVqRHNaNhgR+UMUpw9n02l/pY+mc= -cloud.google.com/go/servicecontrol v1.11.1/go.mod h1:aSnNNlwEFBY+PWGQ2DoM0JJ/QUXqV5/ZD9DOLB7SnUk= -cloud.google.com/go/servicedirectory v1.4.0/go.mod h1:gH1MUaZCgtP7qQiI+F+A+OpeKF/HQWgtAddhTbhL2bs= -cloud.google.com/go/servicedirectory v1.5.0/go.mod h1:QMKFL0NUySbpZJ1UZs3oFAmdvVxhhxB6eJ/Vlp73dfg= -cloud.google.com/go/servicedirectory v1.6.0/go.mod h1:pUlbnWsLH9c13yGkxCmfumWEPjsRs1RlmJ4pqiNjVL4= -cloud.google.com/go/servicedirectory v1.7.0/go.mod h1:5p/U5oyvgYGYejufvxhgwjL8UVXjkuw7q5XcG10wx1U= -cloud.google.com/go/servicedirectory v1.8.0/go.mod h1:srXodfhY1GFIPvltunswqXpVxFPpZjf8nkKQT7XcXaY= -cloud.google.com/go/servicedirectory v1.9.0/go.mod h1:29je5JjiygNYlmsGz8k6o+OZ8vd4f//bQLtvzkPPT/s= -cloud.google.com/go/servicedirectory v1.10.1/go.mod h1:Xv0YVH8s4pVOwfM/1eMTl0XJ6bzIOSLDt8f8eLaGOxQ= -cloud.google.com/go/servicedirectory v1.11.0/go.mod h1:Xv0YVH8s4pVOwfM/1eMTl0XJ6bzIOSLDt8f8eLaGOxQ= -cloud.google.com/go/servicedirectory v1.11.1/go.mod h1:tJywXimEWzNzw9FvtNjsQxxJ3/41jseeILgwU/QLrGI= -cloud.google.com/go/servicemanagement v1.4.0/go.mod h1:d8t8MDbezI7Z2R1O/wu8oTggo3BI2GKYbdG4y/SJTco= -cloud.google.com/go/servicemanagement v1.5.0/go.mod h1:XGaCRe57kfqu4+lRxaFEAuqmjzF0r+gWHjWqKqBvKFo= -cloud.google.com/go/servicemanagement v1.6.0/go.mod h1:aWns7EeeCOtGEX4OvZUWCCJONRZeFKiptqKf1D0l/Jc= -cloud.google.com/go/servicemanagement v1.8.0/go.mod h1:MSS2TDlIEQD/fzsSGfCdJItQveu9NXnUniTrq/L8LK4= -cloud.google.com/go/serviceusage v1.3.0/go.mod h1:Hya1cozXM4SeSKTAgGXgj97GlqUvF5JaoXacR1JTP/E= -cloud.google.com/go/serviceusage v1.4.0/go.mod h1:SB4yxXSaYVuUBYUml6qklyONXNLt83U0Rb+CXyhjEeU= -cloud.google.com/go/serviceusage v1.5.0/go.mod h1:w8U1JvqUqwJNPEOTQjrMHkw3IaIFLoLsPLvsE3xueec= -cloud.google.com/go/serviceusage v1.6.0/go.mod h1:R5wwQcbOWsyuOfbP9tGdAnCAc6B9DRwPG1xtWMDeuPA= -cloud.google.com/go/shell v1.3.0/go.mod h1:VZ9HmRjZBsjLGXusm7K5Q5lzzByZmJHf1d0IWHEN5X4= -cloud.google.com/go/shell v1.4.0/go.mod h1:HDxPzZf3GkDdhExzD/gs8Grqk+dmYcEjGShZgYa9URw= -cloud.google.com/go/shell v1.6.0/go.mod h1:oHO8QACS90luWgxP3N9iZVuEiSF84zNyLytb+qE2f9A= -cloud.google.com/go/shell v1.7.1/go.mod h1:u1RaM+huXFaTojTbW4g9P5emOrrmLE69KrxqQahKn4g= -cloud.google.com/go/shell v1.7.2/go.mod h1:KqRPKwBV0UyLickMn0+BY1qIyE98kKyI216sH/TuHmc= -cloud.google.com/go/spanner v1.41.0/go.mod h1:MLYDBJR/dY4Wt7ZaMIQ7rXOTLjYrmxLE/5ve9vFfWos= -cloud.google.com/go/spanner v1.44.0/go.mod h1:G8XIgYdOK+Fbcpbs7p2fiprDw4CaZX63whnSMLVBxjk= -cloud.google.com/go/spanner v1.45.0/go.mod h1:FIws5LowYz8YAE1J8fOS7DJup8ff7xJeetWEo5REA2M= -cloud.google.com/go/spanner v1.47.0/go.mod h1:IXsJwVW2j4UKs0eYDqodab6HgGuA1bViSqW4uH9lfUI= -cloud.google.com/go/spanner v1.49.0/go.mod h1:eGj9mQGK8+hkgSVbHNQ06pQ4oS+cyc4tXXd6Dif1KoM= -cloud.google.com/go/spanner v1.50.0/go.mod h1:eGj9mQGK8+hkgSVbHNQ06pQ4oS+cyc4tXXd6Dif1KoM= -cloud.google.com/go/speech v1.6.0/go.mod h1:79tcr4FHCimOp56lwC01xnt/WPJZc4v3gzyT7FoBkCM= -cloud.google.com/go/speech v1.7.0/go.mod h1:KptqL+BAQIhMsj1kOP2la5DSEEerPDuOP/2mmkhHhZQ= -cloud.google.com/go/speech v1.8.0/go.mod h1:9bYIl1/tjsAnMgKGHKmBZzXKEkGgtU+MpdDPTE9f7y0= -cloud.google.com/go/speech v1.9.0/go.mod h1:xQ0jTcmnRFFM2RfX/U+rk6FQNUF6DQlydUSyoooSpco= -cloud.google.com/go/speech v1.14.1/go.mod h1:gEosVRPJ9waG7zqqnsHpYTOoAS4KouMRLDFMekpJ0J0= -cloud.google.com/go/speech v1.15.0/go.mod h1:y6oH7GhqCaZANH7+Oe0BhgIogsNInLlz542tg3VqeYI= -cloud.google.com/go/speech v1.17.1/go.mod h1:8rVNzU43tQvxDaGvqOhpDqgkJTFowBpDvCJ14kGlJYo= -cloud.google.com/go/speech v1.19.0/go.mod h1:8rVNzU43tQvxDaGvqOhpDqgkJTFowBpDvCJ14kGlJYo= -cloud.google.com/go/speech v1.19.1/go.mod h1:WcuaWz/3hOlzPFOVo9DUsblMIHwxP589y6ZMtaG+iAA= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= -cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= -cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc= -cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= -cloud.google.com/go/storage v1.28.1/go.mod h1:Qnisd4CqDdo6BGs2AD5LLnEsmSQ80wQ5ogcBBKhU86Y= -cloud.google.com/go/storage v1.29.0/go.mod h1:4puEjyTKnku6gfKoTfNOU/W+a9JyuVNxjpS5GBrB8h4= -cloud.google.com/go/storage v1.30.1/go.mod h1:NfxhC0UJE1aXSx7CIIbCf7y9HKT7BiccwkR7+P7gN8E= cloud.google.com/go/storage v1.36.0 h1:P0mOkAcaJxhCTvAkMhxMfrTKiNcub4YmmPBtlhAyTr8= cloud.google.com/go/storage v1.36.0/go.mod h1:M6M/3V/D3KpzMTJyPOR/HU6n2Si5QdaXYEsng2xgOs8= -cloud.google.com/go/storagetransfer v1.5.0/go.mod h1:dxNzUopWy7RQevYFHewchb29POFv3/AaBgnhqzqiK0w= -cloud.google.com/go/storagetransfer v1.6.0/go.mod h1:y77xm4CQV/ZhFZH75PLEXY0ROiS7Gh6pSKrM8dJyg6I= -cloud.google.com/go/storagetransfer v1.7.0/go.mod h1:8Giuj1QNb1kfLAiWM1bN6dHzfdlDAVC9rv9abHot2W4= -cloud.google.com/go/storagetransfer v1.8.0/go.mod h1:JpegsHHU1eXg7lMHkvf+KE5XDJ7EQu0GwNJbbVGanEw= -cloud.google.com/go/storagetransfer v1.10.0/go.mod h1:DM4sTlSmGiNczmV6iZyceIh2dbs+7z2Ayg6YAiQlYfA= -cloud.google.com/go/storagetransfer v1.10.1/go.mod h1:rS7Sy0BtPviWYTTJVWCSV4QrbBitgPeuK4/FKa4IdLs= -cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw= -cloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g= -cloud.google.com/go/talent v1.3.0/go.mod h1:CmcxwJ/PKfRgd1pBjQgU6W3YBwiewmUzQYH5HHmSCmM= -cloud.google.com/go/talent v1.4.0/go.mod h1:ezFtAgVuRf8jRsvyE6EwmbTK5LKciD4KVnHuDEFmOOA= -cloud.google.com/go/talent v1.5.0/go.mod h1:G+ODMj9bsasAEJkQSzO2uHQWXHHXUomArjWQQYkqK6c= -cloud.google.com/go/talent v1.6.2/go.mod h1:CbGvmKCG61mkdjcqTcLOkb2ZN1SrQI8MDyma2l7VD24= -cloud.google.com/go/talent v1.6.3/go.mod h1:xoDO97Qd4AK43rGjJvyBHMskiEf3KulgYzcH6YWOVoo= -cloud.google.com/go/texttospeech v1.4.0/go.mod h1:FX8HQHA6sEpJ7rCMSfXuzBcysDAuWusNNNvN9FELDd8= -cloud.google.com/go/texttospeech v1.5.0/go.mod h1:oKPLhR4n4ZdQqWKURdwxMy0uiTS1xU161C8W57Wkea4= -cloud.google.com/go/texttospeech v1.6.0/go.mod h1:YmwmFT8pj1aBblQOI3TfKmwibnsfvhIBzPXcW4EBovc= -cloud.google.com/go/texttospeech v1.7.1/go.mod h1:m7QfG5IXxeneGqTapXNxv2ItxP/FS0hCZBwXYqucgSk= -cloud.google.com/go/texttospeech v1.7.2/go.mod h1:VYPT6aTOEl3herQjFHYErTlSZJ4vB00Q2ZTmuVgluD4= -cloud.google.com/go/tpu v1.3.0/go.mod h1:aJIManG0o20tfDQlRIej44FcwGGl/cD0oiRyMKG19IQ= -cloud.google.com/go/tpu v1.4.0/go.mod h1:mjZaX8p0VBgllCzF6wcU2ovUXN9TONFLd7iz227X2Xg= -cloud.google.com/go/tpu v1.5.0/go.mod h1:8zVo1rYDFuW2l4yZVY0R0fb/v44xLh3llq7RuV61fPM= -cloud.google.com/go/tpu v1.6.1/go.mod h1:sOdcHVIgDEEOKuqUoi6Fq53MKHJAtOwtz0GuKsWSH3E= -cloud.google.com/go/tpu v1.6.2/go.mod h1:NXh3NDwt71TsPZdtGWgAG5ThDfGd32X1mJ2cMaRlVgU= -cloud.google.com/go/trace v1.3.0/go.mod h1:FFUE83d9Ca57C+K8rDl/Ih8LwOzWIV1krKgxg6N0G28= -cloud.google.com/go/trace v1.4.0/go.mod h1:UG0v8UBqzusp+z63o7FK74SdFE+AXpCLdFb1rshXG+Y= -cloud.google.com/go/trace v1.8.0/go.mod h1:zH7vcsbAhklH8hWFig58HvxcxyQbaIqMarMg9hn5ECA= -cloud.google.com/go/trace v1.9.0/go.mod h1:lOQqpE5IaWY0Ixg7/r2SjixMuc6lfTFeO4QGM4dQWOk= -cloud.google.com/go/trace v1.10.1/go.mod h1:gbtL94KE5AJLH3y+WVpfWILmqgc6dXcqgNXdOPAQTYk= -cloud.google.com/go/trace v1.10.2/go.mod h1:NPXemMi6MToRFcSxRl2uDnu/qAlAQ3oULUphcHGh1vA= -cloud.google.com/go/translate v1.3.0/go.mod h1:gzMUwRjvOqj5i69y/LYLd8RrNQk+hOmIXTi9+nb3Djs= -cloud.google.com/go/translate v1.4.0/go.mod h1:06Dn/ppvLD6WvA5Rhdp029IX2Mi3Mn7fpMRLPvXT5Wg= -cloud.google.com/go/translate v1.5.0/go.mod h1:29YDSYveqqpA1CQFD7NQuP49xymq17RXNaUDdc0mNu0= -cloud.google.com/go/translate v1.6.0/go.mod h1:lMGRudH1pu7I3n3PETiOB2507gf3HnfLV8qlkHZEyos= -cloud.google.com/go/translate v1.7.0/go.mod h1:lMGRudH1pu7I3n3PETiOB2507gf3HnfLV8qlkHZEyos= -cloud.google.com/go/translate v1.8.1/go.mod h1:d1ZH5aaOA0CNhWeXeC8ujd4tdCFw8XoNWRljklu5RHs= -cloud.google.com/go/translate v1.8.2/go.mod h1:d1ZH5aaOA0CNhWeXeC8ujd4tdCFw8XoNWRljklu5RHs= -cloud.google.com/go/translate v1.9.0/go.mod h1:d1ZH5aaOA0CNhWeXeC8ujd4tdCFw8XoNWRljklu5RHs= -cloud.google.com/go/translate v1.9.1/go.mod h1:TWIgDZknq2+JD4iRcojgeDtqGEp154HN/uL6hMvylS8= -cloud.google.com/go/video v1.8.0/go.mod h1:sTzKFc0bUSByE8Yoh8X0mn8bMymItVGPfTuUBUyRgxk= -cloud.google.com/go/video v1.9.0/go.mod h1:0RhNKFRF5v92f8dQt0yhaHrEuH95m068JYOvLZYnJSw= -cloud.google.com/go/video v1.12.0/go.mod h1:MLQew95eTuaNDEGriQdcYn0dTwf9oWiA4uYebxM5kdg= -cloud.google.com/go/video v1.13.0/go.mod h1:ulzkYlYgCp15N2AokzKjy7MQ9ejuynOJdf1tR5lGthk= -cloud.google.com/go/video v1.14.0/go.mod h1:SkgaXwT+lIIAKqWAJfktHT/RbgjSuY6DobxEp0C5yTQ= -cloud.google.com/go/video v1.15.0/go.mod h1:SkgaXwT+lIIAKqWAJfktHT/RbgjSuY6DobxEp0C5yTQ= -cloud.google.com/go/video v1.17.1/go.mod h1:9qmqPqw/Ib2tLqaeHgtakU+l5TcJxCJbhFXM7UJjVzU= -cloud.google.com/go/video v1.19.0/go.mod h1:9qmqPqw/Ib2tLqaeHgtakU+l5TcJxCJbhFXM7UJjVzU= -cloud.google.com/go/video v1.20.0/go.mod h1:U3G3FTnsvAGqglq9LxgqzOiBc/Nt8zis8S+850N2DUM= -cloud.google.com/go/video v1.20.1/go.mod h1:3gJS+iDprnj8SY6pe0SwLeC5BUW80NjhwX7INWEuWGU= -cloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU= -cloud.google.com/go/videointelligence v1.7.0/go.mod h1:k8pI/1wAhjznARtVT9U1llUaFNPh7muw8QyOUpavru4= -cloud.google.com/go/videointelligence v1.8.0/go.mod h1:dIcCn4gVDdS7yte/w+koiXn5dWVplOZkE+xwG9FgK+M= -cloud.google.com/go/videointelligence v1.9.0/go.mod h1:29lVRMPDYHikk3v8EdPSaL8Ku+eMzDljjuvRs105XoU= -cloud.google.com/go/videointelligence v1.10.0/go.mod h1:LHZngX1liVtUhZvi2uNS0VQuOzNi2TkY1OakiuoUOjU= -cloud.google.com/go/videointelligence v1.11.1/go.mod h1:76xn/8InyQHarjTWsBR058SmlPCwQjgcvoW0aZykOvo= -cloud.google.com/go/videointelligence v1.11.2/go.mod h1:ocfIGYtIVmIcWk1DsSGOoDiXca4vaZQII1C85qtoplc= -cloud.google.com/go/vision v1.2.0/go.mod h1:SmNwgObm5DpFBme2xpyOyasvBc1aPdjvMk2bBk0tKD0= -cloud.google.com/go/vision/v2 v2.2.0/go.mod h1:uCdV4PpN1S0jyCyq8sIM42v2Y6zOLkZs+4R9LrGYwFo= -cloud.google.com/go/vision/v2 v2.3.0/go.mod h1:UO61abBx9QRMFkNBbf1D8B1LXdS2cGiiCRx0vSpZoUo= -cloud.google.com/go/vision/v2 v2.4.0/go.mod h1:VtI579ll9RpVTrdKdkMzckdnwMyX2JILb+MhPqRbPsY= -cloud.google.com/go/vision/v2 v2.5.0/go.mod h1:MmaezXOOE+IWa+cS7OhRRLK2cNv1ZL98zhqFFZaaH2E= -cloud.google.com/go/vision/v2 v2.6.0/go.mod h1:158Hes0MvOS9Z/bDMSFpjwsUrZ5fPrdwuyyvKSGAGMY= -cloud.google.com/go/vision/v2 v2.7.0/go.mod h1:H89VysHy21avemp6xcf9b9JvZHVehWbET0uT/bcuY/0= -cloud.google.com/go/vision/v2 v2.7.2/go.mod h1:jKa8oSYBWhYiXarHPvP4USxYANYUEdEsQrloLjrSwJU= -cloud.google.com/go/vision/v2 v2.7.3/go.mod h1:V0IcLCY7W+hpMKXK1JYE0LV5llEqVmj+UJChjvA1WsM= -cloud.google.com/go/vmmigration v1.2.0/go.mod h1:IRf0o7myyWFSmVR1ItrBSFLFD/rJkfDCUTO4vLlJvsE= -cloud.google.com/go/vmmigration v1.3.0/go.mod h1:oGJ6ZgGPQOFdjHuocGcLqX4lc98YQ7Ygq8YQwHh9A7g= -cloud.google.com/go/vmmigration v1.5.0/go.mod h1:E4YQ8q7/4W9gobHjQg4JJSgXXSgY21nA5r8swQV+Xxc= -cloud.google.com/go/vmmigration v1.6.0/go.mod h1:bopQ/g4z+8qXzichC7GW1w2MjbErL54rk3/C843CjfY= -cloud.google.com/go/vmmigration v1.7.1/go.mod h1:WD+5z7a/IpZ5bKK//YmT9E047AD+rjycCAvyMxGJbro= -cloud.google.com/go/vmmigration v1.7.2/go.mod h1:iA2hVj22sm2LLYXGPT1pB63mXHhrH1m/ruux9TwWLd8= -cloud.google.com/go/vmwareengine v0.1.0/go.mod h1:RsdNEf/8UDvKllXhMz5J40XxDrNJNN4sagiox+OI208= -cloud.google.com/go/vmwareengine v0.2.2/go.mod h1:sKdctNJxb3KLZkE/6Oui94iw/xs9PRNC2wnNLXsHvH8= -cloud.google.com/go/vmwareengine v0.3.0/go.mod h1:wvoyMvNWdIzxMYSpH/R7y2h5h3WFkx6d+1TIsP39WGY= -cloud.google.com/go/vmwareengine v0.4.1/go.mod h1:Px64x+BvjPZwWuc4HdmVhoygcXqEkGHXoa7uyfTgSI0= -cloud.google.com/go/vmwareengine v1.0.0/go.mod h1:Px64x+BvjPZwWuc4HdmVhoygcXqEkGHXoa7uyfTgSI0= -cloud.google.com/go/vmwareengine v1.0.1/go.mod h1:aT3Xsm5sNx0QShk1Jc1B8OddrxAScYLwzVoaiXfdzzk= -cloud.google.com/go/vpcaccess v1.4.0/go.mod h1:aQHVbTWDYUR1EbTApSVvMq1EnT57ppDmQzZ3imqIk4w= -cloud.google.com/go/vpcaccess v1.5.0/go.mod h1:drmg4HLk9NkZpGfCmZ3Tz0Bwnm2+DKqViEpeEpOq0m8= -cloud.google.com/go/vpcaccess v1.6.0/go.mod h1:wX2ILaNhe7TlVa4vC5xce1bCnqE3AeH27RV31lnmZes= -cloud.google.com/go/vpcaccess v1.7.1/go.mod h1:FogoD46/ZU+JUBX9D606X21EnxiszYi2tArQwLY4SXs= -cloud.google.com/go/vpcaccess v1.7.2/go.mod h1:mmg/MnRHv+3e8FJUjeSibVFvQF1cCy2MsFaFqxeY1HU= -cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xXZmFiHmGE= -cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg= -cloud.google.com/go/webrisk v1.6.0/go.mod h1:65sW9V9rOosnc9ZY7A7jsy1zoHS5W9IAXv6dGqhMQMc= -cloud.google.com/go/webrisk v1.7.0/go.mod h1:mVMHgEYH0r337nmt1JyLthzMr6YxwN1aAIEc2fTcq7A= -cloud.google.com/go/webrisk v1.8.0/go.mod h1:oJPDuamzHXgUc+b8SiHRcVInZQuybnvEW72PqTc7sSg= -cloud.google.com/go/webrisk v1.9.1/go.mod h1:4GCmXKcOa2BZcZPn6DCEvE7HypmEJcJkr4mtM+sqYPc= -cloud.google.com/go/webrisk v1.9.2/go.mod h1:pY9kfDgAqxUpDBOrG4w8deLfhvJmejKB0qd/5uQIPBc= -cloud.google.com/go/websecurityscanner v1.3.0/go.mod h1:uImdKm2wyeXQevQJXeh8Uun/Ym1VqworNDlBXQevGMo= -cloud.google.com/go/websecurityscanner v1.4.0/go.mod h1:ebit/Fp0a+FWu5j4JOmJEV8S8CzdTkAS77oDsiSqYWQ= -cloud.google.com/go/websecurityscanner v1.5.0/go.mod h1:Y6xdCPy81yi0SQnDY1xdNTNpfY1oAgXUlcfN3B3eSng= -cloud.google.com/go/websecurityscanner v1.6.1/go.mod h1:Njgaw3rttgRHXzwCB8kgCYqv5/rGpFCsBOvPbYgszpg= -cloud.google.com/go/websecurityscanner v1.6.2/go.mod h1:7YgjuU5tun7Eg2kpKgGnDuEOXWIrh8x8lWrJT4zfmas= -cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0= -cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M= -cloud.google.com/go/workflows v1.8.0/go.mod h1:ysGhmEajwZxGn1OhGOGKsTXc5PyxOc0vfKf5Af+to4M= -cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA= -cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcPALq2CxzdePw= -cloud.google.com/go/workflows v1.11.1/go.mod h1:Z+t10G1wF7h8LgdY/EmRcQY8ptBD/nvofaL6FqlET6g= -cloud.google.com/go/workflows v1.12.0/go.mod h1:PYhSk2b6DhZ508tj8HXKaBh+OFe+xdl0dHF/tJdzPQM= -cloud.google.com/go/workflows v1.12.1/go.mod h1:5A95OhD/edtOhQd/O741NSfIMezNTbCwLM1P1tBRGHM= -collectd.org v0.3.0/go.mod h1:A/8DzQBkF6abtvrT2j/AU/4tiBgJWYyh0y/oB/4MlWE= dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= -gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= -git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc= -github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= -github.com/Azure/azure-sdk-for-go/sdk/azcore v0.21.1/go.mod h1:fBF9PQNqB8scdgpZ3ufzaLntG0AG7C1WjPMsiFOmfHM= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.0.0/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.0.0/go.mod h1:+6sju8gk8FRmSajX3Oz4G5Gm7P+mbqE9FVaXXFYTkCM= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0/go.mod h1:OQeznEEkTZ9OrhHJoDD8ZDq51FHgXjqtP9z6bEwBq9U= -github.com/Azure/azure-sdk-for-go/sdk/internal v0.8.3/go.mod h1:KLF4gFr6DcKFZwSuH8w8yEK6DpFl3LP5rhdvAb7Yz5I= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal v1.0.0/go.mod h1:ceIuwmxDWptoW3eCqSXlnPsZFKh4X+R38dWPv7GS9Vs= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.0.0/go.mod h1:s1tW/At+xHqjNFvWU4G0c0Qv33KOhvbGNj0RCTQDV8s= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.2.0/go.mod h1:c+Lifp3EDEamAkPVzMooRNOK6CZjNSdEnf1A7jsI9u4= -github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.3.0/go.mod h1:tPaiy8S5bQ+S5sOiDlINkp7+Ef339+Nz5L5XO+cnOHo= -github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.2.0/go.mod h1:+6KLcKIVgxoBDMqMO/Nvy7bZ9a0nbU3I1DtFQK3YvB4= -github.com/AzureAD/microsoft-authentication-library-for-go v0.4.0/go.mod h1:Vt9sXTKwMyGcOxSmLDMnGPgqsUg7m8pe215qMLrDXw4= -github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0/go.mod h1:kgDmCTgBzIEPFElEF+FK0SdjAor06dRq2Go927dnQ6o= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/CloudyKit/fastprinter v0.0.0-20170127035650-74b38d55f37a/go.mod h1:EFZQ978U7x8IRnstaskI3IysnWY5Ao3QgZUKOXlsAdw= -github.com/CloudyKit/jet v2.1.3-0.20180809161101-62edd43e4f88+incompatible/go.mod h1:HPYO+50pSWkPoj9Q/eq0aRGByCL6ScRlUmiEX5Zgm+w= -github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= -github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8= github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= -github.com/GoogleCloudPlatform/cloudsql-proxy v0.0.0-20190129172621-c8b1d7a94ddf/go.mod h1:aJ4qN3TfrelA6NZ6AXsXRfmEVaYin3EDbSPJrKS8OXo= -github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= -github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk= -github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY= -github.com/Joker/jade v1.0.1-0.20190614124447-d475f43051e7/go.mod h1:6E6s8o2AE4KhCrqr6GRJjdC/gNfTdxkIXvuGZZda2VM= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= -github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= @@ -983,26 +68,14 @@ github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8 github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= github.com/SaveTheRbtz/mph v0.1.1-0.20240117162131-4166ec7869bc h1:DCHzPQOcU/7gwDTWbFQZc5qHMPS1g0xTO56k8NXsv9M= github.com/SaveTheRbtz/mph v0.1.1-0.20240117162131-4166ec7869bc/go.mod h1:LJM5a3zcIJ/8TmZwlUczvROEJT8ntOdhdG9jjcR1B0I= -github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= -github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= 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.6.0/go.mod h1:0qHz5QP0GMX4pfmMA/zt5RgfNuXJrTP0zS7DqpHGGTw= -github.com/VictoriaMetrics/fastcache v1.12.1/go.mod h1:tX04vaqcNoQeGLD+ra5pU5sWkuxnzWhEzLwhP9w653o= github.com/VictoriaMetrics/fastcache v1.12.2 h1:N0y9ASrJ0F6h0QaC3o6uJb3NIZ9VKLjCM7NQbSmF7WI= github.com/VictoriaMetrics/fastcache v1.12.2/go.mod h1:AmC+Nzz1+3G2eCPapF6UcsnkThDcMsQicp4xDukwJYI= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= -github.com/aclements/go-gg v0.0.0-20170118225347-6dbb4e4fefb0/go.mod h1:55qNq4vcpkIuHowELi5C8e+1yUHtoLoOUR9QU5j7Tes= -github.com/aclements/go-moremath v0.0.0-20210112150236-f10218a38794/go.mod h1:7e+I0LQFUI9AXWxOfsQROs9xPhoJtbsyWcjJqDd4KPY= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= -github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= -github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY= -github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk= -github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= -github.com/ajstarks/svgo v0.0.0-20210923152817-c3b6e2f0c527/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= -github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= @@ -1010,17 +83,9 @@ github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= -github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= -github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/apache/arrow/go/arrow v0.0.0-20191024131854-af6fa24be0db/go.mod h1:VTxUBvSJ3s3eHAg65PNgrsn5BtqCRPdmyXh6rAfdxN0= -github.com/apache/arrow/go/v10 v10.0.1/go.mod h1:YvhnlEePVnBS4+0z3fhPfUy7W1Ikj0Ih0vcRo/gZ1M0= -github.com/apache/arrow/go/v11 v11.0.0/go.mod h1:Eg5OsL5H+e299f7u5ssuXsuHQVEGC4xei5aX110hRiI= -github.com/apache/arrow/go/v12 v12.0.0/go.mod h1:d+tV/eHZZ7Dz7RPrFKtPK02tpr+c9/PEd/zm8mDS9Vg= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= -github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= @@ -1029,85 +94,48 @@ github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6l github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= -github.com/aws/aws-sdk-go-v2 v1.2.0/go.mod h1:zEQs02YRBw1DjK0PoJv3ygDYOFTre1ejlJWl8FwAuQo= github.com/aws/aws-sdk-go-v2 v1.9.0/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= -github.com/aws/aws-sdk-go-v2 v1.21.2/go.mod h1:ErQhvNuEMhJjweavOYhxVkn2RUx7kQXVATHrjKtxIpM= -github.com/aws/aws-sdk-go-v2 v1.23.1/go.mod h1:i1XDttT4rnf6vxc9AuskLc6s7XBee8rlLilKlc03uAA= github.com/aws/aws-sdk-go-v2 v1.27.0 h1:7bZWKoXhzI+mMR/HjdMx8ZCC5+6fY0lS5tr0bbgiLlo= github.com/aws/aws-sdk-go-v2 v1.27.0/go.mod h1:ffIFB97e2yNsv4aTSGkqtHnppsIJzw7G7BReUZ3jCXM= -github.com/aws/aws-sdk-go-v2/config v1.1.1/go.mod h1:0XsVy9lBI/BCXm+2Tuvt39YmdHwS5unDQmxZOYe8F5Y= github.com/aws/aws-sdk-go-v2/config v1.8.0/go.mod h1:w9+nMZ7soXCe5nT46Ri354SNhXDQ6v+V5wqDjnZE+GY= -github.com/aws/aws-sdk-go-v2/config v1.18.45/go.mod h1:ZwDUgFnQgsazQTnWfeLWk5GjeqTQTL8lMkoE1UXzxdE= -github.com/aws/aws-sdk-go-v2/config v1.25.5/go.mod h1:Bf4gDvy4ZcFIK0rqDu1wp9wrubNba2DojiPB2rt6nvI= github.com/aws/aws-sdk-go-v2/config v1.27.15 h1:uNnGLZ+DutuNEkuPh6fwqK7LpEiPmzb7MIMA1mNWEUc= github.com/aws/aws-sdk-go-v2/config v1.27.15/go.mod h1:7j7Kxx9/7kTmL7z4LlhwQe63MYEE5vkVV6nWg4ZAI8M= -github.com/aws/aws-sdk-go-v2/credentials v1.1.1/go.mod h1:mM2iIjwl7LULWtS6JCACyInboHirisUUdkBPoTHMOUo= github.com/aws/aws-sdk-go-v2/credentials v1.4.0/go.mod h1:dgGR+Qq7Wjcd4AOAW5Rf5Tnv3+x7ed6kETXyS9WCuAY= -github.com/aws/aws-sdk-go-v2/credentials v1.13.43/go.mod h1:zWJBz1Yf1ZtX5NGax9ZdNjhhI4rgjfgsyk6vTY1yfVg= -github.com/aws/aws-sdk-go-v2/credentials v1.16.4/go.mod h1:Kdh/okh+//vQ/AjEt81CjvkTo64+/zIE4OewP7RpfXk= github.com/aws/aws-sdk-go-v2/credentials v1.17.15 h1:YDexlvDRCA8ems2T5IP1xkMtOZ1uLJOCJdTr0igs5zo= github.com/aws/aws-sdk-go-v2/credentials v1.17.15/go.mod h1:vxHggqW6hFNaeNC0WyXS3VdyjcV0a4KMUY4dKJ96buU= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.0.2/go.mod h1:3hGg3PpiEjHnrkrlasTfxFqUsZ2GCk/fMUn4CbKgSkM= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.5.0/go.mod h1:CpNzHK9VEFUCknu50kkB8z58AH2B5DvPP7ea1LHve/Y= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.13/go.mod h1:f/Ib/qYjhV2/qdsf79H3QP/eRE4AkVyEf6sk7XfZ1tg= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.5/go.mod h1:VhnExhw6uXy9QzetvpXDolo1/hjhx4u9qukBGkuUwjs= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.3 h1:dQLK4TjtnlRGb0czOht2CevZ5l6RSyRWAnKeGd7VAFE= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.3/go.mod h1:TL79f2P6+8Q7dTsILpiVST+AL9lkF6PPGI167Ny0Cjw= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.5.1 h1:VGkV9KmhGqOQWnHyi4gLG98kE6OecT42fdrCGFWxJsc= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.5.1/go.mod h1:PLlnMiki//sGnCJiW+aVpvP/C8Kcm8mEj/IVm9+9qk4= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.43/go.mod h1:auo+PiyLl0n1l8A0e8RIeR8tOzYPfZZH/JNlrJ8igTQ= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.4/go.mod h1:xEhvbJcyUf/31yfGSQBe01fukXwXJ0gxDp7rLfymWE0= github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.7 h1:lf/8VTF2cM+N4SLzaYJERKEWAXq8MOMpZfU6wEPWsPk= github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.7/go.mod h1:4SjkU7QiqK2M9oozyMzfZ/23LmUY+h3oFqhdeP5OMiI= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.37/go.mod h1:Qe+2KtKml+FEsQF/DHmDV+xjtche/hwoF75EG4UlHW8= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.4/go.mod h1:dYvTNAggxDZy6y1AF7YDwXsPuHFy/VNEpEI/2dWK9IU= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.7 h1:4OYVp0705xu8yjdyoWix0r9wPIRXnIzzOoUpQVHIJ/g= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.7/go.mod h1:vd7ESTEvI76T2Na050gODNmNU7+OyKrIKroYTu4ABiI= github.com/aws/aws-sdk-go-v2/internal/ini v1.2.2/go.mod h1:BQV0agm+JEhqR+2RT5e1XTFIDcAAV0eW6z2trp+iduw= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.45/go.mod h1:lD5M20o09/LCuQ2mE62Mb/iSdSlCNuj6H5ci7tW7OsE= -github.com/aws/aws-sdk-go-v2/internal/ini v1.7.1/go.mod h1:6fQQgfuGmw8Al/3M2IgIllycxV7ZW7WCdVSqfBeUiCY= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.3.0/go.mod h1:v8ygadNyATSm6elwJ/4gzJwcFhri9RqS8skgHKiwXPU= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.1/go.mod h1:l9ymW25HOqymeU2m1gbUQ3rUIsTwKs8gYHXkqDQUhiI= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2 h1:Ji0DY1xUsUr3I8cHps0G+XM3WWU16lP6yG8qu1GAZAs= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2/go.mod h1:5CsjAbs3NlGQyZNFACh+zztPDI7fU6eW9QsxjfnuBKg= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.0.2/go.mod h1:45MfaXZ0cNbeuT0KQ1XJylq8A6+OpVV2E5kvY/Kq+u8= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.3.0/go.mod h1:R1KK+vY8AfalhG1AOu5e35pOD2SdoPKQCFLTvnxiohk= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.37/go.mod h1:vBmDnwWXWxNPFRMmG2m/3MKOe+xEcMDo1tanpaWCcck= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.4/go.mod h1:aYCGNjyUCUelhofxlZyj63srdxWUSsBSGg5l6MCuXuE= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.9 h1:Wx0rlZoEJR7JwlSZcHnEa7CNjrSIyVxMFWGAaXy4fJY= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.9/go.mod h1:aVMHdE0aHO3v+f/iw01fmXV/5DbfQ3Bi9nN7nd9bE9Y= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.7.0 h1:HWsM0YQWX76V6MOp07YuTYacm8k7h69ObJuw7Nck+og= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.7.0/go.mod h1:LKb3cKNQIMh+itGnEpKGcnL/6OIjPZqrtYah1w5f+3o= -github.com/aws/aws-sdk-go-v2/service/kms v1.26.3/go.mod h1:N3++/sLV97B8Zliz7KRqNcojOX7iMBZWKiuit5FKtH0= -github.com/aws/aws-sdk-go-v2/service/route53 v1.1.1/go.mod h1:rLiOUrPLW/Er5kRcQ7NkwbjlijluLsrIbu/iyl35RO4= -github.com/aws/aws-sdk-go-v2/service/route53 v1.30.2/go.mod h1:TQZBt/WaQy+zTHoW++rnl8JBrmZ0VO6EUbVua1+foCA= github.com/aws/aws-sdk-go-v2/service/s3 v1.15.0 h1:nPLfLPfglacc29Y949sDxpr3X/blaY40s3B85WT2yZU= github.com/aws/aws-sdk-go-v2/service/s3 v1.15.0/go.mod h1:Iv2aJVtVSm/D22rFoX99cLG4q4uB7tppuCsulGe98k4= -github.com/aws/aws-sdk-go-v2/service/sso v1.1.1/go.mod h1:SuZJxklHxLAXgLTc1iFXbEWkXs7QRTQpCLGaKIprQW0= github.com/aws/aws-sdk-go-v2/service/sso v1.4.0/go.mod h1:+1fpWnL96DL23aXPpMGbsmKe8jLTEfbjuQoA4WS1VaA= -github.com/aws/aws-sdk-go-v2/service/sso v1.15.2/go.mod h1:gsL4keucRCgW+xA85ALBpRFfdSLH4kHOVSnLMSuBECo= -github.com/aws/aws-sdk-go-v2/service/sso v1.17.3/go.mod h1:oA6VjNsLll2eVuUoF2D+CMyORgNzPEW/3PyUdq6WQjI= github.com/aws/aws-sdk-go-v2/service/sso v1.20.8 h1:Kv1hwNG6jHC/sxMTe5saMjH6t6ZLkgfvVxyEjfWL1ks= github.com/aws/aws-sdk-go-v2/service/sso v1.20.8/go.mod h1:c1qtZUWtygI6ZdvKppzCSXsDOq5I4luJPZ0Ud3juFCA= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.17.3/go.mod h1:a7bHA82fyUXOm+ZSWKU6PIoBxrjSprdLoM8xPYvzYVg= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.20.1/go.mod h1:hHL974p5auvXlZPIjJTblXJpbkfK4klBczlsEaMCGVY= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.2 h1:nWBZ1xHCF+A7vv9sDzJOq4NWIdzFYm0kH7Pr4OjHYsQ= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.2/go.mod h1:9lmoVDVLz/yUZwLaQ676TK02fhCu4+PgRSmMaKR1ozk= -github.com/aws/aws-sdk-go-v2/service/sts v1.1.1/go.mod h1:Wi0EBZwiz/K44YliU0EKxqTCJGUfYTWXrrBwkq736bM= github.com/aws/aws-sdk-go-v2/service/sts v1.7.0/go.mod h1:0qcSMCyASQPN2sk/1KQLQ2Fh6yq8wm0HSDAimPhzCoM= -github.com/aws/aws-sdk-go-v2/service/sts v1.23.2/go.mod h1:Eows6e1uQEsc4ZaHANmsPRzAKcVDrcmjjWiih2+HUUQ= -github.com/aws/aws-sdk-go-v2/service/sts v1.25.4/go.mod h1:feTnm2Tk/pJxdX+eooEsxvlvTWBvDm6CasRZ+JOs2IY= github.com/aws/aws-sdk-go-v2/service/sts v1.28.9 h1:Qp6Boy0cGDloOE3zI6XhNLNZgjNS8YmiFQFHe71SaW0= github.com/aws/aws-sdk-go-v2/service/sts v1.28.9/go.mod h1:0Aqn1MnEuitqfsCNyKsdKLhDUOr4txD/g19EfiUqgws= -github.com/aws/smithy-go v1.1.0/go.mod h1:EzMw8dbp/YJL4A5/sbhGddag+NPT7q084agLbB9LgIw= github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= -github.com/aws/smithy-go v1.15.0/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= -github.com/aws/smithy-go v1.17.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE= github.com/aws/smithy-go v1.20.2 h1:tbp628ireGtzcHDDmLT/6ADHidqnwgF57XOXZe6tp4Q= github.com/aws/smithy-go v1.20.2/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E= -github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= @@ -1117,100 +145,57 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce 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.5.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= -github.com/bits-and-blooms/bitset v1.7.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= 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/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c= -github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= -github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= -github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= -github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU= -github.com/btcsuite/btcd/btcec/v2 v2.2.1/go.mod h1:9/CSmJxmuvqzX9Wh2fXMWToLOHhPd11lSPuIupwTkI8= github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ= github.com/btcsuite/btcd/btcec/v2 v2.3.4/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= -github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 h1:KdUfX2zKommPRa+PD0sWZUyXe9w277ABlgELO7H04IM= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= -github.com/bytecodealliance/wasmtime-go/v7 v7.0.0/go.mod h1:bu6fic7trDt20w+LMooX7j3fsOwv4/ln6j8gAdP6vmA= -github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34= -github.com/c-bata/go-prompt v0.2.6/go.mod h1:/LMAke8wD2FsNu9EXNdHxNLbd9MedkPnCdfpU9wwHfY= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk= github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 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/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudflare/cloudflare-go v0.14.0/go.mod h1:EnwdgGMaFOruiPZRFSgn+TsQ3hQ7C/YWzIGLeu5c304= -github.com/cloudflare/cloudflare-go v0.79.0/go.mod h1:gkHQf9xEubaQPEuerBuoinR9P8bf8a05Lq0X6WKy1Oc= 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/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20230310173818-32f1caf87195/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20230428030218-4003588d1b74/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa h1:jQCWAUqqlij9Pgj2i/PB79y4KOPYVyFYdROxgaCwdTQ= github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa/go.mod h1:x/1Gn8zydmfq8dk6e9PdstVsDgu9RuyIIJqAaF//0IM= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= -github.com/cockroachdb/datadriven v1.0.0/go.mod h1:5Ib8Meh+jk1RlHIXej6Pzevx/NLlNvQB9pmSBZErGA4= 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.6.1/go.mod h1:tm6FTP5G81vwJ5lC0SizQo374JNCOPrHyXGitRJoDqM= -github.com/cockroachdb/errors v1.8.1/go.mod h1:qGwQn6JmZ+oMjuLwjWzUNqblqk0xl4CVV3SQbGwK7Ac= github.com/cockroachdb/errors v1.11.3 h1:5bA+k2Y6r+oz/6Z/RFlNeVCesGARKuC6YymtcDrbC/I= github.com/cockroachdb/errors v1.11.3/go.mod h1:m4UIW4CDjx+R5cybPsNrRbreomiFqt8o1h1wUVazSd8= github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce h1:giXvy4KSc/6g/esnpM7Geqxka4WSqI1SZc7sMJFd3y4= github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce/go.mod h1:9/y3cnZ5GKakj/H4y9r9GTjCvAFta7KLgSHPJJYc52M= -github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= -github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593/go.mod h1:6hk1eMY/u5t+Cf18q5lFMUA1Rc+Sm5I6Ra1QuPyxXCo= github.com/cockroachdb/pebble v1.1.1 h1:XnKU22oiCLy2Xn8vp1re67cXg4SAasg/WDt1NtcRFaw= github.com/cockroachdb/pebble v1.1.1/go.mod h1:4exszw1r40423ZsmkG/09AFEG83I0uDgfujJdbL6kYU= -github.com/cockroachdb/redact v1.0.8/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= 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/sentry-go v0.6.1-cockroachdb.2/go.mod h1:8BT+cPK6xvFOcRlk0R8eg+OTkcqI6baNH4xAkpiYVvQ= 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/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= -github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= -github.com/consensys/bavard v0.1.8-0.20210406032232-f3452dc9b572/go.mod h1:Bpd0/3mZuaj6Sj+PqrmIquiOKy397AKGThQPaGzNXAQ= 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.4.1-0.20210426202927-39ac3d4b3f1f/go.mod h1:815PAHg3wvysy0SyIqanF8gZ0Y1wjk/hrDHD/iT88+Q= -github.com/consensys/gnark-crypto v0.10.0/go.mod h1:Iq/P3HHl0ElSjsg2E1gsMwhAyxnxoKK5nVyZKd+/KhU= 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/containerd/cgroups v0.0.0-20201119153540-4cbc285b3327/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= @@ -1234,49 +219,28 @@ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfc github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 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.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/crate-crypto/go-ipa v0.0.0-20230601170251-1830d0757c80/go.mod h1:gzbVz57IDJgQ9rLQwfSk696JGWof8ftznEL9GoAv3NI= 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 v0.7.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= 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/cskr/pubsub v1.0.2 h1:vlOzMhl6PFn60gRlTQQsIfVwaPB/B/8MziK8FhEPt/0= github.com/cskr/pubsub v1.0.2/go.mod h1:/8MzYXk/NJAz782G8RPkFzXTZVu63VotefPnR9TIRis= -github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= -github.com/dave/astrid v0.0.0-20170323122508-8c2895878b14/go.mod h1:Sth2QfxfATb/nW4EsrSi2KyJmbcniZ8TgTaji17D6ms= -github.com/dave/brenda v1.1.0/go.mod h1:4wCUr6gSlu5/1Tk7akE5X7UorwiQ8Rij0SKH3/BGMOM= -github.com/dave/courtney v0.3.0/go.mod h1:BAv3hA06AYfNUjfjQr+5gc6vxeBVOupLqrColj+QSD8= -github.com/dave/dst v0.27.2/go.mod h1:jHh6EOibnHgcUW3WjKHisiooEkYwqpHLBSX1iOBhEyc= -github.com/dave/gopackages v0.0.0-20170318123100-46e7023ec56e/go.mod h1:i00+b/gKdIDIxuLDFob7ustLAVqhsZRk2qVZrArELGQ= -github.com/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg= -github.com/dave/jennifer v1.5.0/go.mod h1:4MnyiFIlZS3l5tSDn8VnzE6ffAhYBMB2SZntBsZGUok= -github.com/dave/kerr v0.0.0-20170318121727-bc25dd6abe8e/go.mod h1:qZqlPyPvfsDJt+3wHJ1EvSXDuVjFTK0j2p/ca+gtsb8= -github.com/dave/patsy v0.0.0-20210517141501-957256f50cba/go.mod h1:qfR88CgEGLoiqDaE+xxDCi5QA5v4vUoW0UCX2Nd5Tlc= -github.com/dave/rebecca v0.9.1/go.mod h1:N6XYdMD/OKw3lkF3ywh8Z6wPGuwNFDNtWYEMFWEmXBA= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c h1:pFUpOrbxDR6AkioZ1ySsx5yxlDQZ8stG2b88gTPxgJU= github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c/go.mod h1:6UhI8N9EjYm1c2odKpFpAYeR8dsBeM7PtzQhRgxRr9U= -github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo= -github.com/deckarep/golang-set/v2 v2.1.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= 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/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= -github.com/deepmap/oapi-codegen v1.6.0/go.mod h1:ryDa9AgbELGeB+YEXE1dR53yAjHwFvE9iAUlWl9Al3M= -github.com/deepmap/oapi-codegen v1.8.2/go.mod h1:YLgSKSDv/bZQB7N4ws6luhozi3cEdRktEqrX88CvjIw= github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f h1:U5y3Y5UE0w7amNe7Z5G/twsBW0KEalRQXZzf8ufSh9I= github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE= -github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= github.com/dgraph-io/badger/v2 v2.2007.3/go.mod h1:26P/7fbL4kUZVEVKLAKXkBXKOydDmM2p1e+NhhnBCAE= github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o= github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk= @@ -1284,24 +248,12 @@ github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KP github.com/dgraph-io/ristretto v0.1.0 h1:Jv3CGQHp9OjuMBSne1485aDpUkTKEcUqF+jm/LuerPI= github.com/dgraph-io/ristretto v0.1.0/go.mod h1:fux0lOrBhrVCJd3lcTHsIJhq1T2rokOu6v9Vcb3Q9ug= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-bitstream v0.0.0-20180413035011-3522498ce2c8/go.mod h1:VMaSuZ+SZcx/wljOQKvp5srsbCiKDEb6K2wC4+PiBmQ= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= -github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko= -github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= -github.com/docker/docker v1.6.2/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/dop251/goja v0.0.0-20211022113120-dc8c55024d06/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk= -github.com/dop251/goja v0.0.0-20220405120441-9037c2b61cbf/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk= -github.com/dop251/goja v0.0.0-20230806174421-c933cf95e127/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4= -github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y= -github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d/go.mod h1:DngW8aVqWbuLRMHItjPUyqdj+HWPvnQe8V8y1nDpIbM= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= @@ -1309,11 +261,9 @@ github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+m github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= -github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/ef-ds/deque v1.0.4 h1:iFAZNmveMT9WERAkqLJ+oaABF9AcVQ5AjXem/hroniI= github.com/ef-ds/deque v1.0.4/go.mod h1:gXDnTC3yqvBcHbq2lcExjtAcVrOnJCbMcZXmuj8Z4tg= -github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= github.com/elastic/gosigar v0.12.0/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= github.com/elastic/gosigar v0.14.2 h1:Dg80n8cr90OZ7x+bAax/QjoW/XqTI11RmA79ZwIm9/4= github.com/elastic/gosigar v0.14.2/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= @@ -1323,48 +273,21 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m 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/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= -github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= -github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= -github.com/envoyproxy/go-control-plane v0.10.3/go.mod h1:fJJn/j26vwOu972OllsvAgJJM//w9BV6Fxbg2LuVd34= -github.com/envoyproxy/go-control-plane v0.11.0/go.mod h1:VnHyVMpzcLvCFt9yUz1UnCwHLhwx1WguiVDV7pTG/tI= -github.com/envoyproxy/go-control-plane v0.11.1-0.20230524094728-9239064ad72f/go.mod h1:sfYdkwUW4BA3PbKjySwjJy+O4Pu0h62rlqCMHNk+K+Q= -github.com/envoyproxy/go-control-plane v0.11.1/go.mod h1:uhMcXKCQMEJHiAb0w+YGefQLaTEw+YhGluxZkrTmD0g= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo= -github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w= -github.com/envoyproxy/protoc-gen-validate v0.10.0/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= -github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= -github.com/envoyproxy/protoc-gen-validate v1.0.1/go.mod h1:0vj8bNkYbSTNS2PIyH87KZaeN4x9zpL9Qt8fQC7d+vs= -github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A= github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew= -github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= -github.com/ethereum/c-kzg-4844 v0.4.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= 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.10.26/go.mod h1:EYFyF19u3ezGLD4RqOkLq+ZCXzYbLoNDdZlMt7kyKFg= -github.com/ethereum/go-ethereum v1.13.5/go.mod h1:yMTu38GSuyxaYzQMViqNmQ1s3cE84abZexQmTgenWk0= github.com/ethereum/go-ethereum v1.13.10 h1:Ppdil79nN+Vc+mXfge0AuUgmKWuVv4eMqzoIVSdqZek= github.com/ethereum/go-ethereum v1.13.10/go.mod h1:sc48XYQxCzH3fG9BcrXCOOgQk2JfZzNAmIKnceogzsA= 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/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/fjl/gencodec v0.0.0-20220412091415-8bb9e558978c/go.mod h1:AzA8Lj6YtixmJWL+wkKoBGsLWy9gFrAzi4g+5bCKwpY= -github.com/fjl/gencodec v0.0.0-20230517082657-f9840df7b83e/go.mod h1:AzA8Lj6YtixmJWL+wkKoBGsLWy9gFrAzi4g+5bCKwpY= -github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= -github.com/flosch/pongo2 v0.0.0-20190707114632-bbf5a6c351f4/go.mod h1:T9YF2M40nIgbVgp3rreNmTged+9HrbNTIQf1PsaIiTA= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/flynn/noise v1.0.1 h1:vPp/jdQLXC6ppsXSj/pM3W1BIJ5FEHE2TulSJBpb43Y= github.com/flynn/noise v1.0.1/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag= -github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= -github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= @@ -1375,7 +298,6 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/fxamacker/cbor/v2 v2.4.1-0.20220515183430-ad2eae63303f/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= github.com/fxamacker/cbor/v2 v2.4.1-0.20230228173756-c0c9f774e40c h1:5tm/Wbs9d9r+qZaUFXk59CWDD0+77PBqDREffYkyi5c= github.com/fxamacker/cbor/v2 v2.4.1-0.20230228173756-c0c9f774e40c/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= github.com/fxamacker/circlehash v0.3.0 h1:XKdvTtIJV9t7DDUtsf0RIpC1OcxZtPbmgIH7ekx28WA= @@ -1386,40 +308,21 @@ github.com/gammazero/deque v0.1.0 h1:f9LnNmq66VDeuAlSAapemq/U7hJ2jpIWa4c09q8Dlik github.com/gammazero/deque v0.1.0/go.mod h1:KQw7vFau1hHuM8xmI9RbgKFbAsQFWmBpqQ2KenFLk6M= github.com/gammazero/workerpool v1.1.2 h1:vuioDQbgrz4HoaCi2q1HLlOXdpbap5AET7xu5/qj87g= github.com/gammazero/workerpool v1.1.2/go.mod h1:UelbXcO0zCIGFcufcirHhq2/xtLXJdQ29qZNlXG9OjQ= -github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61/go.mod h1:Q0X6pkwTILDlzrGEckF6HKjXe48EgsY/l7K7vhY4MW8= -github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI= github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= -github.com/gballet/go-verkle v0.0.0-20230607174250-df487255f46b/go.mod h1:CDncRYVRSDqwakm282WEkjfaAj1hxU/v5RXxk5nXOiI= -github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= -github.com/getkin/kin-openapi v0.61.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps= github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= -github.com/ghemawat/stream v0.0.0-20171120220530-696b145b53b9/go.mod h1:106OIgooyS7OzLDOpUGgm9fA3bQENb/cFSyyBmMoJDs= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do= github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8= github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= -github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= -github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= -github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= -github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= 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-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g= -github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks= -github.com/go-fonts/latin-modern v0.3.0/go.mod h1:ysEQXnuT/sCDOAONxC7ImeEDVINbltClhasMAqEtRK0= -github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= -github.com/go-fonts/liberation v0.2.0/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= -github.com/go-fonts/liberation v0.3.0/go.mod h1:jdJ+cqF+F4SUL2V+qxBth8fvBpBDS7yloUL5Fi8GTGY= -github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmnUIzUY= 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= @@ -1428,34 +331,22 @@ github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2 github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= github.com/go-kit/kit v0.12.0 h1:e4o3o3IsBfAKQh5Qbbiqyfu97Ku7jrO/JbohvztANh4= github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEaizzs= -github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= -github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U= -github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81/go.mod h1:SX0U8uGpxhq9o2S/CELCSUxEWWAuoCUcVCQWv7G2OCk= -github.com/go-latex/latex v0.0.0-20230307184459-12ec69307ad9/go.mod h1:gWuR/CrFDDeVRFQwHPvsv9soJVB/iqymhuZQuJ3a9OM= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= -github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.2.6/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-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= -github.com/go-pdf/fpdf v0.6.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= @@ -1470,11 +361,8 @@ github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91 github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= github.com/go-playground/validator/v10 v10.14.1 h1:9c50NUPC30zyuKprjL3vNZ0m5oG+jU0zvx4AqHGnv4k= github.com/go-playground/validator/v10 v10.14.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= -github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= @@ -1487,7 +375,6 @@ github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6Wezm github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= github.com/gobwas/ws v1.2.1 h1:F2aeBZrm2NDsc7vbovKrWSogd4wvfAxg0FQ89/iqOTk= github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= -github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -1496,8 +383,6 @@ github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/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 v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -1505,18 +390,7 @@ github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zV github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM= -github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= -github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang-jwt/jwt/v4 v4.3.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= -github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= -github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= -github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68= github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -1534,7 +408,6 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt 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/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -1552,9 +425,7 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD 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/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -1563,17 +434,8 @@ github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEW 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/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y= -github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= -github.com/gonum/blas v0.0.0-20181208220705-f22b278b28ac/go.mod h1:P32wAyui1PQ58Oce/KYkOqQv8cVw1zAapXOl+dRFGbc= -github.com/gonum/floats v0.0.0-20181209220543-c233463c7e82/go.mod h1:PxC8OnwL11+aosOB5+iEPoV3picfs8tUpkVd0pDo+Kg= -github.com/gonum/internal v0.0.0-20181124074243-f884aa714029/go.mod h1:Pu4dmpkhSyOzRwuXkOgAvijx4o+4YMUJJo9OvPYMkks= -github.com/gonum/lapack v0.0.0-20181123203213-e4cdc5a0bff9/go.mod h1:XA3DeT6rxh2EAE789SSiSJNqxPaC0aE9J8NTOI0Jo/A= -github.com/gonum/matrix v0.0.0-20181209220409-c518dec07be9/go.mod h1:0EXg4mc1CNP0HCqCz+K4ts155PXIlUywf0wqN+GfPZw= 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/flatbuffers v1.11.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= -github.com/google/flatbuffers v2.0.8+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= 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= @@ -1587,17 +449,11 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 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/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= -github.com/google/go-pkcs11 v0.2.0/go.mod h1:6eQoGcuNJpa7jnd5pMGdkSaQpNDYvPlXWMcjXXThLlY= -github.com/google/go-pkcs11 v0.2.1-0.20230907215043-c6f79328ddf9/go.mod h1:6eQoGcuNJpa7jnd5pMGdkSaQpNDYvPlXWMcjXXThLlY= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= -github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa/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/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= @@ -1606,7 +462,6 @@ github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPg 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/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw= github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= @@ -1619,60 +474,25 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-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-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg= github.com/google/pprof v0.0.0-20231229205709-960ae82b1e42 h1:dHLYa5D8/Ta0aLR2XcPsrkpAgGeFs6thhMcQK0oQ0n8= github.com/google/pprof v0.0.0-20231229205709-960ae82b1e42/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/s2a-go v0.1.0/go.mod h1:OJpEgntRZo8ugHpF9hkoLJbS5dSI20XZeXJ9JVywLlM= -github.com/google/s2a-go v0.1.3/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= -github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= -github.com/google/safehtml v0.0.2/go.mod h1:L4KWwDsUJdECRAEpZoBn3O64bQaywRscowZjJAzjHnU= github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= -github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= -github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= -github.com/googleapis/enterprise-certificate-proxy v0.2.1/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= -github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= -github.com/googleapis/enterprise-certificate-proxy v0.2.4/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= -github.com/googleapis/enterprise-certificate-proxy v0.2.5/go.mod h1:RxW0N9901Cko1VOCW3SXCpWP+mlIEkk2tP7jnHy9a3w= github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= -github.com/googleapis/gax-go v0.0.0-20161107002406-da06d194a00e/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= -github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= -github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= -github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= -github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= -github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo= -github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY= -github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= -github.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= -github.com/googleapis/gax-go/v2 v2.8.0/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= -github.com/googleapis/gax-go/v2 v2.10.0/go.mod h1:4UOEnMCrxsSqQ940WnTiD6qJ63le2ev3xfyagutxiPw= -github.com/googleapis/gax-go/v2 v2.11.0/go.mod h1:DxmR61SGKkGLa2xigwuZIQpkCI2S5iydzRfb3peWZJI= github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= -github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c h1:7lF+Vz0LqiRidnzC1Oq86fpX1q/iEv2KJdrCtttYjT4= @@ -1680,16 +500,13 @@ github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORR github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/graph-gophers/graphql-go v1.3.0/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= @@ -1701,28 +518,19 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w= github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 h1:Wqo399gCIufwto+VfwCSvsnfGpF/w5E9CNxSwbpD6No= github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0/go.mod h1:qmOFXW2epJhM0qSnUUYpldc7gVz2KMQwJ/QYCDIa7XU= -github.com/guptarohit/asciigraph v0.5.5/go.mod h1:dYl5wwK4gNsnFf9Zp+l06rFiDZ5YtXM6x7SRWZ3KGag= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -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-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= -github.com/hashicorp/go-hclog v1.2.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= 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-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-retryablehttp v0.7.4/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= 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= @@ -1732,7 +540,6 @@ github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09 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/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= @@ -1743,11 +550,8 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO 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/holiman/billy v0.0.0-20230718173358-1c7e68d277a7/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= -github.com/holiman/uint256 v1.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw= -github.com/holiman/uint256 v1.2.3/go.mod h1:SC8Ryt4n+UBbPbIBKaG9zbbDlp4jOru9xFZmPzLUTxw= github.com/holiman/uint256 v1.3.0 h1:4wdcm/tnd0xXdu7iS3ruNvxkWwrb4aeBQv19ayYn8F4= github.com/holiman/uint256 v1.3.0/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= @@ -1758,34 +562,16 @@ github.com/huandu/go-clone v1.7.2/go.mod h1:ReGivhG6op3GYr+UY3lS6mxjKp7MIGTknuU5 github.com/huandu/go-clone/generic v1.7.2 h1:47pQphxs1Xc9cVADjOHN+Bm5D0hNagwH9UXErbxgVKA= github.com/huandu/go-clone/generic v1.7.2/go.mod h1:xgd9ZebcMsBWWcBx5mVMCoqMX24gLWr5lQicr+nVXNs= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= -github.com/huin/goupnp v1.0.3/go.mod h1:ZxNlw5WqJj6wSsRK5+YfflQGXYfccj5VgQsMNixHM7Y= github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= -github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= -github.com/hydrogen18/memlistener v0.0.0-20141126152155-54553eb933fb/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE= -github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= 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/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= -github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= github.com/improbable-eng/grpc-web v0.15.0 h1:BN+7z6uNXZ1tQGcNAuaU1YjsLTApzkjt2tzCixLaUPQ= github.com/improbable-eng/grpc-web v0.15.0/go.mod h1:1sy9HKV4Jt9aEs9JSnkWlRJPuPtwNr0l57L4f878wP8= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/influxdata/flux v0.65.1/go.mod h1:J754/zds0vvpfwuq7Gc2wRdVwEodfpCFM7mYlOw2LqY= -github.com/influxdata/influxdb v1.8.3/go.mod h1:JugdFhsvvI8gadxOI6noqNeeBHvWNTbfYGtiAn+2jhI= -github.com/influxdata/influxdb-client-go/v2 v2.4.0/go.mod h1:vLNHdxTJkIf2mSLvGrpj8TCcISApPoXkaxP8g9uRlW8= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= -github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= -github.com/influxdata/influxql v1.1.1-0.20200828144457-65d3ef77d385/go.mod h1:gHp9y86a/pxhjJ+zMjNXiQAA197Xk9wLxaz+fGG+kWk= -github.com/influxdata/line-protocol v0.0.0-20180522152040-32c6aa80de5e/go.mod h1:4kt73NQhadE3daL3WhR5EJ/J2ocX0PZzwxQ0gXJ7oFE= -github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo= -github.com/influxdata/line-protocol v0.0.0-20210311194329-9aa0e372d097/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo= -github.com/influxdata/promql/v2 v2.12.0/go.mod h1:fxOPu+DY0bqCTCECchSRtWfc+0X19ybifQhZoQNF5D8= -github.com/influxdata/roaring v0.4.13-0.20180809181101-fc520f41fab6/go.mod h1:bSgUQ7q5ZLSO+bKBGqJiCBGAl+9DxyW63zLTujjUlOE= -github.com/influxdata/tdigest v0.0.0-20181121200506-bf2b5ad3c0a9/go.mod h1:Js0mqiSBE6Ffsg94weZZ2c+v/ciT8QRHFOap7EKDrR0= -github.com/influxdata/usage-client v0.0.0-20160829180054-6d3895376368/go.mod h1:Wbbw6tYNvwa5dlB6304Sd+82Z3f7PmVZHVKU637d4po= github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs= github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0= github.com/ipfs/boxo v0.17.1-0.20240131173518-89bceff34bf1 h1:5H/HYvdmbxp09+sAvdqJzyrWoyCS6OroeW9Ym06Tb+0= @@ -1830,10 +616,6 @@ github.com/ipfs/go-peertaskqueue v0.8.1 h1:YhxAs1+wxb5jk7RvS0LHdyiILpNmRIRnZVzte github.com/ipfs/go-peertaskqueue v0.8.1/go.mod h1:Oxxd3eaK279FxeydSPPVGHzbwVeHjatZ2GA8XD+KbPU= github.com/ipld/go-ipld-prime v0.21.0 h1:n4JmcpOlPDIxBcY037SVfpd1G+Sj1nKZah0m6QH9C2E= github.com/ipld/go-ipld-prime v0.21.0/go.mod h1:3RLqy//ERg/y5oShXXdx5YIp50cFGOanyMctpPjsvxQ= -github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI= -github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0= -github.com/iris-contrib/i18n v0.0.0-20171121225848-987a633949d0/go.mod h1:pMCz62A0xJL6I+umB2YTlFRwWXaDFA0jy+5HzGiJjqI= -github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw= 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/jbenet/go-cienv v0.1.0/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA= @@ -1841,8 +623,6 @@ github.com/jbenet/go-temp-err-catcher v0.1.0 h1:zpb3ZH6wIE8Shj2sKS+khgRvf7T7RABo github.com/jbenet/go-temp-err-catcher v0.1.0/go.mod h1:0kJRvmDZXNMIiJirNPEYfhpPwbGVtZVWC34vc5WLsDk= github.com/jbenet/goprocess v0.1.4 h1:DRGOFReOMqqDNXwW70QkacFW0YN9QnwLV0Vqk+3oU0o= github.com/jbenet/goprocess v0.1.4/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4= -github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e/go.mod h1:G1CVv03EnqU1wYL2dFwXxW2An0az9JTl/ZsqXQeBlkU= -github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267/go.mod h1:h1nSAbGFqGVzn6Jyl1R/iCcBUHN4g+gW1u9CoBTrb9E= github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= @@ -1856,63 +636,33 @@ github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 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/jsternberg/zap-logfmt v1.0.0/go.mod h1:uvPs/4X51zdkcm5jXl5SYoN+4RK21K8mysFmDaM/h+o= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q= -github.com/juju/loggo v0.0.0-20180524022052-584905176618/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U= -github.com/juju/testing v0.0.0-20180920084828-472a3e8b2073/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= -github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= -github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef/go.mod h1:Ct9fl0F6iIOGgxJ5npU/IUOhOhqlVrGjyIZc8/MagT0= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 h1:uC1QfSlInpQF+M0ao65imhwqKnz3Q2z/d8PWZRMQvDM= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw= github.com/k0kubun/pp v3.0.1+incompatible h1:3tqvf7QgUnZ5tXO6pNAZlrvHgl6DvifjDrd9g2S9Z40= github.com/k0kubun/pp v3.0.1+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg= -github.com/k0kubun/pp/v3 v3.2.0/go.mod h1:ODtJQbQcIRfAD3N+theGCV1m/CBxweERz2dapdz1EwA= -github.com/karalabe/usb v0.0.2/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= -github.com/kataras/golog v0.0.9/go.mod h1:12HJgwBIZFNGL0EJnMRhmvGA0PQGx8VFwrZtM4CqbAk= -github.com/kataras/iris/v12 v12.0.1/go.mod h1:udK4vLQKkdDqMGJJVd/msuMtN6hpYJhg/lSzuxjhO+U= -github.com/kataras/neffos v0.0.10/go.mod h1:ZYmJC07hQPW67eKuzlfY7SO3bC0mw83A3j6im82hfqw= -github.com/kataras/pio v0.0.0-20190103105442-ea782b38602d/go.mod h1:NV88laa9UiiDuX9AhMbDPkGYSPugBOV6yTZB1l2K9Z0= -github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= -github.com/kevinburke/go-bindata v3.23.0+incompatible/go.mod h1:/pEEZ72flUW2p0yi30bslSp9YqD9pysLxunQDdb2CPM= github.com/kevinburke/go-bindata v3.24.0+incompatible h1:qajFA3D0pH94OTLU4zcCCKCDgR+Zr2cZK/RPJHDdFoY= github.com/kevinburke/go-bindata v3.24.0+incompatible/go.mod h1:/pEEZ72flUW2p0yi30bslSp9YqD9pysLxunQDdb2CPM= -github.com/kilic/bls12-381 v0.1.0/go.mod h1:vDTTHJONJ6G+P2R74EhnyotQDTliQDnFEwhdmfzw1ig= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= 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/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= -github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/compress v1.9.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= -github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= -github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4= github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= -github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= -github.com/klauspost/cpuid/v2 v2.2.0/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= -github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= -github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6/go.mod h1:+ZoRqAPRLkC4NPOvfYeR5KNOrY6TD+/sAC3HXPZgDYg= -github.com/klauspost/pgzip v1.0.2-0.20170402124221-0bf5dcad4ada/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= 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.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/koron/go-ssdp v0.0.4 h1:1IDwrghSKYM7yLf7XCzbByg2sJ/JcNOZRXS2jczTwz0= @@ -1922,28 +672,21 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= 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.3/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 v0.0.0-20170224010052-a616ab194758/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= 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/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g= -github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg= -github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= 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/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= -github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/libp2p/go-addr-util v0.1.0 h1:acKsntI33w2bTU7tC9a0SaPimJGfSI0bFKC18ChxeVI= github.com/libp2p/go-addr-util v0.1.0/go.mod h1:6I3ZYuFr2O/9D+SoyM0zEw0EF3YkldtTX406BpdQMqw= github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= @@ -1986,65 +729,35 @@ github.com/logrusorgru/aurora/v4 v4.0.0/go.mod h1:lP0iIa2nrnT/qoFXcOZSrZQpJ1o6n2 github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= -github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= -github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= -github.com/lyft/protoc-gen-star/v2 v2.0.1/go.mod h1:RcCdONR2ScXaYnQC5tUzxzlpA3WVYF7/opLeUgcQs/o= -github.com/lyft/protoc-gen-star/v2 v2.0.3/go.mod h1:amey7yeodaJhXSbf/TlLvWiqQfLOSpEk//mLlc+axEk= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 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.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd h1:br0buuQ854V8u83wA0rVZ8ttrq5CpaPZdvrK0LP2lOk= github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd/go.mod h1:QuCEs1Nt24+FYQEqAAncTDPJIuGs+LxK1MCiFL25pMU= -github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ= 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.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/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.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= -github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.17/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.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -github.com/mattn/go-sqlite3 v1.14.5/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI= -github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= -github.com/mattn/go-tty v0.0.0-20180907095812-13ff1204f104/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE= -github.com/mattn/go-tty v0.0.3/go.mod h1:ihxohKRERHTVzN+aSVRwACLCeqIoZAWpoICkkvrWyR0= -github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/mediocregopher/mediocre-go-lib v0.0.0-20181029021733-cb65787f37ed/go.mod h1:dSsfyI2zABAdhcbvkXqgxOxrCsbYeHCPgrZkku60dSg= -github.com/mediocregopher/radix/v3 v3.3.0/go.mod h1:EmfVyvspXz1uZEyPBMyGK+kjWiKQGvsUt6O3Pj+LDCQ= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= -github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM= @@ -2055,9 +768,7 @@ github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b h1:z78hV3sbSMAUoyUM github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b/go.mod h1:lxPUiZwKoFL8DUUmalo2yJJUCxbPKtm8OKfqr2/FTNU= github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc h1:PTfri+PuQmWDqERdnNMiD9ZejrlswWrCpBEZgWOiTrc= github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc/go.mod h1:cGKTAVKx4SxOuR/czcZ/E2RSJ3sfHs8FpHhQ5CWMf9s= -github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= -github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= @@ -2071,10 +782,8 @@ github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS4 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/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= @@ -2085,16 +794,11 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= -github.com/montanaflynn/stats v0.6.6/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= -github.com/montanaflynn/stats v0.7.0/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= -github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= -github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg= github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA= github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE= github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI= @@ -2132,20 +836,15 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/grpc-proxy v0.0.0-20181017164139-0f1106ef9c76/go.mod h1:x5OoJHDHqxHS801UIuhqGl6QdSAEJvtausosHSdazIo= -github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0= -github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E= github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= -github.com/nats-io/nats.go v1.8.1/go.mod h1:BrFz9vVn0fU3AcH9Vn4Kd7W0NpJ651tD5omQ3M8LwxM= github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= -github.com/nats-io/nkeys v0.0.2/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7TDb/4= github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= 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= @@ -2155,45 +854,37 @@ github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= 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/onflow/atree v0.6.1-0.20230711151834-86040b30171f/go.mod h1:xvP61FoOs95K7IYdIYRnNcYQGf4nbF/uuJ0tHf4DRuM= -github.com/onflow/atree v0.8.0-rc.6 h1:GWgaylK24b5ta2Hq+TvyOF7X5tZLiLzMMn7lEt59fsA= -github.com/onflow/atree v0.8.0-rc.6/go.mod h1:yccR+LR7xc1Jdic0mrjocbHvUD7lnVvg8/Ct1AA5zBo= -github.com/onflow/cadence v1.0.0-M3/go.mod h1:odXGZZ/wGNA5mwT8bC9v8u8EXACHllB2ABSZK65TGL8= -github.com/onflow/cadence v1.0.0-preview.52 h1:hZ92e6lL2+PQa3C1i5jJh0zZYFdW89+X1MS0Bkd6Ayo= -github.com/onflow/cadence v1.0.0-preview.52/go.mod h1:7wvvecnAZtYOspLOS3Lh+FuAmMeSrXhAWiycC3kQ1UU= -github.com/onflow/crypto v0.25.0/go.mod h1:C8FbaX0x8y+FxWjbkHy0Q4EASCDR9bSPWZqlpCLYyVI= +github.com/onflow/atree v0.8.0 h1:qg5c6J1gVDNObughpEeWm8oxqhPGdEyGrda121GM4u0= +github.com/onflow/atree v0.8.0/go.mod h1:yccR+LR7xc1Jdic0mrjocbHvUD7lnVvg8/Ct1AA5zBo= +github.com/onflow/cadence v1.2.2 h1:LwigF/2lPiXlwX5rFn71KeMpmW5Iu/f/JtsPLLULBCc= +github.com/onflow/cadence v1.2.2/go.mod h1:PYX1xLejqswtDsQzN93x/VpfSKNyjUk6hrkc/mpv7xs= github.com/onflow/crypto v0.25.2 h1:GjHunqVt+vPcdqhxxhAXiMIF3YiLX7gTuTR5O+VG2ns= github.com/onflow/crypto v0.25.2/go.mod h1:fY7eLqUdMKV8EGOw301unP8h7PvLVy8/6gVR++/g0BY= -github.com/onflow/flow-core-contracts/lib/go/contracts v1.3.1 h1:q9tXLIALwQ76bO4bmSrhtTkyc2cZF4/gH11ix9E3F5k= -github.com/onflow/flow-core-contracts/lib/go/contracts v1.3.1/go.mod h1:u/mkP/B+PbV33tEG3qfkhhBlydSvAKxfLZSfB4lsJHg= -github.com/onflow/flow-core-contracts/lib/go/templates v1.3.1 h1:FfhMBAb78p6VAWkJ+iqdKLErGQVQgxk5w6DP5ZruWX8= -github.com/onflow/flow-core-contracts/lib/go/templates v1.3.1/go.mod h1:NgbMOYnMh0GN48VsNKZuiwK7uyk38Wyo8jN9+C9QE30= -github.com/onflow/flow-ft/lib/go/contracts v1.0.0 h1:mToacZ5NWqtlWwk/7RgIl/jeKB/Sy/tIXdw90yKHcV0= -github.com/onflow/flow-ft/lib/go/contracts v1.0.0/go.mod h1:PwsL8fC81cjnUnTfmyL/HOIyHnyaw/JA474Wfj2tl6A= -github.com/onflow/flow-ft/lib/go/templates v1.0.0 h1:6cMS/lUJJ17HjKBfMO/eh0GGvnpElPgBXx7h5aoWJhs= -github.com/onflow/flow-ft/lib/go/templates v1.0.0/go.mod h1:uQ8XFqmMK2jxyBSVrmyuwdWjTEb+6zGjRYotfDJ5pAE= -github.com/onflow/flow-go-sdk v1.0.0-M1/go.mod h1:TDW0MNuCs4SvqYRUzkbRnRmHQL1h4X8wURsCw9P9beo= -github.com/onflow/flow-go-sdk v1.0.0-preview.55 h1:tUM8K7GcWltM0YSzei/g2Gq4z3BwGFTdpq2QwvB6ubk= -github.com/onflow/flow-go-sdk v1.0.0-preview.55/go.mod h1:rBRNboXaTprn7M0MeO6/R1bxNpctbrx66I2FLp0V6fM= -github.com/onflow/flow-nft/lib/go/contracts v1.2.1 h1:woAAS5z651sDpi7ihAHll8NvRS9uFXIXkL6xR+bKFZY= -github.com/onflow/flow-nft/lib/go/contracts v1.2.1/go.mod h1:2gpbza+uzs1k7x31hkpBPlggIRkI53Suo0n2AyA2HcE= -github.com/onflow/flow-nft/lib/go/templates v1.2.0 h1:JSQyh9rg0RC+D1930BiRXN8lrtMs+ubVMK6aQPon6Yc= -github.com/onflow/flow-nft/lib/go/templates v1.2.0/go.mod h1:p+2hRvtjLUR3MW1NsoJe5Gqgr2eeH49QB6+s6ze00w0= -github.com/onflow/flow/protobuf/go/flow v0.3.2-0.20231121210617-52ee94b830c2/go.mod h1:NA2pX2nw8zuaxfKphhKsk00kWLwfd+tv8mS23YXO4Sk= -github.com/onflow/flow/protobuf/go/flow v0.4.6 h1:KE/CsRVfyG5lGBtm1aNcjojMciQyS5GfPF3ixOWRfi0= -github.com/onflow/flow/protobuf/go/flow v0.4.6/go.mod h1:NA2pX2nw8zuaxfKphhKsk00kWLwfd+tv8mS23YXO4Sk= +github.com/onflow/flow-core-contracts/lib/go/contracts v1.4.0 h1:R86HaOuk6vpuECZnriEUE7bw9inC2AtdSn8lL/iwQLQ= +github.com/onflow/flow-core-contracts/lib/go/contracts v1.4.0/go.mod h1:9asTBnB6Tw2UlVVtQKyS/egYv3xr4zVlJnJ75z1dfac= +github.com/onflow/flow-core-contracts/lib/go/templates v1.4.0 h1:u2DAG8pk0xFH7TwS70t1gSZ/FtIIZWMSNyiu4SeXBYg= +github.com/onflow/flow-core-contracts/lib/go/templates v1.4.0/go.mod h1:pN768Al/wLRlf3bwugv9TyxniqJxMu4sxnX9eQJam64= +github.com/onflow/flow-ft/lib/go/contracts v1.0.1 h1:Ts5ob+CoCY2EjEd0W6vdLJ7hLL3SsEftzXG2JlmSe24= +github.com/onflow/flow-ft/lib/go/contracts v1.0.1/go.mod h1:PwsL8fC81cjnUnTfmyL/HOIyHnyaw/JA474Wfj2tl6A= +github.com/onflow/flow-ft/lib/go/templates v1.0.1 h1:FDYKAiGowABtoMNusLuRCILIZDtVqJ/5tYI4VkF5zfM= +github.com/onflow/flow-ft/lib/go/templates v1.0.1/go.mod h1:uQ8XFqmMK2jxyBSVrmyuwdWjTEb+6zGjRYotfDJ5pAE= +github.com/onflow/flow-go-sdk v1.2.3 h1:jb+0dIXBO12Zt8x3c2xDXYPv6k3sRTUvhe59M+EcXTI= +github.com/onflow/flow-go-sdk v1.2.3/go.mod h1:jMaffBTlAIdutx+pBhRIigLZFIBYSDDST0Uax1rW2qo= +github.com/onflow/flow-nft/lib/go/contracts v1.2.2 h1:XFERNVUDGbZ4ViZjt7P1cGD80mO1PzUJYPfdhXFsGbQ= +github.com/onflow/flow-nft/lib/go/contracts v1.2.2/go.mod h1:eZ9VMMNfCq0ho6kV25xJn1kXeCfxnkhj3MwF3ed08gY= +github.com/onflow/flow-nft/lib/go/templates v1.2.1 h1:SAALMZPDw9Eb9p5kSLnmnFxjyig1MLiT4JUlLp0/bSE= +github.com/onflow/flow-nft/lib/go/templates v1.2.1/go.mod h1:W6hOWU0xltPqNpv9gQX8Pj8Jtf0OmRxc1XX2V0kzJaI= +github.com/onflow/flow/protobuf/go/flow v0.4.7 h1:iP6DFx4wZ3ETORsyeqzHu7neFT3d1CXF6wdK+AOOjmc= +github.com/onflow/flow/protobuf/go/flow v0.4.7/go.mod h1:NA2pX2nw8zuaxfKphhKsk00kWLwfd+tv8mS23YXO4Sk= github.com/onflow/go-ethereum v1.14.7 h1:gg3awYqI02e3AypRdpJKEvNTJ6kz/OhAqRti0h54Wlc= github.com/onflow/go-ethereum v1.14.7/go.mod h1:zV14QLrXyYu5ucvcwHUA0r6UaqveqbXaehAVQJlSW+I= -github.com/onflow/sdks v0.5.1-0.20230912225508-b35402f12bba/go.mod h1:F0dj0EyHC55kknLkeD10js4mo14yTdMotnWMslPirrU= github.com/onflow/sdks v0.6.0-preview.1 h1:mb/cUezuqWEP1gFZNAgUI4boBltudv4nlfxke1KBp9k= github.com/onflow/sdks v0.6.0-preview.1/go.mod h1:F0dj0EyHC55kknLkeD10js4mo14yTdMotnWMslPirrU= github.com/onflow/wal v1.0.2 h1:5bgsJVf2O3cfMNK12fiiTyYZ8cOrUiELt3heBJfHOhc= github.com/onflow/wal v1.0.2/go.mod h1:iMC8gkLqu4nkbkAla5HkSBb+FGyQOZiWz3DYm2wSXCk= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= @@ -2211,7 +902,6 @@ github.com/opencontainers/runtime-spec v1.1.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/ github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/opentracing/opentracing-go v1.0.3-0.20180606204148-bd9c31933947/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= @@ -2222,7 +912,6 @@ github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnh github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/paulbellamy/ratecounter v0.2.0/go.mod h1:Hfx1hDpSGoqxkVVpBi/IlYD7kChlfo5C6hzIHwPqfFE= 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/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= @@ -2230,31 +919,19 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU= github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= -github.com/peterh/liner v1.0.1-0.20180619022028-8c1271fcf47f/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc= -github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= -github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= -github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY= -github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= -github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM= github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= 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/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ= -github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 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/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= -github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= -github.com/pkg/term v0.0.0-20180730021639-bffc007b7fd5/go.mod h1:eCbImbZ95eXtAUIbLAuAVnBnwf83mjf6QIVH8SHYwqQ= -github.com/pkg/term v1.2.0-beta.2/go.mod h1:E25nymQcrSllhX42Ok8MRm1+hyBdHY0dCeiKZ9jpNGw= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/polydawn/refmt v0.89.0 h1:ADJTApkvkeBZsN0tBTx8QjpD9JkmxbKp0cxfr9qszm4= @@ -2262,7 +939,6 @@ github.com/polydawn/refmt v0.89.0/go.mod h1:/zvteZs/GwLtCgZ4BL6CBsk9IKIlexP43ObX github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= -github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= @@ -2270,8 +946,6 @@ github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDf github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.0/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk= github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= @@ -2280,9 +954,6 @@ github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1: github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.1-0.20210607210712-147c58e9608a/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= -github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= -github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= @@ -2290,12 +961,9 @@ github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7q github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/common v0.46.0 h1:doXzt5ybi1HBKpsZOL0sSkaNHJJqkyfEWZGGqqScV0Y= github.com/prometheus/common v0.46.0/go.mod h1:Tp0qkxpb9Jsg54QMe+EAmqXkSV7Evdy1BTn+g2pa/hQ= github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= @@ -2306,12 +974,9 @@ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.3.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/protolambda/bls12-381-util v0.0.0-20220416220906-d8552aa452c7/go.mod h1:IToEjHuttnUzwZI5KBSM/LOOW3qLbbrHOEfp3SbECGY= github.com/psiemens/sconfig v0.1.0 h1:xfWqW+TRpih7mXZIqKYTmpRhlZLQ1kbxV8EjllPv76s= github.com/psiemens/sconfig v0.1.0/go.mod h1:+MLKqdledP/8G3rOBpknbLh0IclCf4WneJUtS26JB2U= github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= @@ -2325,18 +990,11 @@ github.com/quic-go/webtransport-go v0.6.0/go.mod h1:9KjU4AEBqEQidGHNDkZrb8CAa1ab github.com/raulk/go-watchdog v1.3.0 h1:oUmdlHxdkXRJlwfG0O9omj8ukerm8MEQavSiDTEtBsk= github.com/raulk/go-watchdog v1.3.0/go.mod h1:fIvOnLbF0b0ZwkB9YU4mOW9Did//4vPZtDqv66NfsMU= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -github.com/retailnext/hllpp v1.0.1-0.20180308014038-101a6d2f8b52/go.mod h1:RDpi1RftBQPUCDRw6SmxeaREsAaRKnOclghuzp/WRzc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRrjvIXnJho= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -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.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= @@ -2349,20 +1007,12 @@ github.com/rs/zerolog v1.29.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6us github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= -github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/schollz/progressbar/v3 v3.13.1 h1:o8rySDYiQ59Mwzy2FELeHY5ZARXZTVJC7iHD6PEFUiE= github.com/schollz/progressbar/v3 v3.13.1/go.mod h1:xvrbki8kfT1fzWzBT/UZd9L6GA+jdL7HAgq2RFnO6fQ= -github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= -github.com/segmentio/kafka-go v0.2.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sethvargo/go-retry v0.2.3 h1:oYlgvIvsju3jNbottWABtbnoLC+GDtLdBHxKWxQm/iU= github.com/sethvargo/go-retry v0.2.3/go.mod h1:1afjQuvh7s4gflMObvjLPaWgluLLyhA1wmVZ6KLpICw= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= @@ -2396,7 +1046,6 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/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/slok/go-http-metrics v0.10.0 h1:rh0LaYEKza5eaYRGDXujKrOln57nHBi4TtVhmNEpbgM= @@ -2417,9 +1066,6 @@ github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasO github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= -github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= -github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= github.com/spf13/afero v1.10.0 h1:EaGW2JJh15aKOejeuJ+wpFSHnbd7GE6Wvp3TsNhb6LY= github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= @@ -2427,8 +1073,6 @@ github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= -github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= @@ -2442,7 +1086,6 @@ github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DM github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/spf13/viper v1.15.0 h1:js3yy885G8xwJa6iOISGFwd+qlUo5AvyXb7CiihdtiU= github.com/spf13/viper v1.15.0/go.mod h1:fFcTBJxvhhzSJiZy8n+PeW6t8l+KeT/uTARa0jHOQLA= -github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q= 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/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= @@ -2454,7 +1097,6 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS 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.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 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= @@ -2462,17 +1104,13 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 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.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -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.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= -github.com/supranational/blst v0.3.8-0.20220526154634-513d2456b344/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= 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.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= @@ -2480,13 +1118,9 @@ github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45 github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= github.com/texttheater/golang-levenshtein/levenshtein v0.0.0-20200805054039-cae8b0eaed6c h1:HelZ2kAFadG0La9d+4htN4HzQ68Bm2iM9qKMSMES6xg= github.com/texttheater/golang-levenshtein/levenshtein v0.0.0-20200805054039-cae8b0eaed6c/go.mod h1:JlzghshsemAMDGZLytTFY8C1JQxQPhnatWqNwUXjggo= -github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= -github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= -github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI= github.com/tklauser/go-sysconf v0.3.9/go.mod h1:11DU/5sG7UexIrp/O6g35hrWzu0JxlwQ3LSFUzyeuhs= 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.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM= github.com/tklauser/numcpus v0.3.0/go.mod h1:yFGUr7TUHQRAhyqBcEg0Ge34zDBAsIvJJcyE6boqnA8= github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= @@ -2494,7 +1128,6 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1 github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/turbolent/prettier v0.0.0-20220320183459-661cc755135d h1:5JInRQbk5UBX8JfUvKh2oYTLMVwj3p6n+wapDDm7hko= github.com/turbolent/prettier v0.0.0-20220320183459-661cc755135d/go.mod h1:Nlx5Y115XQvNcIdIy7dZXaNSUpzwBSge4/Ivk93/Yog= -github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs= 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/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= @@ -2508,16 +1141,6 @@ github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijb github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.10/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= -github.com/urfave/cli/v2 v2.10.2/go.mod h1:f8iq5LtQ/bLxafbdBSLPPNsgaW0l/2fYYEHhAyPlwvo= -github.com/urfave/cli/v2 v2.24.1/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc= -github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= -github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= -github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.6.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w= -github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= -github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= -github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= @@ -2533,28 +1156,17 @@ github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0/go.mod h1:x6AKhvS github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 h1:EKhdznlJHPMoKr0XTrX+IlJs1LH3lyx2nfr1dOlZ79k= github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1/go.mod h1:8UvriyWtv5Q5EOgjHaSseUEdkQfvwFv1I/In/O2M9gc= github.com/whyrusleeping/go-logging v0.0.0-20170515211332-0457bb6b88fc/go.mod h1:bopw91TMyo8J3tvftk8xmU2kPmlrt4nScJQZU2hE5EM= -github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= -github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= -github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= -github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= -github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI= github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.11-flow-expose-msg.0.20240220190333-03695dea34a3 h1:GyrwPbleN4FGHa/Ku1aiNKowV4l4FCKRzZfCbvbv5P4= github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.11-flow-expose-msg.0.20240220190333-03695dea34a3/go.mod h1:Irbd2TlWD6Bk0i9ggIqd+WPz0Axp8wP9VuNCm2+Ibrg= -github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= -github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= -github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc= 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.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg= github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= @@ -2565,7 +1177,6 @@ github.com/zeebo/blake3 v0.2.3 h1:TFoLXsjeXqRNFxSbk35Dk4YtszE/MQQGK10BH4ptoTg= github.com/zeebo/blake3 v0.2.3/go.mod h1:mjJjZpnsyIVtVgTOSpJ9vmRE4wgDeyt2HU3qXvvKCaQ= github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo= github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= -github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= @@ -2578,33 +1189,24 @@ 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.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0 h1:UNQQKPfTDe1J81ViolILjTKPr9WetKW6uei2hFgJmFs= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0/go.mod h1:r9vWsPS/3AQItv3OSlEJ/E4mbrhUbbw18meOjArPtKQ= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 h1:sv9kVfal0MK0wBMCOGr+HeJm9v803BkJxGrk2au7j08= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0/go.mod h1:SK2UL73Zy1quvRPonmOmRDiWk1KBV3LyIeeIxcEApWw= -go.opentelemetry.io/otel v1.8.0/go.mod h1:2pkj+iMj0o03Y+cW6/m8Y4WkRdYN3AvCXCnzRMp9yvM= -go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 h1:cl5P5/GIfFh4t6xyruOgJP5QiA1pw4fYYdv6nc6CBWw= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0/go.mod h1:zgBdWWAu7oEEMC06MMKc5NLbA/1YDXV1sMpSqEeLQLg= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 h1:tIqheXEFWAZ7O8A7m+J0aPTmpJN3YQ7qetUAdkkkKpk= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0/go.mod h1:nUeKExfxAQVbiVFn32YXpXZZHZ61Cc3s3Rn1pDBGAb0= -go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4= go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw= go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg= -go.opentelemetry.io/otel/trace v1.8.0/go.mod h1:0Bt3PXY8w+3pheS3hQUt+wow8b1ojPaTBoTCh2zIFI4= -go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= -go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -2614,14 +1216,11 @@ 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/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.uber.org/automaxprocs v1.5.2/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0= go.uber.org/dig v1.17.1 h1:Tga8Lz8PcYNsWsyHMZ1Vm0OQOUaJNDyvPImgbAu9YSc= go.uber.org/dig v1.17.1/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE= go.uber.org/fx v1.20.1 h1:zVwVQGS8zYvhh9Xxcu4w1M6ESyeMzebzj2NbSayZ4Mk= go.uber.org/fx v1.20.1/go.mod h1:iSYNbHf2y55acNCwCXKx7LbWb5WG1Bnue5RDXz1OREg= -go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= 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/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= @@ -2633,7 +1232,6 @@ go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9i go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.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.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= @@ -2652,40 +1250,19 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190909091759-094676da4a83/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200602180216-279210d13fed/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= -golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= -golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= -golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= -golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= -golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= -golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= -golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= -golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= +golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/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-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE= 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= @@ -2693,29 +1270,10 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0 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-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= -golang.org/x/exp v0.0.0-20220426173459-3bcf042a4bf5/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= -golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= -golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= -golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= -golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= -golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA= golang.org/x/exp v0.0.0-20240119083558-1b970713d09a/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= -golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= 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/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20210216034530-4410531fe030/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20210607152325-775e3b0c77b9/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= -golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= -golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= -golang.org/x/image v0.0.0-20220302094943-723b81ca9867/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= -golang.org/x/image v0.5.0/go.mod h1:FVC7BI/5Ym8R25iw5OLsgshdUBbT1h5jZTpA+mvAdZ4= -golang.org/x/image v0.6.0/go.mod h1:MXLdDR43H7cDJq5GEGXEVeeNhPgi+YYEQ2pC1byI1x0= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -2728,7 +1286,6 @@ golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRu 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= @@ -2740,18 +1297,7 @@ 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.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= -golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 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= @@ -2769,7 +1315,6 @@ golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190227160552-c95aed5357e7/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-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190327091125-710a502c58a2/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= @@ -2780,7 +1325,6 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL 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-20190827160401-ba9fcec4b297/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= @@ -2798,55 +1342,19 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 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-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d/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-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= -golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ= -golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= -golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= -golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= -golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= -golang.org/x/oauth2 v0.0.0-20170207211851-4464e7848382/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -2858,34 +1366,9 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ 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-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= -golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= -golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.0.0-20221006150949-b44042a4b9c1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec= -golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= -golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= -golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= -golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= -golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= -golang.org/x/oauth2 v0.11.0/go.mod h1:LdF7O/8bLR/qWK9DrpXmbHLTouvRHK0SgJl0GmDBchk= -golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0= golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ= golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA= golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= -golang.org/x/perf v0.0.0-20230113213139-801c7ef9e5c5/go.mod h1:UBKtEnL8aqnd+0JHqZ+2qoMDwtuy6cYhhKNoHLBiTQc= 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= @@ -2897,15 +1380,7 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ 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.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180810173357-98c5dad5d1a0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -2937,14 +1412,12 @@ golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7w 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-20191008105621-543471e840be/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-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200107162124-548cf772de50/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= @@ -2966,158 +1439,70 @@ golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200918174421-af09f7315aff/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/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-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210304124612-50617c2ba197/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-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210420205809-ac73e9fd8988/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603125802-9665404d3644/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-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/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-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211020174200-9d6173849985/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211210111614-af8b64212486/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-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/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-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/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-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/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-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= -golang.org/x/sys v0.23.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/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= -golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= -golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo= -golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= -golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= -golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= -golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= -golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= -golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= -golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= +golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= +golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= 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.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= -golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 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-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.3.0/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-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181221001348-537d06c36207/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-20190206041539-40960b6deb8e/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-20190327201419-c70d86f8b7cf/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-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= @@ -3127,21 +1512,17 @@ golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/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-20191108193012-7d206e10da11/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-20191126055441-b0650ceb63d9/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-20200108203644-89082a384178/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= @@ -3163,7 +1544,6 @@ golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc 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-20201124115921-2c860bdd6e78/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-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= @@ -3171,52 +1551,18 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.8-0.20211029000441-d6a9af8af023/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= -golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= -golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= -golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= -golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4= -golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= -golang.org/x/tools v0.10.0/go.mod h1:UJwyiVBsOA2uwvK/e5OY3GTpDUJriEd+/YlqAwLPmyM= -golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= -golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= 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-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= -gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= -gonum.org/v1/gonum v0.0.0-20181121035319-3f7ecaa7e8ca/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= -gonum.org/v1/gonum v0.6.0/go.mod h1:9mxDZsDKxgMAuccQkewq682L+0eCu4dCN2yonUJTCLU= -gonum.org/v1/gonum v0.6.1/go.mod h1:9mxDZsDKxgMAuccQkewq682L+0eCu4dCN2yonUJTCLU= -gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= -gonum.org/v1/gonum v0.9.3/go.mod h1:TZumC3NeyVQskjXqmyWt4S3bINhy7B4eYwW69EbyX+0= -gonum.org/v1/gonum v0.11.0/go.mod h1:fSG4YDCxxUZQJ7rKsQrj0gMOg00Il0Z96/qMA4bVQhA= -gonum.org/v1/gonum v0.13.0/go.mod h1:/WPYRckkfWrhWefxyYTfrTtQR0KH4iyHNuzxqXAKyAU= gonum.org/v1/gonum v0.14.0 h1:2NiG67LD1tEH0D7kM+ps2V+fXmsAnpUeec7n8tcr4S0= gonum.org/v1/gonum v0.14.0/go.mod h1:AoWeoz0becf9QMWtE8iWXNXc27fK4fNeHNf/oMejGfU= -gonum.org/v1/netlib v0.0.0-20181029234149-ec6d1f5cefe6/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= -gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= -gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= -gonum.org/v1/plot v0.9.0/go.mod h1:3Pcqqmp6RHvJI72kgb8fThyUnav364FOsdDo2aGW5lY= -gonum.org/v1/plot v0.10.0/go.mod h1:JWIHJ7U20drSQb/aDpTetJzfC1KlAPldJLpkSy88dvQ= -gonum.org/v1/plot v0.10.1/go.mod h1:VZW5OlhkL1mysU9vaqNHnsy86inf6Ot+jB3r+BczCEo= -google.golang.org/api v0.0.0-20170206182103-3d017632ea10/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= @@ -3240,53 +1586,6 @@ google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz513 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.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= -google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= -google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= -google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= -google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= -google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= -google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= -google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= -google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= -google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= -google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= -google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= -google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= -google.golang.org/api v0.77.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= -google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw= -google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg= -google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o= -google.golang.org/api v0.85.0/go.mod h1:AqZf8Ep9uZ2pyTvgL+x0D3Zt0eoT9b5E8fmzfu6FO2g= -google.golang.org/api v0.90.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= -google.golang.org/api v0.93.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= -google.golang.org/api v0.95.0/go.mod h1:eADj+UBuxkh5zlrSntJghuNeg8HwQ1w5lTKkuqaETEI= -google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= -google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= -google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= -google.golang.org/api v0.99.0/go.mod h1:1YOf74vkVndF7pG6hIHuINsM7eWwpVTAfNMNiL91A08= -google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70= -google.golang.org/api v0.102.0/go.mod h1:3VFl6/fzoA+qNuS1N1/VfXY4LjoXN/wzeIp7TweWwGo= -google.golang.org/api v0.103.0/go.mod h1:hGtW6nK1AC+d9si/UBhw8Xli+QMOf6xyNAyJw4qU9w0= -google.golang.org/api v0.106.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= -google.golang.org/api v0.107.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= -google.golang.org/api v0.108.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= -google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60ca7iI= -google.golang.org/api v0.111.0/go.mod h1:qtFHvU9mhgTJegR31csQ+rwxyUTHOKFqCKWp1J0fdw0= -google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg= -google.golang.org/api v0.118.0/go.mod h1:76TtD3vkgmZ66zZzp72bUUklpmQmKlhh6sYtIjYK+5E= -google.golang.org/api v0.122.0/go.mod h1:gcitW0lvnyWjSp9nKxAbdHKIZ6vF4aajGueeslZOyms= -google.golang.org/api v0.124.0/go.mod h1:xu2HQurE5gi/3t1aFCvhPD781p0a3p11sdunTJ2BlP4= -google.golang.org/api v0.125.0/go.mod h1:mBwVAtz+87bEN6CbA1GtZPDOqY2R5ONPqJeIlvyo4Aw= -google.golang.org/api v0.126.0/go.mod h1:mBwVAtz+87bEN6CbA1GtZPDOqY2R5ONPqJeIlvyo4Aw= -google.golang.org/api v0.128.0/go.mod h1:Y611qgqaE92On/7g65MQgxYul3c0rEB894kniWLY750= -google.golang.org/api v0.139.0/go.mod h1:CVagp6Eekz9CjGZ718Z+sloknzkDJE7Vc1Ckj9+viBk= -google.golang.org/api v0.149.0/go.mod h1:Mwn1B7JTXrzXtnvmzQE2BD6bYZQ8DShKZDZbeN9I7qI= -google.golang.org/api v0.151.0/go.mod h1:ccy+MJ6nrYFgE3WgRx/AMXOxOmU8Q4hSa+jjibzhxcg= google.golang.org/api v0.162.0 h1:Vhs54HkaEpkMBdgGdOT2P6F0csGG/vxDS0hWHJzmmps= google.golang.org/api v0.162.0/go.mod h1:6SulDkfoBIg4NFmCuZ39XeeAgSHCPecfSUuDyYlAHs0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= @@ -3300,7 +1599,6 @@ google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCID google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= -google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= @@ -3311,7 +1609,6 @@ google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRn 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-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= -google.golang.org/genproto v0.0.0-20190716160619-c506a9f90610/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 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= @@ -3319,7 +1616,6 @@ google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvx 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-20200108215221-bd8f9a0ef82f/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= @@ -3332,7 +1628,6 @@ google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/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= @@ -3346,164 +1641,15 @@ google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210126160654-44e461bb6506/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-20210226172003-ab064af71705/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-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= -google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= -google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= -google.golang.org/genproto v0.0.0-20220329172620-7be39ac1afc7/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220722212130-b98a9ff5e252/go.mod h1:GkXuJDJ6aQ7lnJcRF+SJVgFdQhypqgl3LB1C9vabdRE= -google.golang.org/genproto v0.0.0-20220801145646-83ce21fca29f/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc= -google.golang.org/genproto v0.0.0-20220815135757-37a418bb8959/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220817144833-d7fd3f11b9b1/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220829144015-23454907ede3/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220913154956-18f8339a66a5/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220914142337-ca0e39ece12f/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220915135415-7fd63a7952de/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220916172020-2692e8806bfa/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220919141832-68c03719ef51/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006/go.mod h1:ht8XFiar2npT/g4vkk7O0WYS1sHOHbdujxbEp7CJWbw= -google.golang.org/genproto v0.0.0-20220926165614-551eb538f295/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= -google.golang.org/genproto v0.0.0-20220926220553-6981cbe3cfce/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= -google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqwhZAwq4wsRUaVG555sVgsNmIjRtO7t/JH29U= -google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= -google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= -google.golang.org/genproto v0.0.0-20221024153911-1573dae28c9c/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= -google.golang.org/genproto v0.0.0-20221024183307-1bc688fe9f3e/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= -google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c/go.mod h1:CGI5F/G+E5bKwmfYo09AXuVN4dD894kIKUFmVbP2/Fo= -google.golang.org/genproto v0.0.0-20221109142239-94d6d90a7d66/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221114212237-e4508ebdbee1/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221117204609-8f9c96812029/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221201204527-e3fa12d562f3/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd/go.mod h1:cTsE614GARnxrLsqKREzmNYJACSWWpAWdNMwnD7c2BE= -google.golang.org/genproto v0.0.0-20221207170731-23e4bf6bdc37/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230112194545-e10362b5ecf9/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230113154510-dbe35b8444a5/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230123190316-2c411cf9d197/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230124163310-31e0e69b6fc2/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230125152338-dcaf20b6aeaa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230127162408-596548ed4efa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230216225411-c8e22ba71e44/go.mod h1:8B0gmkoRebU8ukX6HP+4wrVQUY1+6PkQ44BSyIlflHA= -google.golang.org/genproto v0.0.0-20230222225845-10f96fb3dbec/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= -google.golang.org/genproto v0.0.0-20230223222841-637eb2293923/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= -google.golang.org/genproto v0.0.0-20230303212802-e74f57abe488/go.mod h1:TvhZT5f700eVlTNwND1xoEZQeWTB2RY/65kplwl/bFA= -google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= -google.golang.org/genproto v0.0.0-20230320184635-7606e756e683/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= -google.golang.org/genproto v0.0.0-20230323212658-478b75c54725/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= -google.golang.org/genproto v0.0.0-20230330154414-c0448cd141ea/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= -google.golang.org/genproto v0.0.0-20230331144136-dcfb400f0633/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= -google.golang.org/genproto v0.0.0-20230403163135-c38d8f061ccd/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= -google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= -google.golang.org/genproto v0.0.0-20230525234025-438c736192d0/go.mod h1:9ExIQyXL5hZrHzQceCwuSYwZZ5QZBazOcprJ5rgs3lY= -google.golang.org/genproto v0.0.0-20230526161137-0005af68ea54/go.mod h1:zqTuNwFlFRsw5zIts5VnzLQxSRqh+CGOTVMlYbY0Eyk= -google.golang.org/genproto v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:zqTuNwFlFRsw5zIts5VnzLQxSRqh+CGOTVMlYbY0Eyk= -google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:xZnkP7mREFX5MORlOPEzLMr+90PPZQ2QWzrVTWfAq64= -google.golang.org/genproto v0.0.0-20230629202037-9506855d4529/go.mod h1:xZnkP7mREFX5MORlOPEzLMr+90PPZQ2QWzrVTWfAq64= -google.golang.org/genproto v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:O9kGHb51iE/nOGvQaDUuadVYqovW56s5emA88lQnj6Y= -google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98/go.mod h1:S7mY02OqCJTD0E1OiQy1F72PWFB4bZJ87cAtLPYgDR0= -google.golang.org/genproto v0.0.0-20230726155614-23370e0ffb3e/go.mod h1:0ggbjUrZYpy1q+ANUS30SEoGZ53cdfwtbuG7Ptgy108= -google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5/go.mod h1:oH/ZOT02u4kWEp7oYBGYFFkCdKS/uYR9Z7+0/xuuFp8= -google.golang.org/genproto v0.0.0-20230821184602-ccc8af3d0e93/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4= -google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4= -google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4= -google.golang.org/genproto v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:CCviP9RmpZ1mxVr8MUjCnSiY09IbAXZxhLE6EhHIdPU= -google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97/go.mod h1:t1VqOqqvce95G3hIDCT5FeO3YUc6Q4Oe24L/+rNMxRk= -google.golang.org/genproto v0.0.0-20231012201019-e917dd12ba7a/go.mod h1:EMfReVxb80Dq1hhioy0sOsY9jCE46YDgHlJ7fWVUWRE= -google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:CgAqfJo+Xmu0GwA0411Ht3OU3OntXwsGmrmjI8ioGXI= google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY= google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo= -google.golang.org/genproto/googleapis/api v0.0.0-20230525234020-1aefcd67740a/go.mod h1:ts19tUU+Z0ZShN1y3aPyq2+O3d5FUNNgT6FtOzmrNn8= -google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= -google.golang.org/genproto/googleapis/api v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= -google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= -google.golang.org/genproto/googleapis/api v0.0.0-20230629202037-9506855d4529/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= -google.golang.org/genproto/googleapis/api v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:mPBs5jNgx2GuQGvFwUvVKqtn6HsUw9nP64BedgvqEsQ= -google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ= -google.golang.org/genproto/googleapis/api v0.0.0-20230726155614-23370e0ffb3e/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ= -google.golang.org/genproto/googleapis/api v0.0.0-20230803162519-f966b187b2e5/go.mod h1:5DZzOUPCLYL3mNkQ0ms0F3EuUNZ7py1Bqeq6sxzI7/Q= -google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk= -google.golang.org/genproto/googleapis/api v0.0.0-20230913181813-007df8e322eb/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk= -google.golang.org/genproto/googleapis/api v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:RdyHbowztCGQySiCvQPgWQWgWhGnouTdCflKoDBt32U= -google.golang.org/genproto/googleapis/api v0.0.0-20231002182017-d307bd883b97/go.mod h1:iargEX0SFPm3xcfMI0d1domjg0ZF4Aa0p2awqyxhvF0= -google.golang.org/genproto/googleapis/api v0.0.0-20231012201019-e917dd12ba7a/go.mod h1:SUBoKXbI1Efip18FClrQVGjWcyd0QZd8KkvdP34t7ww= -google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:IBQ646DjkDkvUIsVq/cc03FUFQ9wbZu7yE396YcL870= google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de h1:jFNzHPIeuzhdRwVhbZdiym9q0ory/xY3sA+v2wPg8I0= google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:5iCWqnniDlqZHrd3neWVTOwvh/v6s3232omMecelax8= -google.golang.org/genproto/googleapis/bytestream v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:ylj+BE99M198VPbBh6A8d9n3w8fChvyLK3wwBOjXBFA= -google.golang.org/genproto/googleapis/bytestream v0.0.0-20230807174057-1744710a1577/go.mod h1:NjCQG/D8JandXxM57PZbAJL1DCNL6EypA0vPPwfsc7c= -google.golang.org/genproto/googleapis/bytestream v0.0.0-20231030173426-d783a09b4405/go.mod h1:GRUCuLdzVqZte8+Dl/D4N25yLzcGqqWaYkeVOwulFqw= google.golang.org/genproto/googleapis/bytestream v0.0.0-20240125205218-1f4bbc51befe h1:weYsP+dNijSQVoLAb5bpUos3ciBpNU/NEVlHFKrk8pg= google.golang.org/genproto/googleapis/bytestream v0.0.0-20240125205218-1f4bbc51befe/go.mod h1:SCz6T5xjNXM4QFPRwxHcfChp7V+9DcXR3ay2TkHR8Tg= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234015-3fc162c6f38a/go.mod h1:xURIpW9ES5+/GZhnV6beoEtxQrnkRGIfP5VQG2tCBLc= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230629202037-9506855d4529/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:8mL13HKkDa+IuJ8yruA3ci0q+0vsUz4m//+ottjwS5o= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230731190214-cbb8c96f2d6d/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230803162519-f966b187b2e5/go.mod h1:zBEcrKX2ZOcEkHWxBPAIvYUWOKKMIhYcmNiUIu2ji3I= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230920183334-c177e329c48b/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:KSqppvjFjtoCI+KGd4PELB0qLNxdJHRGqRI09mB6pQA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97/go.mod h1:v7nGkzlmW8P3n/bKmWBn2WpBjpOEx8Q6gMueudAmKfY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231012201019-e917dd12ba7a/go.mod h1:4cYg8o5yUbm77w8ZX00LhMVNl/YVBFJRYWDc0uYWMs0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:swOH3j0KzcDDgGUWr+SNpyTen5YrXjS3eyPzFYKc6lc= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405/go.mod h1:67X1fPuzjcrkymZzZV1vvkFeTn2Rvc6lYF9MYFGCcwE= google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de h1:cZGRis4/ot9uVm639a+rHCUaG0JJHEsdyzSQTMX+suY= google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY= -google.golang.org/grpc v0.0.0-20170208002647-2a6bf6142e96/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= -google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= @@ -3525,44 +1671,11 @@ google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM 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.32.0/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.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= -google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= -google.golang.org/grpc v1.52.0/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY= -google.golang.org/grpc v1.52.3/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY= -google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= -google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= -google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= -google.golang.org/grpc v1.56.1/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= -google.golang.org/grpc v1.56.2/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= -google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= -google.golang.org/grpc v1.58.2/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= -google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= -google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0 h1:TLkBREm4nIsEcexnCjgQd5GQWaHcqMzwQV0TX9pq8S0= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0/go.mod h1:DNq5QpG7LJqD2AamLZ7zvKE0DEpVl2BSEVjFycAAjRY= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= @@ -3578,19 +1691,12 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba 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.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 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-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/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/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= @@ -3598,23 +1704,17 @@ 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/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= -gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= 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/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= -gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= -gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98= 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/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 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.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -3623,7 +1723,6 @@ 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.0/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= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= @@ -3637,70 +1736,14 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh 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= -honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= -lukechampine.com/blake3 v1.1.7/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA= -lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE= lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= -lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= -lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= -modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= -modernc.org/cc/v3 v3.36.2/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= -modernc.org/cc/v3 v3.36.3/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= -modernc.org/cc/v3 v3.37.0/go.mod h1:vtL+3mdHx/wcj3iEGz84rQa8vEqR6XM84v5Lcvfph20= -modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0= -modernc.org/ccgo/v3 v3.0.0-20220428102840-41399a37e894/go.mod h1:eI31LL8EwEBKPpNpA4bU1/i+sKOwOrQy8D87zWUcRZc= -modernc.org/ccgo/v3 v3.0.0-20220430103911-bc99d88307be/go.mod h1:bwdAnOoaIt8Ax9YdWGjxWsdkPcZyRPHqrOvJxaKAKGw= -modernc.org/ccgo/v3 v3.0.0-20220904174949-82d86e1b6d56/go.mod h1:YSXjPL62P2AMSxBphRHPn7IkzhVHqkvOnRKAKh+W6ZI= -modernc.org/ccgo/v3 v3.16.4/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= -modernc.org/ccgo/v3 v3.16.6/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= -modernc.org/ccgo/v3 v3.16.8/go.mod h1:zNjwkizS+fIFDrDjIAgBSCLkWbJuHF+ar3QRn+Z9aws= -modernc.org/ccgo/v3 v3.16.9/go.mod h1:zNMzC9A9xeNUepy6KuZBbugn3c0Mc9TeiJO4lgvkJDo= -modernc.org/ccgo/v3 v3.16.13-0.20221017192402-261537637ce8/go.mod h1:fUB3Vn0nVPReA+7IG7yZDfjv1TMWjhQP8gCxrFAtL5g= -modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY= -modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= -modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= -modernc.org/libc v0.0.0-20220428101251-2d5f3daf273b/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= -modernc.org/libc v1.16.0/go.mod h1:N4LD6DBE9cf+Dzf9buBlzVJndKr/iJHG97vGLHYnb5A= -modernc.org/libc v1.16.1/go.mod h1:JjJE0eu4yeK7tab2n4S1w8tlWd9MxXLRzheaRnAKymU= -modernc.org/libc v1.16.17/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU= -modernc.org/libc v1.16.19/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= -modernc.org/libc v1.17.0/go.mod h1:XsgLldpP4aWlPlsjqKRdHPqCxCjISdHfM/yeWC5GyW0= -modernc.org/libc v1.17.1/go.mod h1:FZ23b+8LjxZs7XtFMbSzL/EhPxNbfZbErxEHc7cbD9s= -modernc.org/libc v1.17.4/go.mod h1:WNg2ZH56rDEwdropAJeZPQkXmDwh+JCA1s/htl6r2fA= -modernc.org/libc v1.18.0/go.mod h1:vj6zehR5bfc98ipowQOM2nIDUZnVew/wNC/2tOGS+q0= -modernc.org/libc v1.20.3/go.mod h1:ZRfIaEkgrYgZDl6pa4W39HgN5G/yDW+NRmNKZBDFrk0= -modernc.org/libc v1.21.4/go.mod h1:przBsL5RDOZajTVslkugzLBj1evTue36jEomFQOoYuI= -modernc.org/libc v1.22.2/go.mod h1:uvQavJ1pZ0hIoC/jfqNoMLURIMhKzINIWypNM17puug= -modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= -modernc.org/memory v1.2.0/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= -modernc.org/memory v1.2.1/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= -modernc.org/memory v1.3.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= -modernc.org/memory v1.4.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= -modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= -modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/sqlite v1.18.1/go.mod h1:6ho+Gow7oX5V+OiOQ6Tr4xeqbx13UZ6t+Fw9IRUG4d4= -modernc.org/sqlite v1.18.2/go.mod h1:kvrTLEWgxUcHa2GfHBQtanR1H9ht3hTJNtKpzH9k1u0= -modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= -modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= -modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw= -modernc.org/tcl v1.13.2/go.mod h1:7CLiGIPo1M8Rv1Mitpv5akc2+8fxUd2y2UzC/MfMzy0= -modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= -modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= -modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= -modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8= nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g= nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= -pgregory.net/rapid v0.4.7/go.mod h1:UYpPVyjFHzYBGHIxLFoupi8vwk6rXNzRY9OMvVxFIOU= pgregory.net/rapid v1.1.0 h1:CMa0sjHSru3puNx+J0MIAuiiEV4N0qj8/cMWGBBCsjw= pgregory.net/rapid v1.1.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= 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= diff --git a/integration/benchmark/account/account_provider.go b/integration/benchmark/account/account_provider.go index cdbd743d025..d46ecd42dbe 100644 --- a/integration/benchmark/account/account_provider.go +++ b/integration/benchmark/account/account_provider.go @@ -10,9 +10,11 @@ import ( "golang.org/x/sync/errgroup" flowsdk "github.com/onflow/flow-go-sdk" + "github.com/onflow/flow-go/module/util" "github.com/onflow/flow-go-sdk/crypto" + "github.com/onflow/flow-go/fvm/blueprints" "github.com/onflow/flow-go/fvm/systemcontracts" "github.com/onflow/flow-go/integration/benchmark/common" diff --git a/integration/benchmark/contLoadGenerator.go b/integration/benchmark/contLoadGenerator.go index 2ca1cee974c..7f1c31562cf 100644 --- a/integration/benchmark/contLoadGenerator.go +++ b/integration/benchmark/contLoadGenerator.go @@ -12,6 +12,7 @@ import ( flowsdk "github.com/onflow/flow-go-sdk" "github.com/onflow/flow-go-sdk/access" "github.com/onflow/flow-go-sdk/crypto" + "github.com/onflow/flow-go/fvm/systemcontracts" "github.com/onflow/flow-go/integration/benchmark/account" "github.com/onflow/flow-go/integration/benchmark/common" diff --git a/integration/benchmark/follower.go b/integration/benchmark/follower.go index 746c5b17b40..0681570b491 100644 --- a/integration/benchmark/follower.go +++ b/integration/benchmark/follower.go @@ -10,6 +10,7 @@ import ( flowsdk "github.com/onflow/flow-go-sdk" "github.com/onflow/flow-go-sdk/access" + "github.com/onflow/flow-go/module/metrics" "github.com/rs/zerolog" diff --git a/integration/benchmark/follower_test.go b/integration/benchmark/follower_test.go index 1b5b942e497..8ae218a6885 100644 --- a/integration/benchmark/follower_test.go +++ b/integration/benchmark/follower_test.go @@ -10,6 +10,7 @@ import ( "github.com/stretchr/testify/require" flowsdk "github.com/onflow/flow-go-sdk" + mockClient "github.com/onflow/flow-go/integration/benchmark/mock" "github.com/onflow/flow-go/utils/unittest" ) diff --git a/integration/benchmark/load/common.go b/integration/benchmark/load/common.go index 93882d96757..4ce4e589fc8 100644 --- a/integration/benchmark/load/common.go +++ b/integration/benchmark/load/common.go @@ -7,6 +7,7 @@ import ( "github.com/rs/zerolog" flowsdk "github.com/onflow/flow-go-sdk" + "github.com/onflow/flow-go/integration/benchmark/common" "github.com/onflow/flow-go/integration/benchmark/account" diff --git a/integration/benchmark/load/load_type_test.go b/integration/benchmark/load/load_type_test.go index 9a53ffe75a2..a856d4540e9 100644 --- a/integration/benchmark/load/load_type_test.go +++ b/integration/benchmark/load/load_type_test.go @@ -8,14 +8,13 @@ import ( "github.com/onflow/cadence" "github.com/onflow/cadence/encoding/ccf" - convert2 "github.com/onflow/flow-emulator/convert" "github.com/rs/zerolog" "github.com/stretchr/testify/require" sdk "github.com/onflow/flow-go-sdk" "github.com/onflow/flow-go-sdk/crypto" - cadenceCommon "github.com/onflow/cadence/runtime/common" + cadenceCommon "github.com/onflow/cadence/common" "github.com/onflow/flow-go/engine/execution/computation" "github.com/onflow/flow-go/engine/execution/testutil" @@ -28,6 +27,7 @@ import ( "github.com/onflow/flow-go/integration/benchmark/common" "github.com/onflow/flow-go/integration/benchmark/load" "github.com/onflow/flow-go/integration/convert" + "github.com/onflow/flow-go/integration/internal/emulator" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/utils/unittest" ) @@ -254,7 +254,7 @@ func (t *testTransactionSender) Send(tx *sdk.Transaction) (sdk.TransactionResult Error: result.Err, BlockID: sdk.EmptyID, BlockHeight: 0, - TransactionID: convert2.FlowIdentifierToSDK(txBody.ID()), + TransactionID: emulator.FlowIdentifierToSDK(txBody.ID()), CollectionID: sdk.EmptyID, } diff --git a/integration/benchmark/load/simple_load.go b/integration/benchmark/load/simple_load.go index e10927d5493..b1b05cf9005 100644 --- a/integration/benchmark/load/simple_load.go +++ b/integration/benchmark/load/simple_load.go @@ -7,6 +7,7 @@ import ( "github.com/rs/zerolog" flowsdk "github.com/onflow/flow-go-sdk" + "github.com/onflow/flow-go/fvm/blueprints" "github.com/onflow/flow-go/integration/benchmark/account" ) diff --git a/integration/benchmark/load/token_transfer_load.go b/integration/benchmark/load/token_transfer_load.go index e382c58611f..703b013900b 100644 --- a/integration/benchmark/load/token_transfer_load.go +++ b/integration/benchmark/load/token_transfer_load.go @@ -5,6 +5,7 @@ import ( "github.com/rs/zerolog" flowsdk "github.com/onflow/flow-go-sdk" + "github.com/onflow/flow-go/fvm/errors" "github.com/onflow/flow-go/fvm/systemcontracts" "github.com/onflow/flow-go/integration/benchmark/account" diff --git a/integration/benchmark/load/token_transfer_multiple_load.go b/integration/benchmark/load/token_transfer_multiple_load.go index 3e7ab6930a1..05bd4d6ca5b 100644 --- a/integration/benchmark/load/token_transfer_multiple_load.go +++ b/integration/benchmark/load/token_transfer_multiple_load.go @@ -9,6 +9,7 @@ import ( "github.com/onflow/flow-go/model/flow" flowsdk "github.com/onflow/flow-go-sdk" + "github.com/onflow/flow-go/fvm/systemcontracts" "github.com/onflow/flow-go/integration/benchmark/account" "github.com/onflow/flow-go/integration/benchmark/scripts" diff --git a/integration/benchmark/scripts/scripts.go b/integration/benchmark/scripts/scripts.go index 105b5285c1f..b21b564ee20 100644 --- a/integration/benchmark/scripts/scripts.go +++ b/integration/benchmark/scripts/scripts.go @@ -8,6 +8,7 @@ import ( "github.com/onflow/cadence" flowsdk "github.com/onflow/flow-go-sdk" + "github.com/onflow/flow-go/model/flow" ) diff --git a/integration/benchnet2/flow/templates/access.yml b/integration/benchnet2/flow/templates/access.yml index 00208c935e8..d9a68f235b4 100644 --- a/integration/benchnet2/flow/templates/access.yml +++ b/integration/benchnet2/flow/templates/access.yml @@ -7,8 +7,8 @@ metadata: name: {{ $k }} labels: app: {{ $k }} - networkId: {{ $.Values.networkId }} - nodeType: access + network: {{ $.Values.networkId }} + role: access owner: {{ $.Values.owner }} service: flow @@ -18,9 +18,9 @@ spec: selector: matchLabels: app: {{ $k }} - nodeType: access + role: access service: flow - networkId: {{ $.Values.networkId }} + network: {{ $.Values.networkId }} template: metadata: @@ -30,9 +30,9 @@ spec: prometheus.io/port: "8080" labels: app: {{ $k }} - nodeType: access + role: access service: flow - networkId: {{ $.Values.networkId }} + network: {{ $.Values.networkId }} spec: nodeSelector: iam.gke.io/gke-metadata-server-enabled: "true" @@ -84,7 +84,7 @@ spec: - metadata: name: data labels: - networkId: {{ $.Values.networkId }} + network: {{ $.Values.networkId }} spec: accessModes: ["ReadWriteOnce"] resources: @@ -105,7 +105,7 @@ metadata: name: {{ $k }} labels: app: {{ $k }} - networkId: {{ $.Values.networkId }} + network: {{ $.Values.networkId }} spec: {{ if $v.servicePorts }} ports: {{ $v.servicePorts | toYaml | nindent 12 }} diff --git a/integration/benchnet2/flow/templates/collection.yml b/integration/benchnet2/flow/templates/collection.yml index a12805e6103..e73d00f7b18 100644 --- a/integration/benchnet2/flow/templates/collection.yml +++ b/integration/benchnet2/flow/templates/collection.yml @@ -7,8 +7,8 @@ metadata: name: {{ $k }} labels: app: {{ $k }} - networkId: {{ $.Values.networkId }} - nodeType: collection + network: {{ $.Values.networkId }} + role: collection owner: {{ $.Values.owner }} service: flow @@ -18,7 +18,7 @@ spec: selector: matchLabels: app: {{ $k }} - nodeType: collection + role: collection service: flow template: @@ -29,9 +29,9 @@ spec: prometheus.io/port: "8080" labels: app: {{ $k }} - nodeType: collection + role: collection service: flow - networkId: {{ $.Values.networkId }} + network: {{ $.Values.networkId }} spec: nodeSelector: iam.gke.io/gke-metadata-server-enabled: "true" @@ -83,7 +83,7 @@ spec: - metadata: name: data labels: - networkId: {{ $.Values.networkId }} + network: {{ $.Values.networkId }} spec: accessModes: ["ReadWriteOnce"] resources: @@ -103,7 +103,7 @@ metadata: name: {{ $k }} labels: app: {{ $k }} - networkId: {{ $.Values.networkId }} + network: {{ $.Values.networkId }} owner: {{ $.Values.owner }} spec: {{ if $v.servicePorts }} @@ -114,4 +114,4 @@ spec: selector: app: {{ $k }} type: NodePort -{{- end }} \ No newline at end of file +{{- end }} diff --git a/integration/benchnet2/flow/templates/consensus.yml b/integration/benchnet2/flow/templates/consensus.yml index bbe42e00370..ddfbf84380a 100644 --- a/integration/benchnet2/flow/templates/consensus.yml +++ b/integration/benchnet2/flow/templates/consensus.yml @@ -7,8 +7,8 @@ metadata: name: {{ $k }} labels: app: {{ $k }} - networkId: {{ $.Values.networkId }} - nodeType: consensus + network: {{ $.Values.networkId }} + role: consensus owner: {{ $.Values.owner }} service: flow @@ -18,7 +18,7 @@ spec: selector: matchLabels: app: {{ $k }} - nodeType: consensus + role: consensus service: flow template: @@ -29,9 +29,9 @@ spec: prometheus.io/port: "8080" labels: app: {{ $k }} - nodeType: consensus + role: consensus service: flow - networkId: {{ $.Values.networkId }} + network: {{ $.Values.networkId }} spec: nodeSelector: iam.gke.io/gke-metadata-server-enabled: "true" @@ -83,7 +83,7 @@ spec: - metadata: name: data labels: - networkId: {{ $.Values.networkId }} + network: {{ $.Values.networkId }} spec: accessModes: ["ReadWriteOnce"] resources: @@ -103,7 +103,7 @@ metadata: name: {{ $k }} labels: app: {{ $k }} - networkId: {{ $.Values.networkId }} + network: {{ $.Values.networkId }} owner: {{ $.Values.owner }} spec: {{ if $v.servicePorts }} @@ -114,4 +114,4 @@ spec: selector: app: {{ $k }} type: NodePort -{{- end }} \ No newline at end of file +{{- end }} diff --git a/integration/benchnet2/flow/templates/execution.yml b/integration/benchnet2/flow/templates/execution.yml index f620edd1b21..67e6daad85d 100644 --- a/integration/benchnet2/flow/templates/execution.yml +++ b/integration/benchnet2/flow/templates/execution.yml @@ -7,8 +7,8 @@ metadata: name: {{ $k }} labels: app: {{ $k }} - networkId: {{ $.Values.networkId }} - nodeType: execution + network: {{ $.Values.networkId }} + role: execution owner: {{ $.Values.owner }} service: flow @@ -18,7 +18,7 @@ spec: selector: matchLabels: app: {{ $k }} - nodeType: execution + role: execution service: flow template: @@ -29,9 +29,9 @@ spec: prometheus.io/port: "8080" labels: app: {{ $k }} - nodeType: execution + role: execution service: flow - networkId: {{ $.Values.networkId }} + network: {{ $.Values.networkId }} spec: nodeSelector: iam.gke.io/gke-metadata-server-enabled: "true" @@ -83,7 +83,7 @@ spec: - metadata: name: data labels: - networkId: {{ $.Values.networkId }} + network: {{ $.Values.networkId }} spec: accessModes: ["ReadWriteOnce"] resources: @@ -103,7 +103,7 @@ metadata: name: {{ $k }} labels: app: {{ $k }} - networkId: {{ $.Values.networkId }} + network: {{ $.Values.networkId }} owner: {{ $.Values.owner }} spec: {{ if $v.servicePorts }} diff --git a/integration/benchnet2/flow/templates/verification.yml b/integration/benchnet2/flow/templates/verification.yml index b189182d40d..02d7870e131 100644 --- a/integration/benchnet2/flow/templates/verification.yml +++ b/integration/benchnet2/flow/templates/verification.yml @@ -7,9 +7,9 @@ metadata: name: {{ $k }} labels: app: {{ $k }} - nodeType: verification + network: {{ $.Values.networkId }} + role: verification service: flow - networkId: {{ $.Values.networkId }} spec: serviceName: {{ $k }} @@ -17,9 +17,9 @@ spec: selector: matchLabels: app: {{ $k }} - nodeType: verification + role: verification service: flow - networkId: {{ $.Values.networkId }} + network: {{ $.Values.networkId }} template: metadata: @@ -29,8 +29,8 @@ spec: prometheus.io/port: "8080" labels: app: {{ $k }} - networkId: {{ $.Values.networkId }} - nodeType: verification + network: {{ $.Values.networkId }} + role: verification owner: {{ $.Values.owner }} service: flow spec: @@ -83,7 +83,7 @@ spec: - metadata: name: data labels: - networkId: {{ $.Values.networkId }} + network: {{ $.Values.networkId }} spec: accessModes: ["ReadWriteOnce"] resources: @@ -103,7 +103,7 @@ metadata: name: {{ $k }} labels: app: {{ $k }} - networkId: {{ $.Values.networkId }} + network: {{ $.Values.networkId }} owner: {{ $.Values.owner }} spec: {{ if $v.servicePorts }} @@ -114,4 +114,4 @@ spec: selector: app: {{ $k }} type: NodePort -{{- end }} \ No newline at end of file +{{- end }} diff --git a/integration/dkg/dkg_client_test.go b/integration/dkg/dkg_client_test.go index 2399c1401e5..728915004de 100644 --- a/integration/dkg/dkg_client_test.go +++ b/integration/dkg/dkg_client_test.go @@ -10,10 +10,7 @@ import ( "github.com/stretchr/testify/suite" "github.com/onflow/cadence" - jsoncdc "github.com/onflow/cadence/encoding/json" - emulator "github.com/onflow/flow-emulator/emulator" - "github.com/onflow/crypto" "github.com/onflow/flow-core-contracts/lib/go/contracts" "github.com/onflow/flow-core-contracts/lib/go/templates" @@ -23,6 +20,7 @@ import ( sdktemplates "github.com/onflow/flow-go-sdk/templates" "github.com/onflow/flow-go-sdk/test" + emulator "github.com/onflow/flow-go/integration/internal/emulator" "github.com/onflow/flow-go/integration/utils" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module/dkg" @@ -34,13 +32,13 @@ type ClientSuite struct { contractClient *dkg.Client - env templates.Environment - blockchain emulator.Emulator - emulatorClient *utils.EmulatorClient - - dkgAddress sdk.Address - dkgAccountKey *sdk.AccountKey - dkgSigner sdkcrypto.Signer + env templates.Environment + blockchain emulator.Emulator + emulatorClient *utils.EmulatorClient + serviceAccountAddress sdk.Address + dkgAddress sdk.Address + dkgAccountKey *sdk.AccountKey + dkgSigner sdkcrypto.Signer } func TestDKGClient(t *testing.T) { @@ -57,7 +55,7 @@ func (s *ClientSuite) SetupTest() { s.blockchain = blockchain s.emulatorClient = utils.NewEmulatorClient(blockchain) - + s.serviceAccountAddress = sdk.Address(s.blockchain.ServiceKey().Address) // deploy contract s.deployDKGContract() @@ -234,16 +232,16 @@ func (s *ClientSuite) setUpAdmin() { setUpAdminTx := sdk.NewTransaction(). SetScript(templates.GeneratePublishDKGParticipantScript(s.env)). SetComputeLimit(9999). - SetProposalKey(s.blockchain.ServiceKey().Address, s.blockchain.ServiceKey().Index, + SetProposalKey(s.serviceAccountAddress, s.blockchain.ServiceKey().Index, s.blockchain.ServiceKey().SequenceNumber). - SetPayer(s.blockchain.ServiceKey().Address). + SetPayer(s.serviceAccountAddress). AddAuthorizer(s.dkgAddress) signer, err := s.blockchain.ServiceKey().Signer() require.NoError(s.T(), err) s.signAndSubmit(setUpAdminTx, - []sdk.Address{s.blockchain.ServiceKey().Address, s.dkgAddress}, + []sdk.Address{s.serviceAccountAddress, s.dkgAddress}, []sdkcrypto.Signer{signer, s.dkgSigner}, ) } @@ -262,9 +260,9 @@ func (s *ClientSuite) startDKGWithParticipants(nodeIDs []flow.Identifier) { startDKGTx := sdk.NewTransaction(). SetScript(templates.GenerateStartDKGScript(s.env)). SetComputeLimit(9999). - SetProposalKey(s.blockchain.ServiceKey().Address, s.blockchain.ServiceKey().Index, + SetProposalKey(s.serviceAccountAddress, s.blockchain.ServiceKey().Index, s.blockchain.ServiceKey().SequenceNumber). - SetPayer(s.blockchain.ServiceKey().Address). + SetPayer(s.serviceAccountAddress). AddAuthorizer(s.dkgAddress) err := startDKGTx.AddArgument(cadence.NewArray(valueNodeIDs)) @@ -274,7 +272,7 @@ func (s *ClientSuite) startDKGWithParticipants(nodeIDs []flow.Identifier) { require.NoError(s.T(), err) s.signAndSubmit(startDKGTx, - []sdk.Address{s.blockchain.ServiceKey().Address, s.dkgAddress}, + []sdk.Address{s.serviceAccountAddress, s.dkgAddress}, []sdkcrypto.Signer{signer, s.dkgSigner}, ) @@ -293,9 +291,9 @@ func (s *ClientSuite) createParticipant(nodeID flow.Identifier, authoriser sdk.A createParticipantTx := sdk.NewTransaction(). SetScript(templates.GenerateCreateDKGParticipantScript(s.env)). SetComputeLimit(9999). - SetProposalKey(s.blockchain.ServiceKey().Address, s.blockchain.ServiceKey().Index, + SetProposalKey(s.serviceAccountAddress, s.blockchain.ServiceKey().Index, s.blockchain.ServiceKey().SequenceNumber). - SetPayer(s.blockchain.ServiceKey().Address). + SetPayer(s.serviceAccountAddress). AddAuthorizer(authoriser) err := createParticipantTx.AddArgument(cadence.NewAddress(s.dkgAddress)) @@ -310,7 +308,7 @@ func (s *ClientSuite) createParticipant(nodeID flow.Identifier, authoriser sdk.A require.NoError(s.T(), err) s.signAndSubmit(createParticipantTx, - []sdk.Address{s.blockchain.ServiceKey().Address, authoriser}, + []sdk.Address{s.serviceAccountAddress, authoriser}, []sdkcrypto.Signer{s2, signer}, ) diff --git a/integration/dkg/dkg_emulator_suite.go b/integration/dkg/dkg_emulator_suite.go index ee66e7e594c..d95d0baa99c 100644 --- a/integration/dkg/dkg_emulator_suite.go +++ b/integration/dkg/dkg_emulator_suite.go @@ -14,24 +14,23 @@ import ( jsoncdc "github.com/onflow/cadence/encoding/json" "github.com/onflow/flow-core-contracts/lib/go/contracts" "github.com/onflow/flow-core-contracts/lib/go/templates" - emulator "github.com/onflow/flow-emulator/emulator" sdk "github.com/onflow/flow-go-sdk" sdkcrypto "github.com/onflow/flow-go-sdk/crypto" sdktemplates "github.com/onflow/flow-go-sdk/templates" "github.com/onflow/flow-go-sdk/test" - "github.com/onflow/flow-go/module/metrics" - dkgeng "github.com/onflow/flow-go/engine/consensus/dkg" "github.com/onflow/flow-go/engine/testutil" "github.com/onflow/flow-go/fvm/systemcontracts" + emulator "github.com/onflow/flow-go/integration/internal/emulator" "github.com/onflow/flow-go/integration/tests/lib" "github.com/onflow/flow-go/integration/utils" "github.com/onflow/flow-go/model/bootstrap" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/module/dkg" + "github.com/onflow/flow-go/module/metrics" "github.com/onflow/flow-go/network/stub" "github.com/onflow/flow-go/state/protocol/events/gadgets" "github.com/onflow/flow-go/storage/badger" @@ -54,10 +53,10 @@ type EmulatorSuite struct { dkgAccountKey *sdk.AccountKey dkgSigner sdkcrypto.Signer checkDKGUnhappy bool // activate log hook for DKGBroker to check if the DKG core is flagging misbehaviours - - netIDs flow.IdentityList - nodeAccounts []*nodeAccount - nodes []*node + serviceAccountAddress sdk.Address + netIDs flow.IdentityList + nodeAccounts []*nodeAccount + nodes []*node } func (s *EmulatorSuite) SetupTest() { @@ -119,7 +118,7 @@ func (s *EmulatorSuite) initEmulator() { s.Require().NoError(err) s.blockchain = blockchain - + s.serviceAccountAddress = sdk.Address(s.blockchain.ServiceKey().Address) s.adminEmulatorClient = utils.NewEmulatorClient(blockchain) s.hub = stub.NewNetworkHub() @@ -163,15 +162,15 @@ func (s *EmulatorSuite) setupDKGAdmin() { SetScript(templates.GeneratePublishDKGParticipantScript(s.env)). SetComputeLimit(9999). SetProposalKey( - s.blockchain.ServiceKey().Address, + s.serviceAccountAddress, s.blockchain.ServiceKey().Index, s.blockchain.ServiceKey().SequenceNumber). - SetPayer(s.blockchain.ServiceKey().Address). + SetPayer(s.serviceAccountAddress). AddAuthorizer(s.dkgAddress) signer, err := s.blockchain.ServiceKey().Signer() require.NoError(s.T(), err) _, err = s.prepareAndSubmit(setUpAdminTx, - []sdk.Address{s.blockchain.ServiceKey().Address, s.dkgAddress}, + []sdk.Address{s.serviceAccountAddress, s.dkgAddress}, []sdkcrypto.Signer{signer, s.dkgSigner}, ) require.NoError(s.T(), err) @@ -229,13 +228,13 @@ func (s *EmulatorSuite) createAndFundAccount(netID bootstrap.NodeInfo) *nodeAcco sc.FungibleToken.Address.Hex(), sc.FlowToken.Address.Hex(), ))). - AddAuthorizer(s.blockchain.ServiceKey().Address). + AddAuthorizer(s.serviceAccountAddress). SetProposalKey( - s.blockchain.ServiceKey().Address, + s.serviceAccountAddress, s.blockchain.ServiceKey().Index, s.blockchain.ServiceKey().SequenceNumber, ). - SetPayer(s.blockchain.ServiceKey().Address) + SetPayer(s.serviceAccountAddress) err = fundAccountTx.AddArgument(cadence.UFix64(1_000_000)) require.NoError(s.T(), err) @@ -244,7 +243,7 @@ func (s *EmulatorSuite) createAndFundAccount(netID bootstrap.NodeInfo) *nodeAcco signer, err := s.blockchain.ServiceKey().Signer() require.NoError(s.T(), err) _, err = s.prepareAndSubmit(fundAccountTx, - []sdk.Address{s.blockchain.ServiceKey().Address}, + []sdk.Address{s.serviceAccountAddress}, []sdkcrypto.Signer{signer}, ) require.NoError(s.T(), err) @@ -307,10 +306,10 @@ func (s *EmulatorSuite) startDKGWithParticipants(accounts []*nodeAccount) { SetScript(templates.GenerateStartDKGScript(s.env)). SetComputeLimit(9999). SetProposalKey( - s.blockchain.ServiceKey().Address, + s.serviceAccountAddress, s.blockchain.ServiceKey().Index, s.blockchain.ServiceKey().SequenceNumber). - SetPayer(s.blockchain.ServiceKey().Address). + SetPayer(s.serviceAccountAddress). AddAuthorizer(s.dkgAddress) err := startDKGTx.AddArgument(cadence.NewArray(valueNodeIDs)) @@ -318,7 +317,7 @@ func (s *EmulatorSuite) startDKGWithParticipants(accounts []*nodeAccount) { signer, err := s.blockchain.ServiceKey().Signer() require.NoError(s.T(), err) _, err = s.prepareAndSubmit(startDKGTx, - []sdk.Address{s.blockchain.ServiceKey().Address, s.dkgAddress}, + []sdk.Address{s.serviceAccountAddress, s.dkgAddress}, []sdkcrypto.Signer{signer, s.dkgSigner}, ) require.NoError(s.T(), err) @@ -334,7 +333,7 @@ func (s *EmulatorSuite) claimDKGParticipant(node *node) { SetScript(templates.GenerateCreateDKGParticipantScript(s.env)). SetComputeLimit(9999). SetProposalKey( - s.blockchain.ServiceKey().Address, + s.serviceAccountAddress, s.blockchain.ServiceKey().Index, s.blockchain.ServiceKey().SequenceNumber, ). @@ -350,7 +349,7 @@ func (s *EmulatorSuite) claimDKGParticipant(node *node) { signer, err := s.blockchain.ServiceKey().Signer() require.NoError(s.T(), err) _, err = s.prepareAndSubmit(createParticipantTx, - []sdk.Address{node.account.accountAddress, s.blockchain.ServiceKey().Address, s.dkgAddress}, + []sdk.Address{node.account.accountAddress, s.serviceAccountAddress, s.dkgAddress}, []sdkcrypto.Signer{node.account.accountSigner, signer, s.dkgSigner}, ) require.NoError(s.T(), err) @@ -371,21 +370,21 @@ func (s *EmulatorSuite) sendDummyTx() (*flow.Block, error) { createAccountTx, err := sdktemplates.CreateAccount( []*sdk.AccountKey{test.AccountKeyGenerator().New()}, []sdktemplates.Contract{}, - s.blockchain.ServiceKey().Address) + s.serviceAccountAddress) if err != nil { return nil, err } createAccountTx. SetProposalKey( - s.blockchain.ServiceKey().Address, + s.serviceAccountAddress, s.blockchain.ServiceKey().Index, s.blockchain.ServiceKey().SequenceNumber). - SetPayer(s.blockchain.ServiceKey().Address) + SetPayer(s.serviceAccountAddress) signer, err := s.blockchain.ServiceKey().Signer() require.NoError(s.T(), err) block, err := s.prepareAndSubmit(createAccountTx, - []sdk.Address{s.blockchain.ServiceKey().Address}, + []sdk.Address{s.serviceAccountAddress}, []sdkcrypto.Signer{signer}, ) return block, err diff --git a/integration/dkg/node.go b/integration/dkg/node.go index cbea2b7f44a..5c16acba499 100644 --- a/integration/dkg/node.go +++ b/integration/dkg/node.go @@ -8,6 +8,7 @@ import ( sdk "github.com/onflow/flow-go-sdk" sdkcrypto "github.com/onflow/flow-go-sdk/crypto" + "github.com/onflow/flow-go/engine/consensus/dkg" testmock "github.com/onflow/flow-go/engine/testutil/mock" "github.com/onflow/flow-go/model/bootstrap" diff --git a/integration/epochs/cluster_epoch_test.go b/integration/epochs/cluster_epoch_test.go index 953a684e3e7..fbee6b5af4b 100644 --- a/integration/epochs/cluster_epoch_test.go +++ b/integration/epochs/cluster_epoch_test.go @@ -13,13 +13,12 @@ import ( "github.com/onflow/flow-core-contracts/lib/go/contracts" "github.com/onflow/flow-core-contracts/lib/go/templates" - emulator "github.com/onflow/flow-emulator/emulator" - sdk "github.com/onflow/flow-go-sdk" sdkcrypto "github.com/onflow/flow-go-sdk/crypto" sdktemplates "github.com/onflow/flow-go-sdk/templates" "github.com/onflow/flow-go-sdk/test" + emulator "github.com/onflow/flow-go/integration/internal/emulator" "github.com/onflow/flow-go/integration/utils" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/model/flow/factory" @@ -31,10 +30,10 @@ import ( type Suite struct { suite.Suite - env templates.Environment - blockchain *emulator.Blockchain - emulatorClient *utils.EmulatorClient - + env templates.Environment + blockchain *emulator.Blockchain + emulatorClient *utils.EmulatorClient + serviceAccountAddress sdk.Address // Quorum Certificate deployed account and address qcAddress sdk.Address qcAccountKey *sdk.AccountKey @@ -51,7 +50,7 @@ func (s *Suite) SetupTest() { ) s.Require().NoError(err) s.emulatorClient = utils.NewEmulatorClient(s.blockchain) - + s.serviceAccountAddress = sdk.Address(s.blockchain.ServiceKey().Address) // deploy epoch qc contract s.deployEpochQCContract() } @@ -104,16 +103,16 @@ func (s *Suite) PublishVoter() { publishVoterTx := sdk.NewTransaction(). SetScript(templates.GeneratePublishVoterScript(s.env)). SetComputeLimit(9999). - SetProposalKey(s.blockchain.ServiceKey().Address, + SetProposalKey(s.serviceAccountAddress, s.blockchain.ServiceKey().Index, s.blockchain.ServiceKey().SequenceNumber). - SetPayer(s.blockchain.ServiceKey().Address). + SetPayer(s.serviceAccountAddress). AddAuthorizer(s.qcAddress) signer, err := s.blockchain.ServiceKey().Signer() require.NoError(s.T(), err) s.SignAndSubmit(publishVoterTx, - []sdk.Address{s.blockchain.ServiceKey().Address, s.qcAddress}, + []sdk.Address{s.serviceAccountAddress, s.qcAddress}, []sdkcrypto.Signer{signer, s.qcSigner}) } @@ -124,9 +123,9 @@ func (s *Suite) StartVoting(clustering flow.ClusterList, clusterCount, nodesPerC startVotingTx := sdk.NewTransaction(). SetScript(templates.GenerateStartVotingScript(s.env)). SetComputeLimit(9999). - SetProposalKey(s.blockchain.ServiceKey().Address, + SetProposalKey(s.serviceAccountAddress, s.blockchain.ServiceKey().Index, s.blockchain.ServiceKey().SequenceNumber). - SetPayer(s.blockchain.ServiceKey().Address). + SetPayer(s.serviceAccountAddress). AddAuthorizer(s.qcAddress) clusterIndices := make([]cadence.Value, 0, clusterCount) @@ -170,7 +169,7 @@ func (s *Suite) StartVoting(clustering flow.ClusterList, clusterCount, nodesPerC require.NoError(s.T(), err) s.SignAndSubmit(startVotingTx, - []sdk.Address{s.blockchain.ServiceKey().Address, s.qcAddress}, + []sdk.Address{s.serviceAccountAddress, s.qcAddress}, []sdkcrypto.Signer{signer, s.qcSigner}) } @@ -180,9 +179,9 @@ func (s *Suite) CreateVoterResource(address sdk.Address, nodeID flow.Identifier, registerVoterTx := sdk.NewTransaction(). SetScript(templates.GenerateCreateVoterScript(s.env)). SetComputeLimit(9999). - SetProposalKey(s.blockchain.ServiceKey().Address, + SetProposalKey(s.serviceAccountAddress, s.blockchain.ServiceKey().Index, s.blockchain.ServiceKey().SequenceNumber). - SetPayer(s.blockchain.ServiceKey().Address). + SetPayer(s.serviceAccountAddress). AddAuthorizer(address) err := registerVoterTx.AddArgument(cadence.NewAddress(s.qcAddress)) @@ -202,7 +201,7 @@ func (s *Suite) CreateVoterResource(address sdk.Address, nodeID flow.Identifier, require.NoError(s.T(), err) s.SignAndSubmit(registerVoterTx, - []sdk.Address{s.blockchain.ServiceKey().Address, address}, + []sdk.Address{s.serviceAccountAddress, address}, []sdkcrypto.Signer{signer, nodeSigner}) } @@ -210,16 +209,16 @@ func (s *Suite) StopVoting() { tx := sdk.NewTransaction(). SetScript(templates.GenerateStopVotingScript(s.env)). SetComputeLimit(9999). - SetProposalKey(s.blockchain.ServiceKey().Address, + SetProposalKey(s.serviceAccountAddress, s.blockchain.ServiceKey().Index, s.blockchain.ServiceKey().SequenceNumber). - SetPayer(s.blockchain.ServiceKey().Address). + SetPayer(s.serviceAccountAddress). AddAuthorizer(s.qcAddress) signer, err := s.blockchain.ServiceKey().Signer() require.NoError(s.T(), err) s.SignAndSubmit(tx, - []sdk.Address{s.blockchain.ServiceKey().Address, s.qcAddress}, + []sdk.Address{s.serviceAccountAddress, s.qcAddress}, []sdkcrypto.Signer{signer, s.qcSigner}) } diff --git a/integration/go.mod b/integration/go.mod index 8723aa74250..eff334d5a9d 100644 --- a/integration/go.mod +++ b/integration/go.mod @@ -20,23 +20,24 @@ require ( github.com/ipfs/go-ds-badger2 v0.1.3 github.com/ipfs/go-ds-pebble v0.3.1-0.20240828032824-d745b9d3200b github.com/libp2p/go-libp2p v0.32.2 - github.com/onflow/cadence v1.0.0-preview.52 + github.com/onflow/cadence v1.2.2 github.com/onflow/crypto v0.25.2 - github.com/onflow/flow-core-contracts/lib/go/contracts v1.3.1 - github.com/onflow/flow-core-contracts/lib/go/templates v1.3.1 - github.com/onflow/flow-emulator v1.0.0-preview.41.0.20240829134601-0be55d6970b5 - github.com/onflow/flow-go v0.37.7-0.20240826193109-e211841b59f5 - github.com/onflow/flow-go-sdk v1.0.0-preview.55 + github.com/onflow/flow-core-contracts/lib/go/contracts v1.4.0 + github.com/onflow/flow-core-contracts/lib/go/templates v1.4.0 + github.com/onflow/flow-go v0.38.0-preview.0.0.20241021221952-af9cd6e99de1 + github.com/onflow/flow-go-sdk v1.2.3 github.com/onflow/flow-go/insecure v0.0.0-00010101000000-000000000000 - github.com/onflow/flow/protobuf/go/flow v0.4.6 + github.com/onflow/flow/protobuf/go/flow v0.4.7 github.com/onflow/go-ethereum v1.14.7 github.com/prometheus/client_golang v1.18.0 github.com/prometheus/client_model v0.5.0 github.com/prometheus/common v0.46.0 + github.com/psiemens/graceland v1.0.0 github.com/rs/zerolog v1.29.0 github.com/stretchr/testify v1.9.0 go.einride.tech/pid v0.1.0 go.uber.org/atomic v1.11.0 + go.uber.org/mock v0.4.0 golang.org/x/exp v0.0.0-20240119083558-1b970713d09a golang.org/x/sync v0.8.0 google.golang.org/grpc v1.63.2 @@ -104,7 +105,6 @@ require ( github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect github.com/dgraph-io/ristretto v0.1.0 // indirect github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect - github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/distribution/reference v0.5.0 // indirect github.com/docker/cli v24.0.6+incompatible // indirect github.com/docker/distribution v2.8.3+incompatible // indirect @@ -129,7 +129,6 @@ require ( github.com/gammazero/workerpool v1.1.2 // indirect github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff // indirect github.com/getsentry/sentry-go v0.27.0 // indirect - github.com/glebarez/go-sqlite v1.22.0 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/go-billy/v5 v5.5.0 // indirect github.com/go-kit/kit v0.12.0 // indirect @@ -141,7 +140,6 @@ require ( github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.14.1 // indirect - github.com/go-redis/redis/v8 v8.11.5 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect @@ -215,7 +213,6 @@ require ( github.com/libp2p/go-netroute v0.2.1 // indirect github.com/libp2p/go-reuseport v0.4.0 // indirect github.com/libp2p/go-yamux/v4 v4.0.1 // indirect - github.com/logrusorgru/aurora v2.0.3+incompatible // indirect github.com/logrusorgru/aurora/v4 v4.0.0 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/magiconair/properties v1.8.7 // indirect @@ -244,11 +241,11 @@ require ( github.com/multiformats/go-multistream v0.5.0 // indirect github.com/multiformats/go-varint v0.0.7 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect - github.com/onflow/atree v0.8.0-rc.6 // indirect - github.com/onflow/flow-ft/lib/go/contracts v1.0.0 // indirect - github.com/onflow/flow-ft/lib/go/templates v1.0.0 // indirect - github.com/onflow/flow-nft/lib/go/contracts v1.2.1 // indirect - github.com/onflow/flow-nft/lib/go/templates v1.2.0 // indirect + github.com/onflow/atree v0.8.0 // indirect + github.com/onflow/flow-ft/lib/go/contracts v1.0.1 // indirect + github.com/onflow/flow-ft/lib/go/templates v1.0.1 // indirect + github.com/onflow/flow-nft/lib/go/contracts v1.2.2 // indirect + github.com/onflow/flow-nft/lib/go/templates v1.2.1 // indirect github.com/onflow/sdks v0.6.0-preview.1 // indirect github.com/onflow/wal v1.0.2 // indirect github.com/onsi/ginkgo/v2 v2.13.2 // indirect @@ -266,14 +263,12 @@ require ( github.com/polydawn/refmt v0.89.0 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/prometheus/procfs v0.12.0 // indirect - github.com/psiemens/graceland v1.0.0 // indirect github.com/psiemens/sconfig v0.1.0 // indirect github.com/quic-go/qpack v0.4.0 // indirect github.com/quic-go/qtls-go1-20 v0.4.1 // indirect github.com/quic-go/quic-go v0.40.1 // indirect github.com/quic-go/webtransport-go v0.6.0 // indirect github.com/raulk/go-watchdog v1.3.0 // indirect - github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/rivo/uniseg v0.4.4 // indirect github.com/rogpeppe/go-internal v1.11.0 // indirect github.com/rootless-containers/rootlesskit v1.1.1 // indirect @@ -326,16 +321,15 @@ require ( go.opentelemetry.io/proto/otlp v1.0.0 // indirect go.uber.org/dig v1.17.1 // indirect go.uber.org/fx v1.20.1 // indirect - go.uber.org/mock v0.4.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.26.0 // indirect - golang.org/x/crypto v0.26.0 // indirect + golang.org/x/crypto v0.28.0 // indirect golang.org/x/mod v0.18.0 // indirect golang.org/x/net v0.26.0 // indirect golang.org/x/oauth2 v0.17.0 // indirect - golang.org/x/sys v0.23.0 // indirect - golang.org/x/term v0.23.0 // indirect - golang.org/x/text v0.17.0 // indirect + golang.org/x/sys v0.26.0 // indirect + golang.org/x/term v0.25.0 // indirect + golang.org/x/text v0.19.0 // indirect golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.22.0 // indirect golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect @@ -349,10 +343,6 @@ require ( gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect lukechampine.com/blake3 v1.3.0 // indirect - modernc.org/libc v1.37.6 // indirect - modernc.org/mathutil v1.6.0 // indirect - modernc.org/memory v1.7.2 // indirect - modernc.org/sqlite v1.28.0 // indirect rsc.io/tmplfunc v0.0.3 // indirect ) diff --git a/integration/go.sum b/integration/go.sum index 17fec9e6715..851b9d536cc 100644 --- a/integration/go.sum +++ b/integration/go.sum @@ -1,989 +1,39 @@ -cloud.google.com/go v0.0.0-20170206221025-ce650573d812/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.31.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.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.43.0/go.mod h1:BOSR3VbTLkk6FDC/TcffxP4NF/FFBGA5ku+jvKOP7pg= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.51.0/go.mod h1:hWtGJ6gnXH+KgDv+V0zFGDvpi07n3z8ZNj3T1RW0Gcw= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= -cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= -cloud.google.com/go 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 v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= -cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= -cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= -cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= -cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= -cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= -cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= -cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= -cloud.google.com/go v0.100.1/go.mod h1:fs4QogzfH5n2pBXBP9vRiU+eCny7lD2vmFZy79Iuw1U= -cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= -cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= -cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU= -cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA= -cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM= -cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I= -cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY= -cloud.google.com/go v0.110.2/go.mod h1:k04UEeEtb6ZBRTv3dZz4CeJC3jKGxyhl0sAiVVquxiw= -cloud.google.com/go v0.110.4/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI= -cloud.google.com/go v0.110.6/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI= -cloud.google.com/go v0.110.7/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI= -cloud.google.com/go v0.110.8/go.mod h1:Iz8AkXJf1qmxC3Oxoep8R1T36w8B92yU29PcBhHO5fk= cloud.google.com/go v0.112.0 h1:tpFCD7hpHFlQ8yPwT3x+QeXqc2T6+n6T+hmABHfDUSM= cloud.google.com/go v0.112.0/go.mod h1:3jEEVwZ/MHU4djK5t5RHuKOA/GbLddgTdVubX1qnPD4= -cloud.google.com/go/accessapproval v1.4.0/go.mod h1:zybIuC3KpDOvotz59lFe5qxRZx6C75OtwbisN56xYB4= -cloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw= -cloud.google.com/go/accessapproval v1.6.0/go.mod h1:R0EiYnwV5fsRFiKZkPHr6mwyk2wxUJ30nL4j2pcFY2E= -cloud.google.com/go/accessapproval v1.7.1/go.mod h1:JYczztsHRMK7NTXb6Xw+dwbs/WnOJxbo/2mTI+Kgg68= -cloud.google.com/go/accessapproval v1.7.2/go.mod h1:/gShiq9/kK/h8T/eEn1BTzalDvk0mZxJlhfw0p+Xuc0= -cloud.google.com/go/accesscontextmanager v1.3.0/go.mod h1:TgCBehyr5gNMz7ZaH9xubp+CE8dkrszb4oK9CWyvD4o= -cloud.google.com/go/accesscontextmanager v1.4.0/go.mod h1:/Kjh7BBu/Gh83sv+K60vN9QE5NJcd80sU33vIe2IFPE= -cloud.google.com/go/accesscontextmanager v1.6.0/go.mod h1:8XCvZWfYw3K/ji0iVnp+6pu7huxoQTLmxAbVjbloTtM= -cloud.google.com/go/accesscontextmanager v1.7.0/go.mod h1:CEGLewx8dwa33aDAZQujl7Dx+uYhS0eay198wB/VumQ= -cloud.google.com/go/accesscontextmanager v1.8.0/go.mod h1:uI+AI/r1oyWK99NN8cQ3UK76AMelMzgZCvJfsi2c+ps= -cloud.google.com/go/accesscontextmanager v1.8.1/go.mod h1:JFJHfvuaTC+++1iL1coPiG1eu5D24db2wXCDWDjIrxo= -cloud.google.com/go/accesscontextmanager v1.8.2/go.mod h1:E6/SCRM30elQJ2PKtFMs2YhfJpZSNcJyejhuzoId4Zk= -cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw= -cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY= -cloud.google.com/go/aiplatform v1.27.0/go.mod h1:Bvxqtl40l0WImSb04d0hXFU7gDOiq9jQmorivIiWcKg= -cloud.google.com/go/aiplatform v1.35.0/go.mod h1:7MFT/vCaOyZT/4IIFfxH4ErVg/4ku6lKv3w0+tFTgXQ= -cloud.google.com/go/aiplatform v1.36.1/go.mod h1:WTm12vJRPARNvJ+v6P52RDHCNe4AhvjcIZ/9/RRHy/k= -cloud.google.com/go/aiplatform v1.37.0/go.mod h1:IU2Cv29Lv9oCn/9LkFiiuKfwrRTq+QQMbW+hPCxJGZw= -cloud.google.com/go/aiplatform v1.45.0/go.mod h1:Iu2Q7sC7QGhXUeOhAj/oCK9a+ULz1O4AotZiqjQ8MYA= -cloud.google.com/go/aiplatform v1.48.0/go.mod h1:Iu2Q7sC7QGhXUeOhAj/oCK9a+ULz1O4AotZiqjQ8MYA= -cloud.google.com/go/aiplatform v1.50.0/go.mod h1:IRc2b8XAMTa9ZmfJV1BCCQbieWWvDnP1A8znyz5N7y4= -cloud.google.com/go/aiplatform v1.51.0/go.mod h1:IRc2b8XAMTa9ZmfJV1BCCQbieWWvDnP1A8znyz5N7y4= -cloud.google.com/go/aiplatform v1.51.1/go.mod h1:kY3nIMAVQOK2XDqDPHaOuD9e+FdMA6OOpfBjsvaFSOo= -cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI= -cloud.google.com/go/analytics v0.12.0/go.mod h1:gkfj9h6XRf9+TS4bmuhPEShsh3hH8PAZzm/41OOhQd4= -cloud.google.com/go/analytics v0.17.0/go.mod h1:WXFa3WSym4IZ+JiKmavYdJwGG/CvpqiqczmL59bTD9M= -cloud.google.com/go/analytics v0.18.0/go.mod h1:ZkeHGQlcIPkw0R/GW+boWHhCOR43xz9RN/jn7WcqfIE= -cloud.google.com/go/analytics v0.19.0/go.mod h1:k8liqf5/HCnOUkbawNtrWWc+UAzyDlW89doe8TtoDsE= -cloud.google.com/go/analytics v0.21.2/go.mod h1:U8dcUtmDmjrmUTnnnRnI4m6zKn/yaA5N9RlEkYFHpQo= -cloud.google.com/go/analytics v0.21.3/go.mod h1:U8dcUtmDmjrmUTnnnRnI4m6zKn/yaA5N9RlEkYFHpQo= -cloud.google.com/go/analytics v0.21.4/go.mod h1:zZgNCxLCy8b2rKKVfC1YkC2vTrpfZmeRCySM3aUbskA= -cloud.google.com/go/apigateway v1.3.0/go.mod h1:89Z8Bhpmxu6AmUxuVRg/ECRGReEdiP3vQtk4Z1J9rJk= -cloud.google.com/go/apigateway v1.4.0/go.mod h1:pHVY9MKGaH9PQ3pJ4YLzoj6U5FUDeDFBllIz7WmzJoc= -cloud.google.com/go/apigateway v1.5.0/go.mod h1:GpnZR3Q4rR7LVu5951qfXPJCHquZt02jf7xQx7kpqN8= -cloud.google.com/go/apigateway v1.6.1/go.mod h1:ufAS3wpbRjqfZrzpvLC2oh0MFlpRJm2E/ts25yyqmXA= -cloud.google.com/go/apigateway v1.6.2/go.mod h1:CwMC90nnZElorCW63P2pAYm25AtQrHfuOkbRSHj0bT8= -cloud.google.com/go/apigeeconnect v1.3.0/go.mod h1:G/AwXFAKo0gIXkPTVfZDd2qA1TxBXJ3MgMRBQkIi9jc= -cloud.google.com/go/apigeeconnect v1.4.0/go.mod h1:kV4NwOKqjvt2JYR0AoIWo2QGfoRtn/pkS3QlHp0Ni04= -cloud.google.com/go/apigeeconnect v1.5.0/go.mod h1:KFaCqvBRU6idyhSNyn3vlHXc8VMDJdRmwDF6JyFRqZ8= -cloud.google.com/go/apigeeconnect v1.6.1/go.mod h1:C4awq7x0JpLtrlQCr8AzVIzAaYgngRqWf9S5Uhg+wWs= -cloud.google.com/go/apigeeconnect v1.6.2/go.mod h1:s6O0CgXT9RgAxlq3DLXvG8riw8PYYbU/v25jqP3Dy18= -cloud.google.com/go/apigeeregistry v0.4.0/go.mod h1:EUG4PGcsZvxOXAdyEghIdXwAEi/4MEaoqLMLDMIwKXY= -cloud.google.com/go/apigeeregistry v0.5.0/go.mod h1:YR5+s0BVNZfVOUkMa5pAR2xGd0A473vA5M7j247o1wM= -cloud.google.com/go/apigeeregistry v0.6.0/go.mod h1:BFNzW7yQVLZ3yj0TKcwzb8n25CFBri51GVGOEUcgQsc= -cloud.google.com/go/apigeeregistry v0.7.1/go.mod h1:1XgyjZye4Mqtw7T9TsY4NW10U7BojBvG4RMD+vRDrIw= -cloud.google.com/go/apigeeregistry v0.7.2/go.mod h1:9CA2B2+TGsPKtfi3F7/1ncCCsL62NXBRfM6iPoGSM+8= -cloud.google.com/go/apikeys v0.4.0/go.mod h1:XATS/yqZbaBK0HOssf+ALHp8jAlNHUgyfprvNcBIszU= -cloud.google.com/go/apikeys v0.5.0/go.mod h1:5aQfwY4D+ewMMWScd3hm2en3hCj+BROlyrt3ytS7KLI= -cloud.google.com/go/apikeys v0.6.0/go.mod h1:kbpXu5upyiAlGkKrJgQl8A0rKNNJ7dQ377pdroRSSi8= -cloud.google.com/go/appengine v1.4.0/go.mod h1:CS2NhuBuDXM9f+qscZ6V86m1MIIqPj3WC/UoEuR1Sno= -cloud.google.com/go/appengine v1.5.0/go.mod h1:TfasSozdkFI0zeoxW3PTBLiNqRmzraodCWatWI9Dmak= -cloud.google.com/go/appengine v1.6.0/go.mod h1:hg6i0J/BD2cKmDJbaFSYHFyZkgBEfQrDg/X0V5fJn84= -cloud.google.com/go/appengine v1.7.0/go.mod h1:eZqpbHFCqRGa2aCdope7eC0SWLV1j0neb/QnMJVWx6A= -cloud.google.com/go/appengine v1.7.1/go.mod h1:IHLToyb/3fKutRysUlFO0BPt5j7RiQ45nrzEJmKTo6E= -cloud.google.com/go/appengine v1.8.1/go.mod h1:6NJXGLVhZCN9aQ/AEDvmfzKEfoYBlfB80/BHiKVputY= -cloud.google.com/go/appengine v1.8.2/go.mod h1:WMeJV9oZ51pvclqFN2PqHoGnys7rK0rz6s3Mp6yMvDo= -cloud.google.com/go/area120 v0.5.0/go.mod h1:DE/n4mp+iqVyvxHN41Vf1CR602GiHQjFPusMFW6bGR4= -cloud.google.com/go/area120 v0.6.0/go.mod h1:39yFJqWVgm0UZqWTOdqkLhjoC7uFfgXRC8g/ZegeAh0= -cloud.google.com/go/area120 v0.7.0/go.mod h1:a3+8EUD1SX5RUcCs3MY5YasiO1z6yLiNLRiFrykbynY= -cloud.google.com/go/area120 v0.7.1/go.mod h1:j84i4E1RboTWjKtZVWXPqvK5VHQFJRF2c1Nm69pWm9k= -cloud.google.com/go/area120 v0.8.1/go.mod h1:BVfZpGpB7KFVNxPiQBuHkX6Ed0rS51xIgmGyjrAfzsg= -cloud.google.com/go/area120 v0.8.2/go.mod h1:a5qfo+x77SRLXnCynFWPUZhnZGeSgvQ+Y0v1kSItkh4= -cloud.google.com/go/artifactregistry v1.6.0/go.mod h1:IYt0oBPSAGYj/kprzsBjZ/4LnG/zOcHyFHjWPCi6SAQ= -cloud.google.com/go/artifactregistry v1.7.0/go.mod h1:mqTOFOnGZx8EtSqK/ZWcsm/4U8B77rbcLP6ruDU2Ixk= -cloud.google.com/go/artifactregistry v1.8.0/go.mod h1:w3GQXkJX8hiKN0v+at4b0qotwijQbYUqF2GWkZzAhC0= -cloud.google.com/go/artifactregistry v1.9.0/go.mod h1:2K2RqvA2CYvAeARHRkLDhMDJ3OXy26h3XW+3/Jh2uYc= -cloud.google.com/go/artifactregistry v1.11.1/go.mod h1:lLYghw+Itq9SONbCa1YWBoWs1nOucMH0pwXN1rOBZFI= -cloud.google.com/go/artifactregistry v1.11.2/go.mod h1:nLZns771ZGAwVLzTX/7Al6R9ehma4WUEhZGWV6CeQNQ= -cloud.google.com/go/artifactregistry v1.12.0/go.mod h1:o6P3MIvtzTOnmvGagO9v/rOjjA0HmhJ+/6KAXrmYDCI= -cloud.google.com/go/artifactregistry v1.13.0/go.mod h1:uy/LNfoOIivepGhooAUpL1i30Hgee3Cu0l4VTWHUC08= -cloud.google.com/go/artifactregistry v1.14.1/go.mod h1:nxVdG19jTaSTu7yA7+VbWL346r3rIdkZ142BSQqhn5E= -cloud.google.com/go/artifactregistry v1.14.2/go.mod h1:Xk+QbsKEb0ElmyeMfdHAey41B+qBq3q5R5f5xD4XT3U= -cloud.google.com/go/artifactregistry v1.14.3/go.mod h1:A2/E9GXnsyXl7GUvQ/2CjHA+mVRoWAXC0brg2os+kNI= -cloud.google.com/go/asset v1.5.0/go.mod h1:5mfs8UvcM5wHhqtSv8J1CtxxaQq3AdBxxQi2jGW/K4o= -cloud.google.com/go/asset v1.7.0/go.mod h1:YbENsRK4+xTiL+Ofoj5Ckf+O17kJtgp3Y3nn4uzZz5s= -cloud.google.com/go/asset v1.8.0/go.mod h1:mUNGKhiqIdbr8X7KNayoYvyc4HbbFO9URsjbytpUaW0= -cloud.google.com/go/asset v1.9.0/go.mod h1:83MOE6jEJBMqFKadM9NLRcs80Gdw76qGuHn8m3h8oHQ= -cloud.google.com/go/asset v1.10.0/go.mod h1:pLz7uokL80qKhzKr4xXGvBQXnzHn5evJAEAtZiIb0wY= -cloud.google.com/go/asset v1.11.1/go.mod h1:fSwLhbRvC9p9CXQHJ3BgFeQNM4c9x10lqlrdEUYXlJo= -cloud.google.com/go/asset v1.12.0/go.mod h1:h9/sFOa4eDIyKmH6QMpm4eUK3pDojWnUhTgJlk762Hg= -cloud.google.com/go/asset v1.13.0/go.mod h1:WQAMyYek/b7NBpYq/K4KJWcRqzoalEsxz/t/dTk4THw= -cloud.google.com/go/asset v1.14.1/go.mod h1:4bEJ3dnHCqWCDbWJ/6Vn7GVI9LerSi7Rfdi03hd+WTQ= -cloud.google.com/go/asset v1.15.0/go.mod h1:tpKafV6mEut3+vN9ScGvCHXHj7FALFVta+okxFECHcg= -cloud.google.com/go/asset v1.15.1/go.mod h1:yX/amTvFWRpp5rcFq6XbCxzKT8RJUam1UoboE179jU4= -cloud.google.com/go/assuredworkloads v1.5.0/go.mod h1:n8HOZ6pff6re5KYfBXcFvSViQjDwxFkAkmUFffJRbbY= -cloud.google.com/go/assuredworkloads v1.6.0/go.mod h1:yo2YOk37Yc89Rsd5QMVECvjaMKymF9OP+QXWlKXUkXw= -cloud.google.com/go/assuredworkloads v1.7.0/go.mod h1:z/736/oNmtGAyU47reJgGN+KVoYoxeLBoj4XkKYscNI= -cloud.google.com/go/assuredworkloads v1.8.0/go.mod h1:AsX2cqyNCOvEQC8RMPnoc0yEarXQk6WEKkxYfL6kGIo= -cloud.google.com/go/assuredworkloads v1.9.0/go.mod h1:kFuI1P78bplYtT77Tb1hi0FMxM0vVpRC7VVoJC3ZoT0= -cloud.google.com/go/assuredworkloads v1.10.0/go.mod h1:kwdUQuXcedVdsIaKgKTp9t0UJkE5+PAVNhdQm4ZVq2E= -cloud.google.com/go/assuredworkloads v1.11.1/go.mod h1:+F04I52Pgn5nmPG36CWFtxmav6+7Q+c5QyJoL18Lry0= -cloud.google.com/go/assuredworkloads v1.11.2/go.mod h1:O1dfr+oZJMlE6mw0Bp0P1KZSlj5SghMBvTpZqIcUAW4= -cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0= -cloud.google.com/go/automl v1.6.0/go.mod h1:ugf8a6Fx+zP0D59WLhqgTDsQI9w07o64uf/Is3Nh5p8= -cloud.google.com/go/automl v1.7.0/go.mod h1:RL9MYCCsJEOmt0Wf3z9uzG0a7adTT1fe+aObgSpkCt8= -cloud.google.com/go/automl v1.8.0/go.mod h1:xWx7G/aPEe/NP+qzYXktoBSDfjO+vnKMGgsApGJJquM= -cloud.google.com/go/automl v1.12.0/go.mod h1:tWDcHDp86aMIuHmyvjuKeeHEGq76lD7ZqfGLN6B0NuU= -cloud.google.com/go/automl v1.13.1/go.mod h1:1aowgAHWYZU27MybSCFiukPO7xnyawv7pt3zK4bheQE= -cloud.google.com/go/automl v1.13.2/go.mod h1:gNY/fUmDEN40sP8amAX3MaXkxcqPIn7F1UIIPZpy4Mg= -cloud.google.com/go/baremetalsolution v0.3.0/go.mod h1:XOrocE+pvK1xFfleEnShBlNAXf+j5blPPxrhjKgnIFc= -cloud.google.com/go/baremetalsolution v0.4.0/go.mod h1:BymplhAadOO/eBa7KewQ0Ppg4A4Wplbn+PsFKRLo0uI= -cloud.google.com/go/baremetalsolution v0.5.0/go.mod h1:dXGxEkmR9BMwxhzBhV0AioD0ULBmuLZI8CdwalUxuss= -cloud.google.com/go/baremetalsolution v1.1.1/go.mod h1:D1AV6xwOksJMV4OSlWHtWuFNZZYujJknMAP4Qa27QIA= -cloud.google.com/go/baremetalsolution v1.2.0/go.mod h1:68wi9AwPYkEWIUT4SvSGS9UJwKzNpshjHsH4lzk8iOw= -cloud.google.com/go/baremetalsolution v1.2.1/go.mod h1:3qKpKIw12RPXStwQXcbhfxVj1dqQGEvcmA+SX/mUR88= -cloud.google.com/go/batch v0.3.0/go.mod h1:TR18ZoAekj1GuirsUsR1ZTKN3FC/4UDnScjT8NXImFE= -cloud.google.com/go/batch v0.4.0/go.mod h1:WZkHnP43R/QCGQsZ+0JyG4i79ranE2u8xvjq/9+STPE= -cloud.google.com/go/batch v0.7.0/go.mod h1:vLZN95s6teRUqRQ4s3RLDsH8PvboqBK+rn1oevL159g= -cloud.google.com/go/batch v1.3.1/go.mod h1:VguXeQKXIYaeeIYbuozUmBR13AfL4SJP7IltNPS+A4A= -cloud.google.com/go/batch v1.4.1/go.mod h1:KdBmDD61K0ovcxoRHGrN6GmOBWeAOyCgKD0Mugx4Fkk= -cloud.google.com/go/batch v1.5.0/go.mod h1:KdBmDD61K0ovcxoRHGrN6GmOBWeAOyCgKD0Mugx4Fkk= -cloud.google.com/go/batch v1.5.1/go.mod h1:RpBuIYLkQu8+CWDk3dFD/t/jOCGuUpkpX+Y0n1Xccs8= -cloud.google.com/go/beyondcorp v0.2.0/go.mod h1:TB7Bd+EEtcw9PCPQhCJtJGjk/7TC6ckmnSFS+xwTfm4= -cloud.google.com/go/beyondcorp v0.3.0/go.mod h1:E5U5lcrcXMsCuoDNyGrpyTm/hn7ne941Jz2vmksAxW8= -cloud.google.com/go/beyondcorp v0.4.0/go.mod h1:3ApA0mbhHx6YImmuubf5pyW8srKnCEPON32/5hj+RmM= -cloud.google.com/go/beyondcorp v0.5.0/go.mod h1:uFqj9X+dSfrheVp7ssLTaRHd2EHqSL4QZmH4e8WXGGU= -cloud.google.com/go/beyondcorp v0.6.1/go.mod h1:YhxDWw946SCbmcWo3fAhw3V4XZMSpQ/VYfcKGAEU8/4= -cloud.google.com/go/beyondcorp v1.0.0/go.mod h1:YhxDWw946SCbmcWo3fAhw3V4XZMSpQ/VYfcKGAEU8/4= -cloud.google.com/go/beyondcorp v1.0.1/go.mod h1:zl/rWWAFVeV+kx+X2Javly7o1EIQThU4WlkynffL/lk= -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/bigquery v1.42.0/go.mod h1:8dRTJxhtG+vwBKzE5OseQn/hiydoQN3EedCaOdYmxRA= -cloud.google.com/go/bigquery v1.43.0/go.mod h1:ZMQcXHsl+xmU1z36G2jNGZmKp9zNY5BUua5wDgmNCfw= -cloud.google.com/go/bigquery v1.44.0/go.mod h1:0Y33VqXTEsbamHJvJHdFmtqHvMIY28aK1+dFsvaChGc= -cloud.google.com/go/bigquery v1.47.0/go.mod h1:sA9XOgy0A8vQK9+MWhEQTY6Tix87M/ZurWFIxmF9I/E= -cloud.google.com/go/bigquery v1.48.0/go.mod h1:QAwSz+ipNgfL5jxiaK7weyOhzdoAy1zFm0Nf1fysJac= -cloud.google.com/go/bigquery v1.49.0/go.mod h1:Sv8hMmTFFYBlt/ftw2uN6dFdQPzBlREY9yBh7Oy7/4Q= -cloud.google.com/go/bigquery v1.50.0/go.mod h1:YrleYEh2pSEbgTBZYMJ5SuSr0ML3ypjRB1zgf7pvQLU= -cloud.google.com/go/bigquery v1.52.0/go.mod h1:3b/iXjRQGU4nKa87cXeg6/gogLjO8C6PmuM8i5Bi/u4= -cloud.google.com/go/bigquery v1.53.0/go.mod h1:3b/iXjRQGU4nKa87cXeg6/gogLjO8C6PmuM8i5Bi/u4= -cloud.google.com/go/bigquery v1.55.0/go.mod h1:9Y5I3PN9kQWuid6183JFhOGOW3GcirA5LpsKCUn+2ec= -cloud.google.com/go/bigquery v1.56.0/go.mod h1:KDcsploXTEY7XT3fDQzMUZlpQLHzE4itubHrnmhUrZA= cloud.google.com/go/bigquery v1.59.1 h1:CpT+/njKuKT3CEmswm6IbhNu9u35zt5dO4yPDLW+nG4= cloud.google.com/go/bigquery v1.59.1/go.mod h1:VP1UJYgevyTwsV7desjzNzDND5p6hZB+Z8gZJN1GQUc= -cloud.google.com/go/bigtable v1.2.0/go.mod h1:JcVAOl45lrTmQfLj7T6TxyMzIN/3FGGcFm+2xVAli2o= -cloud.google.com/go/billing v1.4.0/go.mod h1:g9IdKBEFlItS8bTtlrZdVLWSSdSyFUZKXNS02zKMOZY= -cloud.google.com/go/billing v1.5.0/go.mod h1:mztb1tBc3QekhjSgmpf/CV4LzWXLzCArwpLmP2Gm88s= -cloud.google.com/go/billing v1.6.0/go.mod h1:WoXzguj+BeHXPbKfNWkqVtDdzORazmCjraY+vrxcyvI= -cloud.google.com/go/billing v1.7.0/go.mod h1:q457N3Hbj9lYwwRbnlD7vUpyjq6u5U1RAOArInEiD5Y= -cloud.google.com/go/billing v1.12.0/go.mod h1:yKrZio/eu+okO/2McZEbch17O5CB5NpZhhXG6Z766ss= -cloud.google.com/go/billing v1.13.0/go.mod h1:7kB2W9Xf98hP9Sr12KfECgfGclsH3CQR0R08tnRlRbc= -cloud.google.com/go/billing v1.16.0/go.mod h1:y8vx09JSSJG02k5QxbycNRrN7FGZB6F3CAcgum7jvGA= -cloud.google.com/go/billing v1.17.0/go.mod h1:Z9+vZXEq+HwH7bhJkyI4OQcR6TSbeMrjlpEjO2vzY64= -cloud.google.com/go/billing v1.17.1/go.mod h1:Z9+vZXEq+HwH7bhJkyI4OQcR6TSbeMrjlpEjO2vzY64= -cloud.google.com/go/billing v1.17.2/go.mod h1:u/AdV/3wr3xoRBk5xvUzYMS1IawOAPwQMuHgHMdljDg= -cloud.google.com/go/binaryauthorization v1.1.0/go.mod h1:xwnoWu3Y84jbuHa0zd526MJYmtnVXn0syOjaJgy4+dM= -cloud.google.com/go/binaryauthorization v1.2.0/go.mod h1:86WKkJHtRcv5ViNABtYMhhNWRrD1Vpi//uKEy7aYEfI= -cloud.google.com/go/binaryauthorization v1.3.0/go.mod h1:lRZbKgjDIIQvzYQS1p99A7/U1JqvqeZg0wiI5tp6tg0= -cloud.google.com/go/binaryauthorization v1.4.0/go.mod h1:tsSPQrBd77VLplV70GUhBf/Zm3FsKmgSqgm4UmiDItk= -cloud.google.com/go/binaryauthorization v1.5.0/go.mod h1:OSe4OU1nN/VswXKRBmciKpo9LulY41gch5c68htf3/Q= -cloud.google.com/go/binaryauthorization v1.6.1/go.mod h1:TKt4pa8xhowwffiBmbrbcxijJRZED4zrqnwZ1lKH51U= -cloud.google.com/go/binaryauthorization v1.7.0/go.mod h1:Zn+S6QqTMn6odcMU1zDZCJxPjU2tZPV1oDl45lWY154= -cloud.google.com/go/binaryauthorization v1.7.1/go.mod h1:GTAyfRWYgcbsP3NJogpV3yeunbUIjx2T9xVeYovtURE= -cloud.google.com/go/certificatemanager v1.3.0/go.mod h1:n6twGDvcUBFu9uBgt4eYvvf3sQ6My8jADcOVwHmzadg= -cloud.google.com/go/certificatemanager v1.4.0/go.mod h1:vowpercVFyqs8ABSmrdV+GiFf2H/ch3KyudYQEMM590= -cloud.google.com/go/certificatemanager v1.6.0/go.mod h1:3Hh64rCKjRAX8dXgRAyOcY5vQ/fE1sh8o+Mdd6KPgY8= -cloud.google.com/go/certificatemanager v1.7.1/go.mod h1:iW8J3nG6SaRYImIa+wXQ0g8IgoofDFRp5UMzaNk1UqI= -cloud.google.com/go/certificatemanager v1.7.2/go.mod h1:15SYTDQMd00kdoW0+XY5d9e+JbOPjp24AvF48D8BbcQ= -cloud.google.com/go/channel v1.8.0/go.mod h1:W5SwCXDJsq/rg3tn3oG0LOxpAo6IMxNa09ngphpSlnk= -cloud.google.com/go/channel v1.9.0/go.mod h1:jcu05W0my9Vx4mt3/rEHpfxc9eKi9XwsdDL8yBMbKUk= -cloud.google.com/go/channel v1.11.0/go.mod h1:IdtI0uWGqhEeatSB62VOoJ8FSUhJ9/+iGkJVqp74CGE= -cloud.google.com/go/channel v1.12.0/go.mod h1:VkxCGKASi4Cq7TbXxlaBezonAYpp1GCnKMY6tnMQnLU= -cloud.google.com/go/channel v1.16.0/go.mod h1:eN/q1PFSl5gyu0dYdmxNXscY/4Fi7ABmeHCJNf/oHmc= -cloud.google.com/go/channel v1.17.0/go.mod h1:RpbhJsGi/lXWAUM1eF4IbQGbsfVlg2o8Iiy2/YLfVT0= -cloud.google.com/go/channel v1.17.1/go.mod h1:xqfzcOZAcP4b/hUDH0GkGg1Sd5to6di1HOJn/pi5uBQ= -cloud.google.com/go/cloudbuild v1.3.0/go.mod h1:WequR4ULxlqvMsjDEEEFnOG5ZSRSgWOywXYDb1vPE6U= -cloud.google.com/go/cloudbuild v1.4.0/go.mod h1:5Qwa40LHiOXmz3386FrjrYM93rM/hdRr7b53sySrTqA= -cloud.google.com/go/cloudbuild v1.6.0/go.mod h1:UIbc/w9QCbH12xX+ezUsgblrWv+Cv4Tw83GiSMHOn9M= -cloud.google.com/go/cloudbuild v1.7.0/go.mod h1:zb5tWh2XI6lR9zQmsm1VRA+7OCuve5d8S+zJUul8KTg= -cloud.google.com/go/cloudbuild v1.9.0/go.mod h1:qK1d7s4QlO0VwfYn5YuClDGg2hfmLZEb4wQGAbIgL1s= -cloud.google.com/go/cloudbuild v1.10.1/go.mod h1:lyJg7v97SUIPq4RC2sGsz/9tNczhyv2AjML/ci4ulzU= -cloud.google.com/go/cloudbuild v1.13.0/go.mod h1:lyJg7v97SUIPq4RC2sGsz/9tNczhyv2AjML/ci4ulzU= -cloud.google.com/go/cloudbuild v1.14.0/go.mod h1:lyJg7v97SUIPq4RC2sGsz/9tNczhyv2AjML/ci4ulzU= -cloud.google.com/go/cloudbuild v1.14.1/go.mod h1:K7wGc/3zfvmYWOWwYTgF/d/UVJhS4pu+HAy7PL7mCsU= -cloud.google.com/go/clouddms v1.3.0/go.mod h1:oK6XsCDdW4Ib3jCCBugx+gVjevp2TMXFtgxvPSee3OM= -cloud.google.com/go/clouddms v1.4.0/go.mod h1:Eh7sUGCC+aKry14O1NRljhjyrr0NFC0G2cjwX0cByRk= -cloud.google.com/go/clouddms v1.5.0/go.mod h1:QSxQnhikCLUw13iAbffF2CZxAER3xDGNHjsTAkQJcQA= -cloud.google.com/go/clouddms v1.6.1/go.mod h1:Ygo1vL52Ov4TBZQquhz5fiw2CQ58gvu+PlS6PVXCpZI= -cloud.google.com/go/clouddms v1.7.0/go.mod h1:MW1dC6SOtI/tPNCciTsXtsGNEM0i0OccykPvv3hiYeM= -cloud.google.com/go/clouddms v1.7.1/go.mod h1:o4SR8U95+P7gZ/TX+YbJxehOCsM+fe6/brlrFquiszk= -cloud.google.com/go/cloudtasks v1.5.0/go.mod h1:fD92REy1x5woxkKEkLdvavGnPJGEn8Uic9nWuLzqCpY= -cloud.google.com/go/cloudtasks v1.6.0/go.mod h1:C6Io+sxuke9/KNRkbQpihnW93SWDU3uXt92nu85HkYI= -cloud.google.com/go/cloudtasks v1.7.0/go.mod h1:ImsfdYWwlWNJbdgPIIGJWC+gemEGTBK/SunNQQNCAb4= -cloud.google.com/go/cloudtasks v1.8.0/go.mod h1:gQXUIwCSOI4yPVK7DgTVFiiP0ZW/eQkydWzwVMdHxrI= -cloud.google.com/go/cloudtasks v1.9.0/go.mod h1:w+EyLsVkLWHcOaqNEyvcKAsWp9p29dL6uL9Nst1cI7Y= -cloud.google.com/go/cloudtasks v1.10.0/go.mod h1:NDSoTLkZ3+vExFEWu2UJV1arUyzVDAiZtdWcsUyNwBs= -cloud.google.com/go/cloudtasks v1.11.1/go.mod h1:a9udmnou9KO2iulGscKR0qBYjreuX8oHwpmFsKspEvM= -cloud.google.com/go/cloudtasks v1.12.1/go.mod h1:a9udmnou9KO2iulGscKR0qBYjreuX8oHwpmFsKspEvM= -cloud.google.com/go/cloudtasks v1.12.2/go.mod h1:A7nYkjNlW2gUoROg1kvJrQGhJP/38UaWwsnuBDOBVUk= -cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= -cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= -cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= -cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= -cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= -cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= -cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU= -cloud.google.com/go/compute v1.12.0/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= -cloud.google.com/go/compute v1.12.1/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= -cloud.google.com/go/compute v1.13.0/go.mod h1:5aPTS0cUNMIc1CE546K+Th6weJUNQErARyZtRXDJ8GE= -cloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvjxega5vAdo= -cloud.google.com/go/compute v1.15.1/go.mod h1:bjjoF/NtFUrkD/urWfdHaKuOPDR5nWIs63rR+SXhcpA= -cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs= -cloud.google.com/go/compute v1.19.0/go.mod h1:rikpw2y+UMidAe9tISo04EHNOIf42RLYF/q8Bs93scU= -cloud.google.com/go/compute v1.19.1/go.mod h1:6ylj3a05WF8leseCdIf77NK0g1ey+nj5IKd5/kvShxE= -cloud.google.com/go/compute v1.19.3/go.mod h1:qxvISKp/gYnXkSAD1ppcSOveRAmzxicEv/JlizULFrI= -cloud.google.com/go/compute v1.20.1/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= -cloud.google.com/go/compute v1.21.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= -cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= -cloud.google.com/go/compute v1.23.1/go.mod h1:CqB3xpmPKKt3OJpW2ndFIXnA9A4xAy/F3Xp1ixncW78= cloud.google.com/go/compute v1.24.0 h1:phWcR2eWzRJaL/kOiJwfFsPs4BaKq1j6vnpZrc1YlVg= cloud.google.com/go/compute v1.24.0/go.mod h1:kw1/T+h/+tK2LJK0wiPPx1intgdAM3j/g3hFDlscY40= -cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU= -cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= -cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= -cloud.google.com/go/contactcenterinsights v1.3.0/go.mod h1:Eu2oemoePuEFc/xKFPjbTuPSj0fYJcPls9TFlPNnHHY= -cloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck= -cloud.google.com/go/contactcenterinsights v1.6.0/go.mod h1:IIDlT6CLcDoyv79kDv8iWxMSTZhLxSCofVV5W6YFM/w= -cloud.google.com/go/contactcenterinsights v1.9.1/go.mod h1:bsg/R7zGLYMVxFFzfh9ooLTruLRCG9fnzhH9KznHhbM= -cloud.google.com/go/contactcenterinsights v1.10.0/go.mod h1:bsg/R7zGLYMVxFFzfh9ooLTruLRCG9fnzhH9KznHhbM= -cloud.google.com/go/contactcenterinsights v1.11.0/go.mod h1:hutBdImE4XNZ1NV4vbPJKSFOnQruhC5Lj9bZqWMTKiU= -cloud.google.com/go/contactcenterinsights v1.11.1/go.mod h1:FeNP3Kg8iteKM80lMwSk3zZZKVxr+PGnAId6soKuXwE= -cloud.google.com/go/container v1.6.0/go.mod h1:Xazp7GjJSeUYo688S+6J5V+n/t+G5sKBTFkKNudGRxg= -cloud.google.com/go/container v1.7.0/go.mod h1:Dp5AHtmothHGX3DwwIHPgq45Y8KmNsgN3amoYfxVkLo= -cloud.google.com/go/container v1.13.1/go.mod h1:6wgbMPeQRw9rSnKBCAJXnds3Pzj03C4JHamr8asWKy4= -cloud.google.com/go/container v1.14.0/go.mod h1:3AoJMPhHfLDxLvrlVWaK57IXzaPnLaZq63WX59aQBfM= -cloud.google.com/go/container v1.15.0/go.mod h1:ft+9S0WGjAyjDggg5S06DXj+fHJICWg8L7isCQe9pQA= -cloud.google.com/go/container v1.22.1/go.mod h1:lTNExE2R7f+DLbAN+rJiKTisauFCaoDq6NURZ83eVH4= -cloud.google.com/go/container v1.24.0/go.mod h1:lTNExE2R7f+DLbAN+rJiKTisauFCaoDq6NURZ83eVH4= -cloud.google.com/go/container v1.26.0/go.mod h1:YJCmRet6+6jnYYRS000T6k0D0xUXQgBSaJ7VwI8FBj4= -cloud.google.com/go/container v1.26.1/go.mod h1:5smONjPRUxeEpDG7bMKWfDL4sauswqEtnBK1/KKpR04= -cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I= -cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4= -cloud.google.com/go/containeranalysis v0.7.0/go.mod h1:9aUL+/vZ55P2CXfuZjS4UjQ9AgXoSw8Ts6lemfmxBxI= -cloud.google.com/go/containeranalysis v0.9.0/go.mod h1:orbOANbwk5Ejoom+s+DUCTTJ7IBdBQJDcSylAx/on9s= -cloud.google.com/go/containeranalysis v0.10.1/go.mod h1:Ya2jiILITMY68ZLPaogjmOMNkwsDrWBSTyBubGXO7j0= -cloud.google.com/go/containeranalysis v0.11.0/go.mod h1:4n2e99ZwpGxpNcz+YsFT1dfOHPQFGcAC8FN2M2/ne/U= -cloud.google.com/go/containeranalysis v0.11.1/go.mod h1:rYlUOM7nem1OJMKwE1SadufX0JP3wnXj844EtZAwWLY= -cloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSiyOzm8kpLq0a0= -cloud.google.com/go/datacatalog v1.5.0/go.mod h1:M7GPLNQeLfWqeIm3iuiruhPzkt65+Bx8dAKvScX8jvs= -cloud.google.com/go/datacatalog v1.6.0/go.mod h1:+aEyF8JKg+uXcIdAmmaMUmZ3q1b/lKLtXCmXdnc0lbc= -cloud.google.com/go/datacatalog v1.7.0/go.mod h1:9mEl4AuDYWw81UGc41HonIHH7/sn52H0/tc8f8ZbZIE= -cloud.google.com/go/datacatalog v1.8.0/go.mod h1:KYuoVOv9BM8EYz/4eMFxrr4DUKhGIOXxZoKYF5wdISM= -cloud.google.com/go/datacatalog v1.8.1/go.mod h1:RJ58z4rMp3gvETA465Vg+ag8BGgBdnRPEMMSTr5Uv+M= -cloud.google.com/go/datacatalog v1.12.0/go.mod h1:CWae8rFkfp6LzLumKOnmVh4+Zle4A3NXLzVJ1d1mRm0= -cloud.google.com/go/datacatalog v1.13.0/go.mod h1:E4Rj9a5ZtAxcQJlEBTLgMTphfP11/lNaAshpoBgemX8= -cloud.google.com/go/datacatalog v1.14.0/go.mod h1:h0PrGtlihoutNMp/uvwhawLQ9+c63Kz65UFqh49Yo+E= -cloud.google.com/go/datacatalog v1.14.1/go.mod h1:d2CevwTG4yedZilwe+v3E3ZBDRMobQfSG/a6cCCN5R4= -cloud.google.com/go/datacatalog v1.16.0/go.mod h1:d2CevwTG4yedZilwe+v3E3ZBDRMobQfSG/a6cCCN5R4= -cloud.google.com/go/datacatalog v1.17.1/go.mod h1:nCSYFHgtxh2MiEktWIz71s/X+7ds/UT9kp0PC7waCzE= -cloud.google.com/go/datacatalog v1.18.0/go.mod h1:nCSYFHgtxh2MiEktWIz71s/X+7ds/UT9kp0PC7waCzE= -cloud.google.com/go/datacatalog v1.18.1/go.mod h1:TzAWaz+ON1tkNr4MOcak8EBHX7wIRX/gZKM+yTVsv+A= cloud.google.com/go/datacatalog v1.19.3 h1:A0vKYCQdxQuV4Pi0LL9p39Vwvg4jH5yYveMv50gU5Tw= cloud.google.com/go/datacatalog v1.19.3/go.mod h1:ra8V3UAsciBpJKQ+z9Whkxzxv7jmQg1hfODr3N3YPJ4= -cloud.google.com/go/dataflow v0.6.0/go.mod h1:9QwV89cGoxjjSR9/r7eFDqqjtvbKxAK2BaYU6PVk9UM= -cloud.google.com/go/dataflow v0.7.0/go.mod h1:PX526vb4ijFMesO1o202EaUmouZKBpjHsTlCtB4parQ= -cloud.google.com/go/dataflow v0.8.0/go.mod h1:Rcf5YgTKPtQyYz8bLYhFoIV/vP39eL7fWNcSOyFfLJE= -cloud.google.com/go/dataflow v0.9.1/go.mod h1:Wp7s32QjYuQDWqJPFFlnBKhkAtiFpMTdg00qGbnIHVw= -cloud.google.com/go/dataflow v0.9.2/go.mod h1:vBfdBZ/ejlTaYIGB3zB4T08UshH70vbtZeMD+urnUSo= -cloud.google.com/go/dataform v0.3.0/go.mod h1:cj8uNliRlHpa6L3yVhDOBrUXH+BPAO1+KFMQQNSThKo= -cloud.google.com/go/dataform v0.4.0/go.mod h1:fwV6Y4Ty2yIFL89huYlEkwUPtS7YZinZbzzj5S9FzCE= -cloud.google.com/go/dataform v0.5.0/go.mod h1:GFUYRe8IBa2hcomWplodVmUx/iTL0FrsauObOM3Ipr0= -cloud.google.com/go/dataform v0.6.0/go.mod h1:QPflImQy33e29VuapFdf19oPbE4aYTJxr31OAPV+ulA= -cloud.google.com/go/dataform v0.7.0/go.mod h1:7NulqnVozfHvWUBpMDfKMUESr+85aJsC/2O0o3jWPDE= -cloud.google.com/go/dataform v0.8.1/go.mod h1:3BhPSiw8xmppbgzeBbmDvmSWlwouuJkXsXsb8UBih9M= -cloud.google.com/go/dataform v0.8.2/go.mod h1:X9RIqDs6NbGPLR80tnYoPNiO1w0wenKTb8PxxlhTMKM= -cloud.google.com/go/datafusion v1.4.0/go.mod h1:1Zb6VN+W6ALo85cXnM1IKiPw+yQMKMhB9TsTSRDo/38= -cloud.google.com/go/datafusion v1.5.0/go.mod h1:Kz+l1FGHB0J+4XF2fud96WMmRiq/wj8N9u007vyXZ2w= -cloud.google.com/go/datafusion v1.6.0/go.mod h1:WBsMF8F1RhSXvVM8rCV3AeyWVxcC2xY6vith3iw3S+8= -cloud.google.com/go/datafusion v1.7.1/go.mod h1:KpoTBbFmoToDExJUso/fcCiguGDk7MEzOWXUsJo0wsI= -cloud.google.com/go/datafusion v1.7.2/go.mod h1:62K2NEC6DRlpNmI43WHMWf9Vg/YvN6QVi8EVwifElI0= -cloud.google.com/go/datalabeling v0.5.0/go.mod h1:TGcJ0G2NzcsXSE/97yWjIZO0bXj0KbVlINXMG9ud42I= -cloud.google.com/go/datalabeling v0.6.0/go.mod h1:WqdISuk/+WIGeMkpw/1q7bK/tFEZxsrFJOJdY2bXvTQ= -cloud.google.com/go/datalabeling v0.7.0/go.mod h1:WPQb1y08RJbmpM3ww0CSUAGweL0SxByuW2E+FU+wXcM= -cloud.google.com/go/datalabeling v0.8.1/go.mod h1:XS62LBSVPbYR54GfYQsPXZjTW8UxCK2fkDciSrpRFdY= -cloud.google.com/go/datalabeling v0.8.2/go.mod h1:cyDvGHuJWu9U/cLDA7d8sb9a0tWLEletStu2sTmg3BE= -cloud.google.com/go/dataplex v1.3.0/go.mod h1:hQuRtDg+fCiFgC8j0zV222HvzFQdRd+SVX8gdmFcZzA= -cloud.google.com/go/dataplex v1.4.0/go.mod h1:X51GfLXEMVJ6UN47ESVqvlsRplbLhcsAt0kZCCKsU0A= -cloud.google.com/go/dataplex v1.5.2/go.mod h1:cVMgQHsmfRoI5KFYq4JtIBEUbYwc3c7tXmIDhRmNNVQ= -cloud.google.com/go/dataplex v1.6.0/go.mod h1:bMsomC/aEJOSpHXdFKFGQ1b0TDPIeL28nJObeO1ppRs= -cloud.google.com/go/dataplex v1.8.1/go.mod h1:7TyrDT6BCdI8/38Uvp0/ZxBslOslP2X2MPDucliyvSE= -cloud.google.com/go/dataplex v1.9.0/go.mod h1:7TyrDT6BCdI8/38Uvp0/ZxBslOslP2X2MPDucliyvSE= -cloud.google.com/go/dataplex v1.9.1/go.mod h1:7TyrDT6BCdI8/38Uvp0/ZxBslOslP2X2MPDucliyvSE= -cloud.google.com/go/dataplex v1.10.1/go.mod h1:1MzmBv8FvjYfc7vDdxhnLFNskikkB+3vl475/XdCDhs= -cloud.google.com/go/dataproc v1.7.0/go.mod h1:CKAlMjII9H90RXaMpSxQ8EU6dQx6iAYNPcYPOkSbi8s= -cloud.google.com/go/dataproc v1.8.0/go.mod h1:5OW+zNAH0pMpw14JVrPONsxMQYMBqJuzORhIBfBn9uI= -cloud.google.com/go/dataproc v1.12.0/go.mod h1:zrF3aX0uV3ikkMz6z4uBbIKyhRITnxvr4i3IjKsKrw4= -cloud.google.com/go/dataproc/v2 v2.0.1/go.mod h1:7Ez3KRHdFGcfY7GcevBbvozX+zyWGcwLJvvAMwCaoZ4= -cloud.google.com/go/dataproc/v2 v2.2.0/go.mod h1:lZR7AQtwZPvmINx5J87DSOOpTfof9LVZju6/Qo4lmcY= -cloud.google.com/go/dataproc/v2 v2.2.1/go.mod h1:QdAJLaBjh+l4PVlVZcmrmhGccosY/omC1qwfQ61Zv/o= -cloud.google.com/go/dataqna v0.5.0/go.mod h1:90Hyk596ft3zUQ8NkFfvICSIfHFh1Bc7C4cK3vbhkeo= -cloud.google.com/go/dataqna v0.6.0/go.mod h1:1lqNpM7rqNLVgWBJyk5NF6Uen2PHym0jtVJonplVsDA= -cloud.google.com/go/dataqna v0.7.0/go.mod h1:Lx9OcIIeqCrw1a6KdO3/5KMP1wAmTc0slZWwP12Qq3c= -cloud.google.com/go/dataqna v0.8.1/go.mod h1:zxZM0Bl6liMePWsHA8RMGAfmTG34vJMapbHAxQ5+WA8= -cloud.google.com/go/dataqna v0.8.2/go.mod h1:KNEqgx8TTmUipnQsScOoDpq/VlXVptUqVMZnt30WAPs= -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/datastore v1.10.0/go.mod h1:PC5UzAmDEkAmkfaknstTYbNpgE49HAgW2J1gcgUfmdM= -cloud.google.com/go/datastore v1.11.0/go.mod h1:TvGxBIHCS50u8jzG+AW/ppf87v1of8nwzFNgEZU1D3c= -cloud.google.com/go/datastore v1.12.0/go.mod h1:KjdB88W897MRITkvWWJrg2OUtrR5XVj1EoLgSp6/N70= -cloud.google.com/go/datastore v1.12.1/go.mod h1:KjdB88W897MRITkvWWJrg2OUtrR5XVj1EoLgSp6/N70= -cloud.google.com/go/datastore v1.13.0/go.mod h1:KjdB88W897MRITkvWWJrg2OUtrR5XVj1EoLgSp6/N70= -cloud.google.com/go/datastore v1.14.0/go.mod h1:GAeStMBIt9bPS7jMJA85kgkpsMkvseWWXiaHya9Jes8= -cloud.google.com/go/datastore v1.15.0/go.mod h1:GAeStMBIt9bPS7jMJA85kgkpsMkvseWWXiaHya9Jes8= -cloud.google.com/go/datastream v1.2.0/go.mod h1:i/uTP8/fZwgATHS/XFu0TcNUhuA0twZxxQ3EyCUQMwo= -cloud.google.com/go/datastream v1.3.0/go.mod h1:cqlOX8xlyYF/uxhiKn6Hbv6WjwPPuI9W2M9SAXwaLLQ= -cloud.google.com/go/datastream v1.4.0/go.mod h1:h9dpzScPhDTs5noEMQVWP8Wx8AFBRyS0s8KWPx/9r0g= -cloud.google.com/go/datastream v1.5.0/go.mod h1:6TZMMNPwjUqZHBKPQ1wwXpb0d5VDVPl2/XoS5yi88q4= -cloud.google.com/go/datastream v1.6.0/go.mod h1:6LQSuswqLa7S4rPAOZFVjHIG3wJIjZcZrw8JDEDJuIs= -cloud.google.com/go/datastream v1.7.0/go.mod h1:uxVRMm2elUSPuh65IbZpzJNMbuzkcvu5CjMqVIUHrww= -cloud.google.com/go/datastream v1.9.1/go.mod h1:hqnmr8kdUBmrnk65k5wNRoHSCYksvpdZIcZIEl8h43Q= -cloud.google.com/go/datastream v1.10.0/go.mod h1:hqnmr8kdUBmrnk65k5wNRoHSCYksvpdZIcZIEl8h43Q= -cloud.google.com/go/datastream v1.10.1/go.mod h1:7ngSYwnw95YFyTd5tOGBxHlOZiL+OtpjheqU7t2/s/c= -cloud.google.com/go/deploy v1.4.0/go.mod h1:5Xghikd4VrmMLNaF6FiRFDlHb59VM59YoDQnOUdsH/c= -cloud.google.com/go/deploy v1.5.0/go.mod h1:ffgdD0B89tToyW/U/D2eL0jN2+IEV/3EMuXHA0l4r+s= -cloud.google.com/go/deploy v1.6.0/go.mod h1:f9PTHehG/DjCom3QH0cntOVRm93uGBDt2vKzAPwpXQI= -cloud.google.com/go/deploy v1.8.0/go.mod h1:z3myEJnA/2wnB4sgjqdMfgxCA0EqC3RBTNcVPs93mtQ= -cloud.google.com/go/deploy v1.11.0/go.mod h1:tKuSUV5pXbn67KiubiUNUejqLs4f5cxxiCNCeyl0F2g= -cloud.google.com/go/deploy v1.13.0/go.mod h1:tKuSUV5pXbn67KiubiUNUejqLs4f5cxxiCNCeyl0F2g= -cloud.google.com/go/deploy v1.13.1/go.mod h1:8jeadyLkH9qu9xgO3hVWw8jVr29N1mnW42gRJT8GY6g= -cloud.google.com/go/dialogflow v1.15.0/go.mod h1:HbHDWs33WOGJgn6rfzBW1Kv807BE3O1+xGbn59zZWI4= -cloud.google.com/go/dialogflow v1.16.1/go.mod h1:po6LlzGfK+smoSmTBnbkIZY2w8ffjz/RcGSS+sh1el0= -cloud.google.com/go/dialogflow v1.17.0/go.mod h1:YNP09C/kXA1aZdBgC/VtXX74G/TKn7XVCcVumTflA+8= -cloud.google.com/go/dialogflow v1.18.0/go.mod h1:trO7Zu5YdyEuR+BhSNOqJezyFQ3aUzz0njv7sMx/iek= -cloud.google.com/go/dialogflow v1.19.0/go.mod h1:JVmlG1TwykZDtxtTXujec4tQ+D8SBFMoosgy+6Gn0s0= -cloud.google.com/go/dialogflow v1.29.0/go.mod h1:b+2bzMe+k1s9V+F2jbJwpHPzrnIyHihAdRFMtn2WXuM= -cloud.google.com/go/dialogflow v1.31.0/go.mod h1:cuoUccuL1Z+HADhyIA7dci3N5zUssgpBJmCzI6fNRB4= -cloud.google.com/go/dialogflow v1.32.0/go.mod h1:jG9TRJl8CKrDhMEcvfcfFkkpp8ZhgPz3sBGmAUYJ2qE= -cloud.google.com/go/dialogflow v1.38.0/go.mod h1:L7jnH+JL2mtmdChzAIcXQHXMvQkE3U4hTaNltEuxXn4= -cloud.google.com/go/dialogflow v1.40.0/go.mod h1:L7jnH+JL2mtmdChzAIcXQHXMvQkE3U4hTaNltEuxXn4= -cloud.google.com/go/dialogflow v1.43.0/go.mod h1:pDUJdi4elL0MFmt1REMvFkdsUTYSHq+rTCS8wg0S3+M= -cloud.google.com/go/dialogflow v1.44.0/go.mod h1:pDUJdi4elL0MFmt1REMvFkdsUTYSHq+rTCS8wg0S3+M= -cloud.google.com/go/dialogflow v1.44.1/go.mod h1:n/h+/N2ouKOO+rbe/ZnI186xImpqvCVj2DdsWS/0EAk= -cloud.google.com/go/dlp v1.6.0/go.mod h1:9eyB2xIhpU0sVwUixfBubDoRwP+GjeUoxxeueZmqvmM= -cloud.google.com/go/dlp v1.7.0/go.mod h1:68ak9vCiMBjbasxeVD17hVPxDEck+ExiHavX8kiHG+Q= -cloud.google.com/go/dlp v1.9.0/go.mod h1:qdgmqgTyReTz5/YNSSuueR8pl7hO0o9bQ39ZhtgkWp4= -cloud.google.com/go/dlp v1.10.1/go.mod h1:IM8BWz1iJd8njcNcG0+Kyd9OPnqnRNkDV8j42VT5KOI= -cloud.google.com/go/dlp v1.10.2/go.mod h1:ZbdKIhcnyhILgccwVDzkwqybthh7+MplGC3kZVZsIOQ= -cloud.google.com/go/documentai v1.7.0/go.mod h1:lJvftZB5NRiFSX4moiye1SMxHx0Bc3x1+p9e/RfXYiU= -cloud.google.com/go/documentai v1.8.0/go.mod h1:xGHNEB7CtsnySCNrCFdCyyMz44RhFEEX2Q7UD0c5IhU= -cloud.google.com/go/documentai v1.9.0/go.mod h1:FS5485S8R00U10GhgBC0aNGrJxBP8ZVpEeJ7PQDZd6k= -cloud.google.com/go/documentai v1.10.0/go.mod h1:vod47hKQIPeCfN2QS/jULIvQTugbmdc0ZvxxfQY1bg4= -cloud.google.com/go/documentai v1.16.0/go.mod h1:o0o0DLTEZ+YnJZ+J4wNfTxmDVyrkzFvttBXXtYRMHkM= -cloud.google.com/go/documentai v1.18.0/go.mod h1:F6CK6iUH8J81FehpskRmhLq/3VlwQvb7TvwOceQ2tbs= -cloud.google.com/go/documentai v1.20.0/go.mod h1:yJkInoMcK0qNAEdRnqY/D5asy73tnPe88I1YTZT+a8E= -cloud.google.com/go/documentai v1.22.0/go.mod h1:yJkInoMcK0qNAEdRnqY/D5asy73tnPe88I1YTZT+a8E= -cloud.google.com/go/documentai v1.22.1/go.mod h1:LKs22aDHbJv7ufXuPypzRO7rG3ALLJxzdCXDPutw4Qc= -cloud.google.com/go/documentai v1.23.0/go.mod h1:LKs22aDHbJv7ufXuPypzRO7rG3ALLJxzdCXDPutw4Qc= -cloud.google.com/go/documentai v1.23.2/go.mod h1:Q/wcRT+qnuXOpjAkvOV4A+IeQl04q2/ReT7SSbytLSo= -cloud.google.com/go/domains v0.6.0/go.mod h1:T9Rz3GasrpYk6mEGHh4rymIhjlnIuB4ofT1wTxDeT4Y= -cloud.google.com/go/domains v0.7.0/go.mod h1:PtZeqS1xjnXuRPKE/88Iru/LdfoRyEHYA9nFQf4UKpg= -cloud.google.com/go/domains v0.8.0/go.mod h1:M9i3MMDzGFXsydri9/vW+EWz9sWb4I6WyHqdlAk0idE= -cloud.google.com/go/domains v0.9.1/go.mod h1:aOp1c0MbejQQ2Pjf1iJvnVyT+z6R6s8pX66KaCSDYfE= -cloud.google.com/go/domains v0.9.2/go.mod h1:3YvXGYzZG1Temjbk7EyGCuGGiXHJwVNmwIf+E/cUp5I= -cloud.google.com/go/edgecontainer v0.1.0/go.mod h1:WgkZ9tp10bFxqO8BLPqv2LlfmQF1X8lZqwW4r1BTajk= -cloud.google.com/go/edgecontainer v0.2.0/go.mod h1:RTmLijy+lGpQ7BXuTDa4C4ssxyXT34NIuHIgKuP4s5w= -cloud.google.com/go/edgecontainer v0.3.0/go.mod h1:FLDpP4nykgwwIfcLt6zInhprzw0lEi2P1fjO6Ie0qbc= -cloud.google.com/go/edgecontainer v1.0.0/go.mod h1:cttArqZpBB2q58W/upSG++ooo6EsblxDIolxa3jSjbY= -cloud.google.com/go/edgecontainer v1.1.1/go.mod h1:O5bYcS//7MELQZs3+7mabRqoWQhXCzenBu0R8bz2rwk= -cloud.google.com/go/edgecontainer v1.1.2/go.mod h1:wQRjIzqxEs9e9wrtle4hQPSR1Y51kqN75dgF7UllZZ4= -cloud.google.com/go/errorreporting v0.3.0/go.mod h1:xsP2yaAp+OAW4OIm60An2bbLpqIhKXdWR/tawvl7QzU= -cloud.google.com/go/essentialcontacts v1.3.0/go.mod h1:r+OnHa5jfj90qIfZDO/VztSFqbQan7HV75p8sA+mdGI= -cloud.google.com/go/essentialcontacts v1.4.0/go.mod h1:8tRldvHYsmnBCHdFpvU+GL75oWiBKl80BiqlFh9tp+8= -cloud.google.com/go/essentialcontacts v1.5.0/go.mod h1:ay29Z4zODTuwliK7SnX8E86aUF2CTzdNtvv42niCX0M= -cloud.google.com/go/essentialcontacts v1.6.2/go.mod h1:T2tB6tX+TRak7i88Fb2N9Ok3PvY3UNbUsMag9/BARh4= -cloud.google.com/go/essentialcontacts v1.6.3/go.mod h1:yiPCD7f2TkP82oJEFXFTou8Jl8L6LBRPeBEkTaO0Ggo= -cloud.google.com/go/eventarc v1.7.0/go.mod h1:6ctpF3zTnaQCxUjHUdcfgcA1A2T309+omHZth7gDfmc= -cloud.google.com/go/eventarc v1.8.0/go.mod h1:imbzxkyAU4ubfsaKYdQg04WS1NvncblHEup4kvF+4gw= -cloud.google.com/go/eventarc v1.10.0/go.mod h1:u3R35tmZ9HvswGRBnF48IlYgYeBcPUCjkr4BTdem2Kw= -cloud.google.com/go/eventarc v1.11.0/go.mod h1:PyUjsUKPWoRBCHeOxZd/lbOOjahV41icXyUY5kSTvVY= -cloud.google.com/go/eventarc v1.12.1/go.mod h1:mAFCW6lukH5+IZjkvrEss+jmt2kOdYlN8aMx3sRJiAI= -cloud.google.com/go/eventarc v1.13.0/go.mod h1:mAFCW6lukH5+IZjkvrEss+jmt2kOdYlN8aMx3sRJiAI= -cloud.google.com/go/eventarc v1.13.1/go.mod h1:EqBxmGHFrruIara4FUQ3RHlgfCn7yo1HYsu2Hpt/C3Y= -cloud.google.com/go/filestore v1.3.0/go.mod h1:+qbvHGvXU1HaKX2nD0WEPo92TP/8AQuCVEBXNY9z0+w= -cloud.google.com/go/filestore v1.4.0/go.mod h1:PaG5oDfo9r224f8OYXURtAsY+Fbyq/bLYoINEK8XQAI= -cloud.google.com/go/filestore v1.5.0/go.mod h1:FqBXDWBp4YLHqRnVGveOkHDf8svj9r5+mUDLupOWEDs= -cloud.google.com/go/filestore v1.6.0/go.mod h1:di5unNuss/qfZTw2U9nhFqo8/ZDSc466dre85Kydllg= -cloud.google.com/go/filestore v1.7.1/go.mod h1:y10jsorq40JJnjR/lQ8AfFbbcGlw3g+Dp8oN7i7FjV4= -cloud.google.com/go/filestore v1.7.2/go.mod h1:TYOlyJs25f/omgj+vY7/tIG/E7BX369triSPzE4LdgE= -cloud.google.com/go/firestore v1.9.0/go.mod h1:HMkjKHNTtRyZNiMzu7YAsLr9K3X2udY2AMwDaMEQiiE= -cloud.google.com/go/firestore v1.11.0/go.mod h1:b38dKhgzlmNNGTNZZwe7ZRFEuRab1Hay3/DBsIGKKy4= -cloud.google.com/go/firestore v1.12.0/go.mod h1:b38dKhgzlmNNGTNZZwe7ZRFEuRab1Hay3/DBsIGKKy4= -cloud.google.com/go/firestore v1.13.0/go.mod h1:QojqqOh8IntInDUSTAh0c8ZsPYAr68Ma8c5DWOy8xb8= -cloud.google.com/go/functions v1.6.0/go.mod h1:3H1UA3qiIPRWD7PeZKLvHZ9SaQhR26XIJcC0A5GbvAk= -cloud.google.com/go/functions v1.7.0/go.mod h1:+d+QBcWM+RsrgZfV9xo6KfA1GlzJfxcfZcRPEhDDfzg= -cloud.google.com/go/functions v1.8.0/go.mod h1:RTZ4/HsQjIqIYP9a9YPbU+QFoQsAlYgrwOXJWHn1POY= -cloud.google.com/go/functions v1.9.0/go.mod h1:Y+Dz8yGguzO3PpIjhLTbnqV1CWmgQ5UwtlpzoyquQ08= -cloud.google.com/go/functions v1.10.0/go.mod h1:0D3hEOe3DbEvCXtYOZHQZmD+SzYsi1YbI7dGvHfldXw= -cloud.google.com/go/functions v1.12.0/go.mod h1:AXWGrF3e2C/5ehvwYo/GH6O5s09tOPksiKhz+hH8WkA= -cloud.google.com/go/functions v1.13.0/go.mod h1:EU4O007sQm6Ef/PwRsI8N2umygGqPBS/IZQKBQBcJ3c= -cloud.google.com/go/functions v1.15.1/go.mod h1:P5yNWUTkyU+LvW/S9O6V+V423VZooALQlqoXdoPz5AE= -cloud.google.com/go/functions v1.15.2/go.mod h1:CHAjtcR6OU4XF2HuiVeriEdELNcnvRZSk1Q8RMqy4lE= -cloud.google.com/go/gaming v1.5.0/go.mod h1:ol7rGcxP/qHTRQE/RO4bxkXq+Fix0j6D4LFPzYTIrDM= -cloud.google.com/go/gaming v1.6.0/go.mod h1:YMU1GEvA39Qt3zWGyAVA9bpYz/yAhTvaQ1t2sK4KPUA= -cloud.google.com/go/gaming v1.7.0/go.mod h1:LrB8U7MHdGgFG851iHAfqUdLcKBdQ55hzXy9xBJz0+w= -cloud.google.com/go/gaming v1.8.0/go.mod h1:xAqjS8b7jAVW0KFYeRUxngo9My3f33kFmua++Pi+ggM= -cloud.google.com/go/gaming v1.9.0/go.mod h1:Fc7kEmCObylSWLO334NcO+O9QMDyz+TKC4v1D7X+Bc0= -cloud.google.com/go/gaming v1.10.1/go.mod h1:XQQvtfP8Rb9Rxnxm5wFVpAp9zCQkJi2bLIb7iHGwB3s= -cloud.google.com/go/gkebackup v0.2.0/go.mod h1:XKvv/4LfG829/B8B7xRkk8zRrOEbKtEam6yNfuQNH60= -cloud.google.com/go/gkebackup v0.3.0/go.mod h1:n/E671i1aOQvUxT541aTkCwExO/bTer2HDlj4TsBRAo= -cloud.google.com/go/gkebackup v0.4.0/go.mod h1:byAyBGUwYGEEww7xsbnUTBHIYcOPy/PgUWUtOeRm9Vg= -cloud.google.com/go/gkebackup v1.3.0/go.mod h1:vUDOu++N0U5qs4IhG1pcOnD1Mac79xWy6GoBFlWCWBU= -cloud.google.com/go/gkebackup v1.3.1/go.mod h1:vUDOu++N0U5qs4IhG1pcOnD1Mac79xWy6GoBFlWCWBU= -cloud.google.com/go/gkebackup v1.3.2/go.mod h1:OMZbXzEJloyXMC7gqdSB+EOEQ1AKcpGYvO3s1ec5ixk= -cloud.google.com/go/gkeconnect v0.5.0/go.mod h1:c5lsNAg5EwAy7fkqX/+goqFsU1Da/jQFqArp+wGNr/o= -cloud.google.com/go/gkeconnect v0.6.0/go.mod h1:Mln67KyU/sHJEBY8kFZ0xTeyPtzbq9StAVvEULYK16A= -cloud.google.com/go/gkeconnect v0.7.0/go.mod h1:SNfmVqPkaEi3bF/B3CNZOAYPYdg7sU+obZ+QTky2Myw= -cloud.google.com/go/gkeconnect v0.8.1/go.mod h1:KWiK1g9sDLZqhxB2xEuPV8V9NYzrqTUmQR9shJHpOZw= -cloud.google.com/go/gkeconnect v0.8.2/go.mod h1:6nAVhwchBJYgQCXD2pHBFQNiJNyAd/wyxljpaa6ZPrY= -cloud.google.com/go/gkehub v0.9.0/go.mod h1:WYHN6WG8w9bXU0hqNxt8rm5uxnk8IH+lPY9J2TV7BK0= -cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y977wO+hBH0= -cloud.google.com/go/gkehub v0.11.0/go.mod h1:JOWHlmN+GHyIbuWQPl47/C2RFhnFKH38jH9Ascu3n0E= -cloud.google.com/go/gkehub v0.12.0/go.mod h1:djiIwwzTTBrF5NaXCGv3mf7klpEMcST17VBTVVDcuaw= -cloud.google.com/go/gkehub v0.14.1/go.mod h1:VEXKIJZ2avzrbd7u+zeMtW00Y8ddk/4V9511C9CQGTY= -cloud.google.com/go/gkehub v0.14.2/go.mod h1:iyjYH23XzAxSdhrbmfoQdePnlMj2EWcvnR+tHdBQsCY= -cloud.google.com/go/gkemulticloud v0.3.0/go.mod h1:7orzy7O0S+5kq95e4Hpn7RysVA7dPs8W/GgfUtsPbrA= -cloud.google.com/go/gkemulticloud v0.4.0/go.mod h1:E9gxVBnseLWCk24ch+P9+B2CoDFJZTyIgLKSalC7tuI= -cloud.google.com/go/gkemulticloud v0.5.0/go.mod h1:W0JDkiyi3Tqh0TJr//y19wyb1yf8llHVto2Htf2Ja3Y= -cloud.google.com/go/gkemulticloud v0.6.1/go.mod h1:kbZ3HKyTsiwqKX7Yw56+wUGwwNZViRnxWK2DVknXWfw= -cloud.google.com/go/gkemulticloud v1.0.0/go.mod h1:kbZ3HKyTsiwqKX7Yw56+wUGwwNZViRnxWK2DVknXWfw= -cloud.google.com/go/gkemulticloud v1.0.1/go.mod h1:AcrGoin6VLKT/fwZEYuqvVominLriQBCKmbjtnbMjG8= -cloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc= -cloud.google.com/go/grafeas v0.3.0/go.mod h1:P7hgN24EyONOTMyeJH6DxG4zD7fwiYa5Q6GUgyFSOU8= -cloud.google.com/go/gsuiteaddons v1.3.0/go.mod h1:EUNK/J1lZEZO8yPtykKxLXI6JSVN2rg9bN8SXOa0bgM= -cloud.google.com/go/gsuiteaddons v1.4.0/go.mod h1:rZK5I8hht7u7HxFQcFei0+AtfS9uSushomRlg+3ua1o= -cloud.google.com/go/gsuiteaddons v1.5.0/go.mod h1:TFCClYLd64Eaa12sFVmUyG62tk4mdIsI7pAnSXRkcFo= -cloud.google.com/go/gsuiteaddons v1.6.1/go.mod h1:CodrdOqRZcLp5WOwejHWYBjZvfY0kOphkAKpF/3qdZY= -cloud.google.com/go/gsuiteaddons v1.6.2/go.mod h1:K65m9XSgs8hTF3X9nNTPi8IQueljSdYo9F+Mi+s4MyU= -cloud.google.com/go/iam v0.1.0/go.mod h1:vcUNEa0pEm0qRVpmWepWaFMIAI8/hjB9mO8rNCJtF6c= -cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= -cloud.google.com/go/iam v0.5.0/go.mod h1:wPU9Vt0P4UmCux7mqtRu6jcpPAb74cP1fh50J3QpkUc= -cloud.google.com/go/iam v0.6.0/go.mod h1:+1AH33ueBne5MzYccyMHtEKqLE4/kJOibtffMHDMFMc= -cloud.google.com/go/iam v0.7.0/go.mod h1:H5Br8wRaDGNc8XP3keLc4unfUUZeyH3Sfl9XpQEYOeg= -cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE= -cloud.google.com/go/iam v0.11.0/go.mod h1:9PiLDanza5D+oWFZiH1uG+RnRCfEGKoyl6yo4cgWZGY= -cloud.google.com/go/iam v0.12.0/go.mod h1:knyHGviacl11zrtZUoDuYpDgLjvr28sLQaG0YB2GYAY= -cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0= -cloud.google.com/go/iam v1.0.1/go.mod h1:yR3tmSL8BcZB4bxByRv2jkSIahVmCtfKZwLYGBalRE8= -cloud.google.com/go/iam v1.1.0/go.mod h1:nxdHjaKfCr7fNYx/HJMM8LgiMugmveWlkatear5gVyk= -cloud.google.com/go/iam v1.1.1/go.mod h1:A5avdyVL2tCppe4unb0951eI9jreack+RJ0/d+KUZOU= -cloud.google.com/go/iam v1.1.2/go.mod h1:A5avdyVL2tCppe4unb0951eI9jreack+RJ0/d+KUZOU= -cloud.google.com/go/iam v1.1.3/go.mod h1:3khUlaBXfPKKe7huYgEpDn6FtgRyMEqbkvBxrQyY5SE= cloud.google.com/go/iam v1.1.6 h1:bEa06k05IO4f4uJonbB5iAgKTPpABy1ayxaIZV/GHVc= cloud.google.com/go/iam v1.1.6/go.mod h1:O0zxdPeGBoFdWW3HWmBxJsk0pfvNM/p/qa82rWOGTwI= -cloud.google.com/go/iap v1.4.0/go.mod h1:RGFwRJdihTINIe4wZ2iCP0zF/qu18ZwyKxrhMhygBEc= -cloud.google.com/go/iap v1.5.0/go.mod h1:UH/CGgKd4KyohZL5Pt0jSKE4m3FR51qg6FKQ/z/Ix9A= -cloud.google.com/go/iap v1.6.0/go.mod h1:NSuvI9C/j7UdjGjIde7t7HBz+QTwBcapPE07+sSRcLk= -cloud.google.com/go/iap v1.7.0/go.mod h1:beqQx56T9O1G1yNPph+spKpNibDlYIiIixiqsQXxLIo= -cloud.google.com/go/iap v1.7.1/go.mod h1:WapEwPc7ZxGt2jFGB/C/bm+hP0Y6NXzOYGjpPnmMS74= -cloud.google.com/go/iap v1.8.1/go.mod h1:sJCbeqg3mvWLqjZNsI6dfAtbbV1DL2Rl7e1mTyXYREQ= -cloud.google.com/go/iap v1.9.0/go.mod h1:01OFxd1R+NFrg78S+hoPV5PxEzv22HXaNqUUlmNHFuY= -cloud.google.com/go/iap v1.9.1/go.mod h1:SIAkY7cGMLohLSdBR25BuIxO+I4fXJiL06IBL7cy/5Q= -cloud.google.com/go/ids v1.1.0/go.mod h1:WIuwCaYVOzHIj2OhN9HAwvW+DBdmUAdcWlFxRl+KubM= -cloud.google.com/go/ids v1.2.0/go.mod h1:5WXvp4n25S0rA/mQWAg1YEEBBq6/s+7ml1RDCW1IrcY= -cloud.google.com/go/ids v1.3.0/go.mod h1:JBdTYwANikFKaDP6LtW5JAi4gubs57SVNQjemdt6xV4= -cloud.google.com/go/ids v1.4.1/go.mod h1:np41ed8YMU8zOgv53MMMoCntLTn2lF+SUzlM+O3u/jw= -cloud.google.com/go/ids v1.4.2/go.mod h1:3vw8DX6YddRu9BncxuzMyWn0g8+ooUjI2gslJ7FH3vk= -cloud.google.com/go/iot v1.3.0/go.mod h1:r7RGh2B61+B8oz0AGE+J72AhA0G7tdXItODWsaA2oLs= -cloud.google.com/go/iot v1.4.0/go.mod h1:dIDxPOn0UvNDUMD8Ger7FIaTuvMkj+aGk94RPP0iV+g= -cloud.google.com/go/iot v1.5.0/go.mod h1:mpz5259PDl3XJthEmh9+ap0affn/MqNSP4My77Qql9o= -cloud.google.com/go/iot v1.6.0/go.mod h1:IqdAsmE2cTYYNO1Fvjfzo9po179rAtJeVGUvkLN3rLE= -cloud.google.com/go/iot v1.7.1/go.mod h1:46Mgw7ev1k9KqK1ao0ayW9h0lI+3hxeanz+L1zmbbbk= -cloud.google.com/go/iot v1.7.2/go.mod h1:q+0P5zr1wRFpw7/MOgDXrG/HVA+l+cSwdObffkrpnSg= -cloud.google.com/go/kms v1.4.0/go.mod h1:fajBHndQ+6ubNw6Ss2sSd+SWvjL26RNo/dr7uxsnnOA= -cloud.google.com/go/kms v1.5.0/go.mod h1:QJS2YY0eJGBg3mnDfuaCyLauWwBJiHRboYxJ++1xJNg= -cloud.google.com/go/kms v1.6.0/go.mod h1:Jjy850yySiasBUDi6KFUwUv2n1+o7QZFyuUJg6OgjA0= -cloud.google.com/go/kms v1.8.0/go.mod h1:4xFEhYFqvW+4VMELtZyxomGSYtSQKzM178ylFW4jMAg= -cloud.google.com/go/kms v1.9.0/go.mod h1:qb1tPTgfF9RQP8e1wq4cLFErVuTJv7UsSC915J8dh3w= -cloud.google.com/go/kms v1.10.0/go.mod h1:ng3KTUtQQU9bPX3+QGLsflZIHlkbn8amFAMY63m8d24= -cloud.google.com/go/kms v1.10.1/go.mod h1:rIWk/TryCkR59GMC3YtHtXeLzd634lBbKenvyySAyYI= -cloud.google.com/go/kms v1.11.0/go.mod h1:hwdiYC0xjnWsKQQCQQmIQnS9asjYVSK6jtXm+zFqXLM= -cloud.google.com/go/kms v1.12.1/go.mod h1:c9J991h5DTl+kg7gi3MYomh12YEENGrf48ee/N/2CDM= -cloud.google.com/go/kms v1.15.0/go.mod h1:c9J991h5DTl+kg7gi3MYomh12YEENGrf48ee/N/2CDM= -cloud.google.com/go/kms v1.15.2/go.mod h1:3hopT4+7ooWRCjc2DxgnpESFxhIraaI2IpAVUEhbT/w= -cloud.google.com/go/kms v1.15.3/go.mod h1:AJdXqHxS2GlPyduM99s9iGqi2nwbviBbhV/hdmt4iOQ= -cloud.google.com/go/kms v1.15.5/go.mod h1:cU2H5jnp6G2TDpUGZyqTCoy1n16fbubHZjmVXSMtwDI= -cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic= -cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI= -cloud.google.com/go/language v1.7.0/go.mod h1:DJ6dYN/W+SQOjF8e1hLQXMF21AkH2w9wiPzPCJa2MIE= -cloud.google.com/go/language v1.8.0/go.mod h1:qYPVHf7SPoNNiCL2Dr0FfEFNil1qi3pQEyygwpgVKB8= -cloud.google.com/go/language v1.9.0/go.mod h1:Ns15WooPM5Ad/5no/0n81yUetis74g3zrbeJBE+ptUY= -cloud.google.com/go/language v1.10.1/go.mod h1:CPp94nsdVNiQEt1CNjF5WkTcisLiHPyIbMhvR8H2AW0= -cloud.google.com/go/language v1.11.0/go.mod h1:uDx+pFDdAKTY8ehpWbiXyQdz8tDSYLJbQcXsCkjYyvQ= -cloud.google.com/go/language v1.11.1/go.mod h1:Xyid9MG9WOX3utvDbpX7j3tXDmmDooMyMDqgUVpH17U= -cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8= -cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08= -cloud.google.com/go/lifesciences v0.8.0/go.mod h1:lFxiEOMqII6XggGbOnKiyZ7IBwoIqA84ClvoezaA/bo= -cloud.google.com/go/lifesciences v0.9.1/go.mod h1:hACAOd1fFbCGLr/+weUKRAJas82Y4vrL3O5326N//Wc= -cloud.google.com/go/lifesciences v0.9.2/go.mod h1:QHEOO4tDzcSAzeJg7s2qwnLM2ji8IRpQl4p6m5Z9yTA= -cloud.google.com/go/logging v1.6.1/go.mod h1:5ZO0mHHbvm8gEmeEUHrmDlTDSu5imF6MUP9OfilNXBw= -cloud.google.com/go/logging v1.7.0/go.mod h1:3xjP2CjkM3ZkO73aj4ASA5wRPGGCRrPIAeNqVNkzY8M= -cloud.google.com/go/logging v1.8.1/go.mod h1:TJjR+SimHwuC8MZ9cjByQulAMgni+RkXeI3wwctHJEI= -cloud.google.com/go/longrunning v0.1.1/go.mod h1:UUFxuDWkv22EuY93jjmDMFT5GPQKeFVJBIF6QlTqdsE= -cloud.google.com/go/longrunning v0.3.0/go.mod h1:qth9Y41RRSUE69rDcOn6DdK3HfQfsUI0YSmW3iIlLJc= -cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo= -cloud.google.com/go/longrunning v0.4.2/go.mod h1:OHrnaYyLUV6oqwh0xiS7e5sLQhP1m0QU9R+WhGDMgIQ= -cloud.google.com/go/longrunning v0.5.0/go.mod h1:0JNuqRShmscVAhIACGtskSAWtqtOoPkwP0YF1oVEchc= -cloud.google.com/go/longrunning v0.5.1/go.mod h1:spvimkwdz6SPWKEt/XBij79E9fiTkHSQl/fRUUQJYJc= -cloud.google.com/go/longrunning v0.5.2/go.mod h1:nqo6DQbNV2pXhGDbDMoN2bWz68MjZUzqv2YttZiveCs= cloud.google.com/go/longrunning v0.5.5 h1:GOE6pZFdSrTb4KAiKnXsJBtlE6mEyaW44oKyMILWnOg= cloud.google.com/go/longrunning v0.5.5/go.mod h1:WV2LAxD8/rg5Z1cNW6FJ/ZpX4E4VnDnoTk0yawPBB7s= -cloud.google.com/go/managedidentities v1.3.0/go.mod h1:UzlW3cBOiPrzucO5qWkNkh0w33KFtBJU281hacNvsdE= -cloud.google.com/go/managedidentities v1.4.0/go.mod h1:NWSBYbEMgqmbZsLIyKvxrYbtqOsxY1ZrGM+9RgDqInM= -cloud.google.com/go/managedidentities v1.5.0/go.mod h1:+dWcZ0JlUmpuxpIDfyP5pP5y0bLdRwOS4Lp7gMni/LA= -cloud.google.com/go/managedidentities v1.6.1/go.mod h1:h/irGhTN2SkZ64F43tfGPMbHnypMbu4RB3yl8YcuEak= -cloud.google.com/go/managedidentities v1.6.2/go.mod h1:5c2VG66eCa0WIq6IylRk3TBW83l161zkFvCj28X7jn8= -cloud.google.com/go/maps v0.1.0/go.mod h1:BQM97WGyfw9FWEmQMpZ5T6cpovXXSd1cGmFma94eubI= -cloud.google.com/go/maps v0.6.0/go.mod h1:o6DAMMfb+aINHz/p/jbcY+mYeXBoZoxTfdSQ8VAJaCw= -cloud.google.com/go/maps v0.7.0/go.mod h1:3GnvVl3cqeSvgMcpRlQidXsPYuDGQ8naBis7MVzpXsY= -cloud.google.com/go/maps v1.3.0/go.mod h1:6mWTUv+WhnOwAgjVsSW2QPPECmW+s3PcRyOa9vgG/5s= -cloud.google.com/go/maps v1.4.0/go.mod h1:6mWTUv+WhnOwAgjVsSW2QPPECmW+s3PcRyOa9vgG/5s= -cloud.google.com/go/maps v1.4.1/go.mod h1:BxSa0BnW1g2U2gNdbq5zikLlHUuHW0GFWh7sgML2kIY= -cloud.google.com/go/mediatranslation v0.5.0/go.mod h1:jGPUhGTybqsPQn91pNXw0xVHfuJ3leR1wj37oU3y1f4= -cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w= -cloud.google.com/go/mediatranslation v0.7.0/go.mod h1:LCnB/gZr90ONOIQLgSXagp8XUW1ODs2UmUMvcgMfI2I= -cloud.google.com/go/mediatranslation v0.8.1/go.mod h1:L/7hBdEYbYHQJhX2sldtTO5SZZ1C1vkapubj0T2aGig= -cloud.google.com/go/mediatranslation v0.8.2/go.mod h1:c9pUaDRLkgHRx3irYE5ZC8tfXGrMYwNZdmDqKMSfFp8= -cloud.google.com/go/memcache v1.4.0/go.mod h1:rTOfiGZtJX1AaFUrOgsMHX5kAzaTQ8azHiuDoTPzNsE= -cloud.google.com/go/memcache v1.5.0/go.mod h1:dk3fCK7dVo0cUU2c36jKb4VqKPS22BTkf81Xq617aWM= -cloud.google.com/go/memcache v1.6.0/go.mod h1:XS5xB0eQZdHtTuTF9Hf8eJkKtR3pVRCcvJwtm68T3rA= -cloud.google.com/go/memcache v1.7.0/go.mod h1:ywMKfjWhNtkQTxrWxCkCFkoPjLHPW6A7WOTVI8xy3LY= -cloud.google.com/go/memcache v1.9.0/go.mod h1:8oEyzXCu+zo9RzlEaEjHl4KkgjlNDaXbCQeQWlzNFJM= -cloud.google.com/go/memcache v1.10.1/go.mod h1:47YRQIarv4I3QS5+hoETgKO40InqzLP6kpNLvyXuyaA= -cloud.google.com/go/memcache v1.10.2/go.mod h1:f9ZzJHLBrmd4BkguIAa/l/Vle6uTHzHokdnzSWOdQ6A= -cloud.google.com/go/metastore v1.5.0/go.mod h1:2ZNrDcQwghfdtCwJ33nM0+GrBGlVuh8rakL3vdPY3XY= -cloud.google.com/go/metastore v1.6.0/go.mod h1:6cyQTls8CWXzk45G55x57DVQ9gWg7RiH65+YgPsNh9s= -cloud.google.com/go/metastore v1.7.0/go.mod h1:s45D0B4IlsINu87/AsWiEVYbLaIMeUSoxlKKDqBGFS8= -cloud.google.com/go/metastore v1.8.0/go.mod h1:zHiMc4ZUpBiM7twCIFQmJ9JMEkDSyZS9U12uf7wHqSI= -cloud.google.com/go/metastore v1.10.0/go.mod h1:fPEnH3g4JJAk+gMRnrAnoqyv2lpUCqJPWOodSaf45Eo= -cloud.google.com/go/metastore v1.11.1/go.mod h1:uZuSo80U3Wd4zi6C22ZZliOUJ3XeM/MlYi/z5OAOWRA= -cloud.google.com/go/metastore v1.12.0/go.mod h1:uZuSo80U3Wd4zi6C22ZZliOUJ3XeM/MlYi/z5OAOWRA= -cloud.google.com/go/metastore v1.13.0/go.mod h1:URDhpG6XLeh5K+Glq0NOt74OfrPKTwS62gEPZzb5SOk= -cloud.google.com/go/metastore v1.13.1/go.mod h1:IbF62JLxuZmhItCppcIfzBBfUFq0DIB9HPDoLgWrVOU= -cloud.google.com/go/monitoring v1.7.0/go.mod h1:HpYse6kkGo//7p6sT0wsIC6IBDET0RhIsnmlA53dvEk= -cloud.google.com/go/monitoring v1.8.0/go.mod h1:E7PtoMJ1kQXWxPjB6mv2fhC5/15jInuulFdYYtlcvT4= -cloud.google.com/go/monitoring v1.12.0/go.mod h1:yx8Jj2fZNEkL/GYZyTLS4ZtZEZN8WtDEiEqG4kLK50w= -cloud.google.com/go/monitoring v1.13.0/go.mod h1:k2yMBAB1H9JT/QETjNkgdCGD9bPF712XiLTVr+cBrpw= -cloud.google.com/go/monitoring v1.15.1/go.mod h1:lADlSAlFdbqQuwwpaImhsJXu1QSdd3ojypXrFSMr2rM= -cloud.google.com/go/monitoring v1.16.0/go.mod h1:Ptp15HgAyM1fNICAojDMoNc/wUmn67mLHQfyqbw+poY= -cloud.google.com/go/monitoring v1.16.1/go.mod h1:6HsxddR+3y9j+o/cMJH6q/KJ/CBTvM/38L/1m7bTRJ4= -cloud.google.com/go/networkconnectivity v1.4.0/go.mod h1:nOl7YL8odKyAOtzNX73/M5/mGZgqqMeryi6UPZTk/rA= -cloud.google.com/go/networkconnectivity v1.5.0/go.mod h1:3GzqJx7uhtlM3kln0+x5wyFvuVH1pIBJjhCpjzSt75o= -cloud.google.com/go/networkconnectivity v1.6.0/go.mod h1:OJOoEXW+0LAxHh89nXd64uGG+FbQoeH8DtxCHVOMlaM= -cloud.google.com/go/networkconnectivity v1.7.0/go.mod h1:RMuSbkdbPwNMQjB5HBWD5MpTBnNm39iAVpC3TmsExt8= -cloud.google.com/go/networkconnectivity v1.10.0/go.mod h1:UP4O4sWXJG13AqrTdQCD9TnLGEbtNRqjuaaA7bNjF5E= -cloud.google.com/go/networkconnectivity v1.11.0/go.mod h1:iWmDD4QF16VCDLXUqvyspJjIEtBR/4zq5hwnY2X3scM= -cloud.google.com/go/networkconnectivity v1.12.1/go.mod h1:PelxSWYM7Sh9/guf8CFhi6vIqf19Ir/sbfZRUwXh92E= -cloud.google.com/go/networkconnectivity v1.13.0/go.mod h1:SAnGPes88pl7QRLUen2HmcBSE9AowVAcdug8c0RSBFk= -cloud.google.com/go/networkconnectivity v1.14.0/go.mod h1:SAnGPes88pl7QRLUen2HmcBSE9AowVAcdug8c0RSBFk= -cloud.google.com/go/networkconnectivity v1.14.1/go.mod h1:LyGPXR742uQcDxZ/wv4EI0Vu5N6NKJ77ZYVnDe69Zug= -cloud.google.com/go/networkmanagement v1.4.0/go.mod h1:Q9mdLLRn60AsOrPc8rs8iNV6OHXaGcDdsIQe1ohekq8= -cloud.google.com/go/networkmanagement v1.5.0/go.mod h1:ZnOeZ/evzUdUsnvRt792H0uYEnHQEMaz+REhhzJRcf4= -cloud.google.com/go/networkmanagement v1.6.0/go.mod h1:5pKPqyXjB/sgtvB5xqOemumoQNB7y95Q7S+4rjSOPYY= -cloud.google.com/go/networkmanagement v1.8.0/go.mod h1:Ho/BUGmtyEqrttTgWEe7m+8vDdK74ibQc+Be0q7Fof0= -cloud.google.com/go/networkmanagement v1.9.0/go.mod h1:UTUaEU9YwbCAhhz3jEOHr+2/K/MrBk2XxOLS89LQzFw= -cloud.google.com/go/networkmanagement v1.9.1/go.mod h1:CCSYgrQQvW73EJawO2QamemYcOb57LvrDdDU51F0mcI= -cloud.google.com/go/networksecurity v0.5.0/go.mod h1:xS6fOCoqpVC5zx15Z/MqkfDwH4+m/61A3ODiDV1xmiQ= -cloud.google.com/go/networksecurity v0.6.0/go.mod h1:Q5fjhTr9WMI5mbpRYEbiexTzROf7ZbDzvzCrNl14nyU= -cloud.google.com/go/networksecurity v0.7.0/go.mod h1:mAnzoxx/8TBSyXEeESMy9OOYwo1v+gZ5eMRnsT5bC8k= -cloud.google.com/go/networksecurity v0.8.0/go.mod h1:B78DkqsxFG5zRSVuwYFRZ9Xz8IcQ5iECsNrPn74hKHU= -cloud.google.com/go/networksecurity v0.9.1/go.mod h1:MCMdxOKQ30wsBI1eI659f9kEp4wuuAueoC9AJKSPWZQ= -cloud.google.com/go/networksecurity v0.9.2/go.mod h1:jG0SeAttWzPMUILEHDUvFYdQTl8L/E/KC8iZDj85lEI= -cloud.google.com/go/notebooks v1.2.0/go.mod h1:9+wtppMfVPUeJ8fIWPOq1UnATHISkGXGqTkxeieQ6UY= -cloud.google.com/go/notebooks v1.3.0/go.mod h1:bFR5lj07DtCPC7YAAJ//vHskFBxA5JzYlH68kXVdk34= -cloud.google.com/go/notebooks v1.4.0/go.mod h1:4QPMngcwmgb6uw7Po99B2xv5ufVoIQ7nOGDyL4P8AgA= -cloud.google.com/go/notebooks v1.5.0/go.mod h1:q8mwhnP9aR8Hpfnrc5iN5IBhrXUy8S2vuYs+kBJ/gu0= -cloud.google.com/go/notebooks v1.7.0/go.mod h1:PVlaDGfJgj1fl1S3dUwhFMXFgfYGhYQt2164xOMONmE= -cloud.google.com/go/notebooks v1.8.0/go.mod h1:Lq6dYKOYOWUCTvw5t2q1gp1lAp0zxAxRycayS0iJcqQ= -cloud.google.com/go/notebooks v1.9.1/go.mod h1:zqG9/gk05JrzgBt4ghLzEepPHNwE5jgPcHZRKhlC1A8= -cloud.google.com/go/notebooks v1.10.0/go.mod h1:SOPYMZnttHxqot0SGSFSkRrwE29eqnKPBJFqgWmiK2k= -cloud.google.com/go/notebooks v1.10.1/go.mod h1:5PdJc2SgAybE76kFQCWrTfJolCOUQXF97e+gteUUA6A= -cloud.google.com/go/optimization v1.1.0/go.mod h1:5po+wfvX5AQlPznyVEZjGJTMr4+CAkJf2XSTQOOl9l4= -cloud.google.com/go/optimization v1.2.0/go.mod h1:Lr7SOHdRDENsh+WXVmQhQTrzdu9ybg0NecjHidBq6xs= -cloud.google.com/go/optimization v1.3.1/go.mod h1:IvUSefKiwd1a5p0RgHDbWCIbDFgKuEdB+fPPuP0IDLI= -cloud.google.com/go/optimization v1.4.1/go.mod h1:j64vZQP7h9bO49m2rVaTVoNM0vEBEN5eKPUPbZyXOrk= -cloud.google.com/go/optimization v1.5.0/go.mod h1:evo1OvTxeBRBu6ydPlrIRizKY/LJKo/drDMMRKqGEUU= -cloud.google.com/go/optimization v1.5.1/go.mod h1:NC0gnUD5MWVAF7XLdoYVPmYYVth93Q6BUzqAq3ZwtV8= -cloud.google.com/go/orchestration v1.3.0/go.mod h1:Sj5tq/JpWiB//X/q3Ngwdl5K7B7Y0KZ7bfv0wL6fqVA= -cloud.google.com/go/orchestration v1.4.0/go.mod h1:6W5NLFWs2TlniBphAViZEVhrXRSMgUGDfW7vrWKvsBk= -cloud.google.com/go/orchestration v1.6.0/go.mod h1:M62Bevp7pkxStDfFfTuCOaXgaaqRAga1yKyoMtEoWPQ= -cloud.google.com/go/orchestration v1.8.1/go.mod h1:4sluRF3wgbYVRqz7zJ1/EUNc90TTprliq9477fGobD8= -cloud.google.com/go/orchestration v1.8.2/go.mod h1:T1cP+6WyTmh6LSZzeUhvGf0uZVmJyTx7t8z7Vg87+A0= -cloud.google.com/go/orgpolicy v1.4.0/go.mod h1:xrSLIV4RePWmP9P3tBl8S93lTmlAxjm06NSm2UTmKvE= -cloud.google.com/go/orgpolicy v1.5.0/go.mod h1:hZEc5q3wzwXJaKrsx5+Ewg0u1LxJ51nNFlext7Tanwc= -cloud.google.com/go/orgpolicy v1.10.0/go.mod h1:w1fo8b7rRqlXlIJbVhOMPrwVljyuW5mqssvBtU18ONc= -cloud.google.com/go/orgpolicy v1.11.0/go.mod h1:2RK748+FtVvnfuynxBzdnyu7sygtoZa1za/0ZfpOs1M= -cloud.google.com/go/orgpolicy v1.11.1/go.mod h1:8+E3jQcpZJQliP+zaFfayC2Pg5bmhuLK755wKhIIUCE= -cloud.google.com/go/orgpolicy v1.11.2/go.mod h1:biRDpNwfyytYnmCRWZWxrKF22Nkz9eNVj9zyaBdpm1o= -cloud.google.com/go/osconfig v1.7.0/go.mod h1:oVHeCeZELfJP7XLxcBGTMBvRO+1nQ5tFG9VQTmYS2Fs= -cloud.google.com/go/osconfig v1.8.0/go.mod h1:EQqZLu5w5XA7eKizepumcvWx+m8mJUhEwiPqWiZeEdg= -cloud.google.com/go/osconfig v1.9.0/go.mod h1:Yx+IeIZJ3bdWmzbQU4fxNl8xsZ4amB+dygAwFPlvnNo= -cloud.google.com/go/osconfig v1.10.0/go.mod h1:uMhCzqC5I8zfD9zDEAfvgVhDS8oIjySWh+l4WK6GnWw= -cloud.google.com/go/osconfig v1.11.0/go.mod h1:aDICxrur2ogRd9zY5ytBLV89KEgT2MKB2L/n6x1ooPw= -cloud.google.com/go/osconfig v1.12.0/go.mod h1:8f/PaYzoS3JMVfdfTubkowZYGmAhUCjjwnjqWI7NVBc= -cloud.google.com/go/osconfig v1.12.1/go.mod h1:4CjBxND0gswz2gfYRCUoUzCm9zCABp91EeTtWXyz0tE= -cloud.google.com/go/osconfig v1.12.2/go.mod h1:eh9GPaMZpI6mEJEuhEjUJmaxvQ3gav+fFEJon1Y8Iw0= -cloud.google.com/go/oslogin v1.4.0/go.mod h1:YdgMXWRaElXz/lDk1Na6Fh5orF7gvmJ0FGLIs9LId4E= -cloud.google.com/go/oslogin v1.5.0/go.mod h1:D260Qj11W2qx/HVF29zBg+0fd6YCSjSqLUkY/qEenQU= -cloud.google.com/go/oslogin v1.6.0/go.mod h1:zOJ1O3+dTU8WPlGEkFSh7qeHPPSoxrcMbbK1Nm2iX70= -cloud.google.com/go/oslogin v1.7.0/go.mod h1:e04SN0xO1UNJ1M5GP0vzVBFicIe4O53FOfcixIqTyXo= -cloud.google.com/go/oslogin v1.9.0/go.mod h1:HNavntnH8nzrn8JCTT5fj18FuJLFJc4NaZJtBnQtKFs= -cloud.google.com/go/oslogin v1.10.1/go.mod h1:x692z7yAue5nE7CsSnoG0aaMbNoRJRXO4sn73R+ZqAs= -cloud.google.com/go/oslogin v1.11.0/go.mod h1:8GMTJs4X2nOAUVJiPGqIWVcDaF0eniEto3xlOxaboXE= -cloud.google.com/go/oslogin v1.11.1/go.mod h1:OhD2icArCVNUxKqtK0mcSmKL7lgr0LVlQz+v9s1ujTg= -cloud.google.com/go/phishingprotection v0.5.0/go.mod h1:Y3HZknsK9bc9dMi+oE8Bim0lczMU6hrX0UpADuMefr0= -cloud.google.com/go/phishingprotection v0.6.0/go.mod h1:9Y3LBLgy0kDTcYET8ZH3bq/7qni15yVUoAxiFxnlSUA= -cloud.google.com/go/phishingprotection v0.7.0/go.mod h1:8qJI4QKHoda/sb/7/YmMQ2omRLSLYSu9bU0EKCNI+Lk= -cloud.google.com/go/phishingprotection v0.8.1/go.mod h1:AxonW7GovcA8qdEk13NfHq9hNx5KPtfxXNeUxTDxB6I= -cloud.google.com/go/phishingprotection v0.8.2/go.mod h1:LhJ91uyVHEYKSKcMGhOa14zMMWfbEdxG032oT6ECbC8= -cloud.google.com/go/policytroubleshooter v1.3.0/go.mod h1:qy0+VwANja+kKrjlQuOzmlvscn4RNsAc0e15GGqfMxg= -cloud.google.com/go/policytroubleshooter v1.4.0/go.mod h1:DZT4BcRw3QoO8ota9xw/LKtPa8lKeCByYeKTIf/vxdE= -cloud.google.com/go/policytroubleshooter v1.5.0/go.mod h1:Rz1WfV+1oIpPdN2VvvuboLVRsB1Hclg3CKQ53j9l8vw= -cloud.google.com/go/policytroubleshooter v1.6.0/go.mod h1:zYqaPTsmfvpjm5ULxAyD/lINQxJ0DDsnWOP/GZ7xzBc= -cloud.google.com/go/policytroubleshooter v1.7.1/go.mod h1:0NaT5v3Ag1M7U5r0GfDCpUFkWd9YqpubBWsQlhanRv0= -cloud.google.com/go/policytroubleshooter v1.8.0/go.mod h1:tmn5Ir5EToWe384EuboTcVQT7nTag2+DuH3uHmKd1HU= -cloud.google.com/go/policytroubleshooter v1.9.0/go.mod h1:+E2Lga7TycpeSTj2FsH4oXxTnrbHJGRlKhVZBLGgU64= -cloud.google.com/go/policytroubleshooter v1.9.1/go.mod h1:MYI8i0bCrL8cW+VHN1PoiBTyNZTstCg2WUw2eVC4c4U= -cloud.google.com/go/privatecatalog v0.5.0/go.mod h1:XgosMUvvPyxDjAVNDYxJ7wBW8//hLDDYmnsNcMGq1K0= -cloud.google.com/go/privatecatalog v0.6.0/go.mod h1:i/fbkZR0hLN29eEWiiwue8Pb+GforiEIBnV9yrRUOKI= -cloud.google.com/go/privatecatalog v0.7.0/go.mod h1:2s5ssIFO69F5csTXcwBP7NPFTZvps26xGzvQ2PQaBYg= -cloud.google.com/go/privatecatalog v0.8.0/go.mod h1:nQ6pfaegeDAq/Q5lrfCQzQLhubPiZhSaNhIgfJlnIXs= -cloud.google.com/go/privatecatalog v0.9.1/go.mod h1:0XlDXW2unJXdf9zFz968Hp35gl/bhF4twwpXZAW50JA= -cloud.google.com/go/privatecatalog v0.9.2/go.mod h1:RMA4ATa8IXfzvjrhhK8J6H4wwcztab+oZph3c6WmtFc= cloud.google.com/go/profiler v0.3.0 h1:R6y/xAeifaUXxd2x6w+jIwKxoKl8Cv5HJvcvASTPWJo= cloud.google.com/go/profiler v0.3.0/go.mod h1:9wYk9eY4iZHsev8TQb61kh3wiOiSyz/xOYixWPzweCU= -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/pubsub v1.26.0/go.mod h1:QgBH3U/jdJy/ftjPhTkyXNj543Tin1pRYcdcPRnFIRI= -cloud.google.com/go/pubsub v1.27.1/go.mod h1:hQN39ymbV9geqBnfQq6Xf63yNhUAhv9CZhzp5O6qsW0= -cloud.google.com/go/pubsub v1.28.0/go.mod h1:vuXFpwaVoIPQMGXqRyUQigu/AX1S3IWugR9xznmcXX8= -cloud.google.com/go/pubsub v1.30.0/go.mod h1:qWi1OPS0B+b5L+Sg6Gmc9zD1Y+HaM0MdUr7LsupY1P4= -cloud.google.com/go/pubsub v1.32.0/go.mod h1:f+w71I33OMyxf9VpMVcZbnG5KSUkCOUHYpFd5U1GdRc= -cloud.google.com/go/pubsub v1.33.0/go.mod h1:f+w71I33OMyxf9VpMVcZbnG5KSUkCOUHYpFd5U1GdRc= -cloud.google.com/go/pubsublite v1.5.0/go.mod h1:xapqNQ1CuLfGi23Yda/9l4bBCKz/wC3KIJ5gKcxveZg= -cloud.google.com/go/pubsublite v1.6.0/go.mod h1:1eFCS0U11xlOuMFV/0iBqw3zP12kddMeCbj/F3FSj9k= -cloud.google.com/go/pubsublite v1.7.0/go.mod h1:8hVMwRXfDfvGm3fahVbtDbiLePT3gpoiJYJY+vxWxVM= -cloud.google.com/go/pubsublite v1.8.1/go.mod h1:fOLdU4f5xldK4RGJrBMm+J7zMWNj/k4PxwEZXy39QS0= -cloud.google.com/go/recaptchaenterprise v1.3.1/go.mod h1:OdD+q+y4XGeAlxRaMn1Y7/GveP6zmq76byL6tjPE7d4= -cloud.google.com/go/recaptchaenterprise/v2 v2.1.0/go.mod h1:w9yVqajwroDNTfGuhmOjPDN//rZGySaf6PtFVcSCa7o= -cloud.google.com/go/recaptchaenterprise/v2 v2.2.0/go.mod h1:/Zu5jisWGeERrd5HnlS3EUGb/D335f9k51B/FVil0jk= -cloud.google.com/go/recaptchaenterprise/v2 v2.3.0/go.mod h1:O9LwGCjrhGHBQET5CA7dd5NwwNQUErSgEDit1DLNTdo= -cloud.google.com/go/recaptchaenterprise/v2 v2.4.0/go.mod h1:Am3LHfOuBstrLrNCBrlI5sbwx9LBg3te2N6hGvHn2mE= -cloud.google.com/go/recaptchaenterprise/v2 v2.5.0/go.mod h1:O8LzcHXN3rz0j+LBC91jrwI3R+1ZSZEWrfL7XHgNo9U= -cloud.google.com/go/recaptchaenterprise/v2 v2.6.0/go.mod h1:RPauz9jeLtB3JVzg6nCbe12qNoaa8pXc4d/YukAmcnA= -cloud.google.com/go/recaptchaenterprise/v2 v2.7.0/go.mod h1:19wVj/fs5RtYtynAPJdDTb69oW0vNHYDBTbB4NvMD9c= -cloud.google.com/go/recaptchaenterprise/v2 v2.7.2/go.mod h1:kR0KjsJS7Jt1YSyWFkseQ756D45kaYNTlDPPaRAvDBU= -cloud.google.com/go/recaptchaenterprise/v2 v2.8.0/go.mod h1:QuE8EdU9dEnesG8/kG3XuJyNsjEqMlMzg3v3scCJ46c= -cloud.google.com/go/recaptchaenterprise/v2 v2.8.1/go.mod h1:JZYZJOeZjgSSTGP4uz7NlQ4/d1w5hGmksVgM0lbEij0= -cloud.google.com/go/recommendationengine v0.5.0/go.mod h1:E5756pJcVFeVgaQv3WNpImkFP8a+RptV6dDLGPILjvg= -cloud.google.com/go/recommendationengine v0.6.0/go.mod h1:08mq2umu9oIqc7tDy8sx+MNJdLG0fUi3vaSVbztHgJ4= -cloud.google.com/go/recommendationengine v0.7.0/go.mod h1:1reUcE3GIu6MeBz/h5xZJqNLuuVjNg1lmWMPyjatzac= -cloud.google.com/go/recommendationengine v0.8.1/go.mod h1:MrZihWwtFYWDzE6Hz5nKcNz3gLizXVIDI/o3G1DLcrE= -cloud.google.com/go/recommendationengine v0.8.2/go.mod h1:QIybYHPK58qir9CV2ix/re/M//Ty10OxjnnhWdaKS1Y= -cloud.google.com/go/recommender v1.5.0/go.mod h1:jdoeiBIVrJe9gQjwd759ecLJbxCDED4A6p+mqoqDvTg= -cloud.google.com/go/recommender v1.6.0/go.mod h1:+yETpm25mcoiECKh9DEScGzIRyDKpZ0cEhWGo+8bo+c= -cloud.google.com/go/recommender v1.7.0/go.mod h1:XLHs/W+T8olwlGOgfQenXBTbIseGclClff6lhFVe9Bs= -cloud.google.com/go/recommender v1.8.0/go.mod h1:PkjXrTT05BFKwxaUxQmtIlrtj0kph108r02ZZQ5FE70= -cloud.google.com/go/recommender v1.9.0/go.mod h1:PnSsnZY7q+VL1uax2JWkt/UegHssxjUVVCrX52CuEmQ= -cloud.google.com/go/recommender v1.10.1/go.mod h1:XFvrE4Suqn5Cq0Lf+mCP6oBHD/yRMA8XxP5sb7Q7gpA= -cloud.google.com/go/recommender v1.11.0/go.mod h1:kPiRQhPyTJ9kyXPCG6u/dlPLbYfFlkwHNRwdzPVAoII= -cloud.google.com/go/recommender v1.11.1/go.mod h1:sGwFFAyI57v2Hc5LbIj+lTwXipGu9NW015rkaEM5B18= -cloud.google.com/go/redis v1.7.0/go.mod h1:V3x5Jq1jzUcg+UNsRvdmsfuFnit1cfe3Z/PGyq/lm4Y= -cloud.google.com/go/redis v1.8.0/go.mod h1:Fm2szCDavWzBk2cDKxrkmWBqoCiL1+Ctwq7EyqBCA/A= -cloud.google.com/go/redis v1.9.0/go.mod h1:HMYQuajvb2D0LvMgZmLDZW8V5aOC/WxstZHiy4g8OiA= -cloud.google.com/go/redis v1.10.0/go.mod h1:ThJf3mMBQtW18JzGgh41/Wld6vnDDc/F/F35UolRZPM= -cloud.google.com/go/redis v1.11.0/go.mod h1:/X6eicana+BWcUda5PpwZC48o37SiFVTFSs0fWAJ7uQ= -cloud.google.com/go/redis v1.13.1/go.mod h1:VP7DGLpE91M6bcsDdMuyCm2hIpB6Vp2hI090Mfd1tcg= -cloud.google.com/go/redis v1.13.2/go.mod h1:0Hg7pCMXS9uz02q+LoEVl5dNHUkIQv+C/3L76fandSA= -cloud.google.com/go/resourcemanager v1.3.0/go.mod h1:bAtrTjZQFJkiWTPDb1WBjzvc6/kifjj4QBYuKCCoqKA= -cloud.google.com/go/resourcemanager v1.4.0/go.mod h1:MwxuzkumyTX7/a3n37gmsT3py7LIXwrShilPh3P1tR0= -cloud.google.com/go/resourcemanager v1.5.0/go.mod h1:eQoXNAiAvCf5PXxWxXjhKQoTMaUSNrEfg+6qdf/wots= -cloud.google.com/go/resourcemanager v1.6.0/go.mod h1:YcpXGRs8fDzcUl1Xw8uOVmI8JEadvhRIkoXXUNVYcVo= -cloud.google.com/go/resourcemanager v1.7.0/go.mod h1:HlD3m6+bwhzj9XCouqmeiGuni95NTrExfhoSrkC/3EI= -cloud.google.com/go/resourcemanager v1.9.1/go.mod h1:dVCuosgrh1tINZ/RwBufr8lULmWGOkPS8gL5gqyjdT8= -cloud.google.com/go/resourcemanager v1.9.2/go.mod h1:OujkBg1UZg5lX2yIyMo5Vz9O5hf7XQOSV7WxqxxMtQE= -cloud.google.com/go/resourcesettings v1.3.0/go.mod h1:lzew8VfESA5DQ8gdlHwMrqZs1S9V87v3oCnKCWoOuQU= -cloud.google.com/go/resourcesettings v1.4.0/go.mod h1:ldiH9IJpcrlC3VSuCGvjR5of/ezRrOxFtpJoJo5SmXg= -cloud.google.com/go/resourcesettings v1.5.0/go.mod h1:+xJF7QSG6undsQDfsCJyqWXyBwUoJLhetkRMDRnIoXA= -cloud.google.com/go/resourcesettings v1.6.1/go.mod h1:M7mk9PIZrC5Fgsu1kZJci6mpgN8o0IUzVx3eJU3y4Jw= -cloud.google.com/go/resourcesettings v1.6.2/go.mod h1:mJIEDd9MobzunWMeniaMp6tzg4I2GvD3TTmPkc8vBXk= -cloud.google.com/go/retail v1.8.0/go.mod h1:QblKS8waDmNUhghY2TI9O3JLlFk8jybHeV4BF19FrE4= -cloud.google.com/go/retail v1.9.0/go.mod h1:g6jb6mKuCS1QKnH/dpu7isX253absFl6iE92nHwlBUY= -cloud.google.com/go/retail v1.10.0/go.mod h1:2gDk9HsL4HMS4oZwz6daui2/jmKvqShXKQuB2RZ+cCc= -cloud.google.com/go/retail v1.11.0/go.mod h1:MBLk1NaWPmh6iVFSz9MeKG/Psyd7TAgm6y/9L2B4x9Y= -cloud.google.com/go/retail v1.12.0/go.mod h1:UMkelN/0Z8XvKymXFbD4EhFJlYKRx1FGhQkVPU5kF14= -cloud.google.com/go/retail v1.14.1/go.mod h1:y3Wv3Vr2k54dLNIrCzenyKG8g8dhvhncT2NcNjb/6gE= -cloud.google.com/go/retail v1.14.2/go.mod h1:W7rrNRChAEChX336QF7bnMxbsjugcOCPU44i5kbLiL8= -cloud.google.com/go/run v0.2.0/go.mod h1:CNtKsTA1sDcnqqIFR3Pb5Tq0usWxJJvsWOCPldRU3Do= -cloud.google.com/go/run v0.3.0/go.mod h1:TuyY1+taHxTjrD0ZFk2iAR+xyOXEA0ztb7U3UNA0zBo= -cloud.google.com/go/run v0.8.0/go.mod h1:VniEnuBwqjigv0A7ONfQUaEItaiCRVujlMqerPPiktM= -cloud.google.com/go/run v0.9.0/go.mod h1:Wwu+/vvg8Y+JUApMwEDfVfhetv30hCG4ZwDR/IXl2Qg= -cloud.google.com/go/run v1.2.0/go.mod h1:36V1IlDzQ0XxbQjUx6IYbw8H3TJnWvhii963WW3B/bo= -cloud.google.com/go/run v1.3.0/go.mod h1:S/osX/4jIPZGg+ssuqh6GNgg7syixKe3YnprwehzHKU= -cloud.google.com/go/run v1.3.1/go.mod h1:cymddtZOzdwLIAsmS6s+Asl4JoXIDm/K1cpZTxV4Q5s= -cloud.google.com/go/scheduler v1.4.0/go.mod h1:drcJBmxF3aqZJRhmkHQ9b3uSSpQoltBPGPxGAWROx6s= -cloud.google.com/go/scheduler v1.5.0/go.mod h1:ri073ym49NW3AfT6DZi21vLZrG07GXr5p3H1KxN5QlI= -cloud.google.com/go/scheduler v1.6.0/go.mod h1:SgeKVM7MIwPn3BqtcBntpLyrIJftQISRrYB5ZtT+KOk= -cloud.google.com/go/scheduler v1.7.0/go.mod h1:jyCiBqWW956uBjjPMMuX09n3x37mtyPJegEWKxRsn44= -cloud.google.com/go/scheduler v1.8.0/go.mod h1:TCET+Y5Gp1YgHT8py4nlg2Sew8nUHMqcpousDgXJVQc= -cloud.google.com/go/scheduler v1.9.0/go.mod h1:yexg5t+KSmqu+njTIh3b7oYPheFtBWGcbVUYF1GGMIc= -cloud.google.com/go/scheduler v1.10.1/go.mod h1:R63Ldltd47Bs4gnhQkmNDse5w8gBRrhObZ54PxgR2Oo= -cloud.google.com/go/scheduler v1.10.2/go.mod h1:O3jX6HRH5eKCA3FutMw375XHZJudNIKVonSCHv7ropY= -cloud.google.com/go/secretmanager v1.6.0/go.mod h1:awVa/OXF6IiyaU1wQ34inzQNc4ISIDIrId8qE5QGgKA= -cloud.google.com/go/secretmanager v1.8.0/go.mod h1:hnVgi/bN5MYHd3Gt0SPuTPPp5ENina1/LxM+2W9U9J4= -cloud.google.com/go/secretmanager v1.9.0/go.mod h1:b71qH2l1yHmWQHt9LC80akm86mX8AL6X1MA01dW8ht4= -cloud.google.com/go/secretmanager v1.10.0/go.mod h1:MfnrdvKMPNra9aZtQFvBcvRU54hbPD8/HayQdlUgJpU= -cloud.google.com/go/secretmanager v1.11.1/go.mod h1:znq9JlXgTNdBeQk9TBW/FnR/W4uChEKGeqQWAJ8SXFw= -cloud.google.com/go/secretmanager v1.11.2/go.mod h1:MQm4t3deoSub7+WNwiC4/tRYgDBHJgJPvswqQVB1Vss= -cloud.google.com/go/security v1.5.0/go.mod h1:lgxGdyOKKjHL4YG3/YwIL2zLqMFCKs0UbQwgyZmfJl4= -cloud.google.com/go/security v1.7.0/go.mod h1:mZklORHl6Bg7CNnnjLH//0UlAlaXqiG7Lb9PsPXLfD0= -cloud.google.com/go/security v1.8.0/go.mod h1:hAQOwgmaHhztFhiQ41CjDODdWP0+AE1B3sX4OFlq+GU= -cloud.google.com/go/security v1.9.0/go.mod h1:6Ta1bO8LXI89nZnmnsZGp9lVoVWXqsVbIq/t9dzI+2Q= -cloud.google.com/go/security v1.10.0/go.mod h1:QtOMZByJVlibUT2h9afNDWRZ1G96gVywH8T5GUSb9IA= -cloud.google.com/go/security v1.12.0/go.mod h1:rV6EhrpbNHrrxqlvW0BWAIawFWq3X90SduMJdFwtLB8= -cloud.google.com/go/security v1.13.0/go.mod h1:Q1Nvxl1PAgmeW0y3HTt54JYIvUdtcpYKVfIB8AOMZ+0= -cloud.google.com/go/security v1.15.1/go.mod h1:MvTnnbsWnehoizHi09zoiZob0iCHVcL4AUBj76h9fXA= -cloud.google.com/go/security v1.15.2/go.mod h1:2GVE/v1oixIRHDaClVbHuPcZwAqFM28mXuAKCfMgYIg= -cloud.google.com/go/securitycenter v1.13.0/go.mod h1:cv5qNAqjY84FCN6Y9z28WlkKXyWsgLO832YiWwkCWcU= -cloud.google.com/go/securitycenter v1.14.0/go.mod h1:gZLAhtyKv85n52XYWt6RmeBdydyxfPeTrpToDPw4Auc= -cloud.google.com/go/securitycenter v1.15.0/go.mod h1:PeKJ0t8MoFmmXLXWm41JidyzI3PJjd8sXWaVqg43WWk= -cloud.google.com/go/securitycenter v1.16.0/go.mod h1:Q9GMaLQFUD+5ZTabrbujNWLtSLZIZF7SAR0wWECrjdk= -cloud.google.com/go/securitycenter v1.18.1/go.mod h1:0/25gAzCM/9OL9vVx4ChPeM/+DlfGQJDwBy/UC8AKK0= -cloud.google.com/go/securitycenter v1.19.0/go.mod h1:LVLmSg8ZkkyaNy4u7HCIshAngSQ8EcIRREP3xBnyfag= -cloud.google.com/go/securitycenter v1.23.0/go.mod h1:8pwQ4n+Y9WCWM278R8W3nF65QtY172h4S8aXyI9/hsQ= -cloud.google.com/go/securitycenter v1.23.1/go.mod h1:w2HV3Mv/yKhbXKwOCu2i8bCuLtNP1IMHuiYQn4HJq5s= -cloud.google.com/go/servicecontrol v1.4.0/go.mod h1:o0hUSJ1TXJAmi/7fLJAedOovnujSEvjKCAFNXPQ1RaU= -cloud.google.com/go/servicecontrol v1.5.0/go.mod h1:qM0CnXHhyqKVuiZnGKrIurvVImCs8gmqWsDoqe9sU1s= -cloud.google.com/go/servicecontrol v1.10.0/go.mod h1:pQvyvSRh7YzUF2efw7H87V92mxU8FnFDawMClGCNuAA= -cloud.google.com/go/servicecontrol v1.11.0/go.mod h1:kFmTzYzTUIuZs0ycVqRHNaNhgR+UMUpw9n02l/pY+mc= -cloud.google.com/go/servicecontrol v1.11.1/go.mod h1:aSnNNlwEFBY+PWGQ2DoM0JJ/QUXqV5/ZD9DOLB7SnUk= -cloud.google.com/go/servicedirectory v1.4.0/go.mod h1:gH1MUaZCgtP7qQiI+F+A+OpeKF/HQWgtAddhTbhL2bs= -cloud.google.com/go/servicedirectory v1.5.0/go.mod h1:QMKFL0NUySbpZJ1UZs3oFAmdvVxhhxB6eJ/Vlp73dfg= -cloud.google.com/go/servicedirectory v1.6.0/go.mod h1:pUlbnWsLH9c13yGkxCmfumWEPjsRs1RlmJ4pqiNjVL4= -cloud.google.com/go/servicedirectory v1.7.0/go.mod h1:5p/U5oyvgYGYejufvxhgwjL8UVXjkuw7q5XcG10wx1U= -cloud.google.com/go/servicedirectory v1.8.0/go.mod h1:srXodfhY1GFIPvltunswqXpVxFPpZjf8nkKQT7XcXaY= -cloud.google.com/go/servicedirectory v1.9.0/go.mod h1:29je5JjiygNYlmsGz8k6o+OZ8vd4f//bQLtvzkPPT/s= -cloud.google.com/go/servicedirectory v1.10.1/go.mod h1:Xv0YVH8s4pVOwfM/1eMTl0XJ6bzIOSLDt8f8eLaGOxQ= -cloud.google.com/go/servicedirectory v1.11.0/go.mod h1:Xv0YVH8s4pVOwfM/1eMTl0XJ6bzIOSLDt8f8eLaGOxQ= -cloud.google.com/go/servicedirectory v1.11.1/go.mod h1:tJywXimEWzNzw9FvtNjsQxxJ3/41jseeILgwU/QLrGI= -cloud.google.com/go/servicemanagement v1.4.0/go.mod h1:d8t8MDbezI7Z2R1O/wu8oTggo3BI2GKYbdG4y/SJTco= -cloud.google.com/go/servicemanagement v1.5.0/go.mod h1:XGaCRe57kfqu4+lRxaFEAuqmjzF0r+gWHjWqKqBvKFo= -cloud.google.com/go/servicemanagement v1.6.0/go.mod h1:aWns7EeeCOtGEX4OvZUWCCJONRZeFKiptqKf1D0l/Jc= -cloud.google.com/go/servicemanagement v1.8.0/go.mod h1:MSS2TDlIEQD/fzsSGfCdJItQveu9NXnUniTrq/L8LK4= -cloud.google.com/go/serviceusage v1.3.0/go.mod h1:Hya1cozXM4SeSKTAgGXgj97GlqUvF5JaoXacR1JTP/E= -cloud.google.com/go/serviceusage v1.4.0/go.mod h1:SB4yxXSaYVuUBYUml6qklyONXNLt83U0Rb+CXyhjEeU= -cloud.google.com/go/serviceusage v1.5.0/go.mod h1:w8U1JvqUqwJNPEOTQjrMHkw3IaIFLoLsPLvsE3xueec= -cloud.google.com/go/serviceusage v1.6.0/go.mod h1:R5wwQcbOWsyuOfbP9tGdAnCAc6B9DRwPG1xtWMDeuPA= -cloud.google.com/go/shell v1.3.0/go.mod h1:VZ9HmRjZBsjLGXusm7K5Q5lzzByZmJHf1d0IWHEN5X4= -cloud.google.com/go/shell v1.4.0/go.mod h1:HDxPzZf3GkDdhExzD/gs8Grqk+dmYcEjGShZgYa9URw= -cloud.google.com/go/shell v1.6.0/go.mod h1:oHO8QACS90luWgxP3N9iZVuEiSF84zNyLytb+qE2f9A= -cloud.google.com/go/shell v1.7.1/go.mod h1:u1RaM+huXFaTojTbW4g9P5emOrrmLE69KrxqQahKn4g= -cloud.google.com/go/shell v1.7.2/go.mod h1:KqRPKwBV0UyLickMn0+BY1qIyE98kKyI216sH/TuHmc= -cloud.google.com/go/spanner v1.41.0/go.mod h1:MLYDBJR/dY4Wt7ZaMIQ7rXOTLjYrmxLE/5ve9vFfWos= -cloud.google.com/go/spanner v1.44.0/go.mod h1:G8XIgYdOK+Fbcpbs7p2fiprDw4CaZX63whnSMLVBxjk= -cloud.google.com/go/spanner v1.45.0/go.mod h1:FIws5LowYz8YAE1J8fOS7DJup8ff7xJeetWEo5REA2M= -cloud.google.com/go/spanner v1.47.0/go.mod h1:IXsJwVW2j4UKs0eYDqodab6HgGuA1bViSqW4uH9lfUI= -cloud.google.com/go/spanner v1.49.0/go.mod h1:eGj9mQGK8+hkgSVbHNQ06pQ4oS+cyc4tXXd6Dif1KoM= -cloud.google.com/go/spanner v1.50.0/go.mod h1:eGj9mQGK8+hkgSVbHNQ06pQ4oS+cyc4tXXd6Dif1KoM= -cloud.google.com/go/speech v1.6.0/go.mod h1:79tcr4FHCimOp56lwC01xnt/WPJZc4v3gzyT7FoBkCM= -cloud.google.com/go/speech v1.7.0/go.mod h1:KptqL+BAQIhMsj1kOP2la5DSEEerPDuOP/2mmkhHhZQ= -cloud.google.com/go/speech v1.8.0/go.mod h1:9bYIl1/tjsAnMgKGHKmBZzXKEkGgtU+MpdDPTE9f7y0= -cloud.google.com/go/speech v1.9.0/go.mod h1:xQ0jTcmnRFFM2RfX/U+rk6FQNUF6DQlydUSyoooSpco= -cloud.google.com/go/speech v1.14.1/go.mod h1:gEosVRPJ9waG7zqqnsHpYTOoAS4KouMRLDFMekpJ0J0= -cloud.google.com/go/speech v1.15.0/go.mod h1:y6oH7GhqCaZANH7+Oe0BhgIogsNInLlz542tg3VqeYI= -cloud.google.com/go/speech v1.17.1/go.mod h1:8rVNzU43tQvxDaGvqOhpDqgkJTFowBpDvCJ14kGlJYo= -cloud.google.com/go/speech v1.19.0/go.mod h1:8rVNzU43tQvxDaGvqOhpDqgkJTFowBpDvCJ14kGlJYo= -cloud.google.com/go/speech v1.19.1/go.mod h1:WcuaWz/3hOlzPFOVo9DUsblMIHwxP589y6ZMtaG+iAA= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= -cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= -cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc= -cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= -cloud.google.com/go/storage v1.28.1/go.mod h1:Qnisd4CqDdo6BGs2AD5LLnEsmSQ80wQ5ogcBBKhU86Y= -cloud.google.com/go/storage v1.29.0/go.mod h1:4puEjyTKnku6gfKoTfNOU/W+a9JyuVNxjpS5GBrB8h4= -cloud.google.com/go/storage v1.30.1/go.mod h1:NfxhC0UJE1aXSx7CIIbCf7y9HKT7BiccwkR7+P7gN8E= cloud.google.com/go/storage v1.37.0 h1:WI8CsaFO8Q9KjPVtsZ5Cmi0dXV25zMoX0FklT7c3Jm4= cloud.google.com/go/storage v1.37.0/go.mod h1:i34TiT2IhiNDmcj65PqwCjcoUX7Z5pLzS8DEmoiFq1k= -cloud.google.com/go/storagetransfer v1.5.0/go.mod h1:dxNzUopWy7RQevYFHewchb29POFv3/AaBgnhqzqiK0w= -cloud.google.com/go/storagetransfer v1.6.0/go.mod h1:y77xm4CQV/ZhFZH75PLEXY0ROiS7Gh6pSKrM8dJyg6I= -cloud.google.com/go/storagetransfer v1.7.0/go.mod h1:8Giuj1QNb1kfLAiWM1bN6dHzfdlDAVC9rv9abHot2W4= -cloud.google.com/go/storagetransfer v1.8.0/go.mod h1:JpegsHHU1eXg7lMHkvf+KE5XDJ7EQu0GwNJbbVGanEw= -cloud.google.com/go/storagetransfer v1.10.0/go.mod h1:DM4sTlSmGiNczmV6iZyceIh2dbs+7z2Ayg6YAiQlYfA= -cloud.google.com/go/storagetransfer v1.10.1/go.mod h1:rS7Sy0BtPviWYTTJVWCSV4QrbBitgPeuK4/FKa4IdLs= -cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw= -cloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g= -cloud.google.com/go/talent v1.3.0/go.mod h1:CmcxwJ/PKfRgd1pBjQgU6W3YBwiewmUzQYH5HHmSCmM= -cloud.google.com/go/talent v1.4.0/go.mod h1:ezFtAgVuRf8jRsvyE6EwmbTK5LKciD4KVnHuDEFmOOA= -cloud.google.com/go/talent v1.5.0/go.mod h1:G+ODMj9bsasAEJkQSzO2uHQWXHHXUomArjWQQYkqK6c= -cloud.google.com/go/talent v1.6.2/go.mod h1:CbGvmKCG61mkdjcqTcLOkb2ZN1SrQI8MDyma2l7VD24= -cloud.google.com/go/talent v1.6.3/go.mod h1:xoDO97Qd4AK43rGjJvyBHMskiEf3KulgYzcH6YWOVoo= -cloud.google.com/go/texttospeech v1.4.0/go.mod h1:FX8HQHA6sEpJ7rCMSfXuzBcysDAuWusNNNvN9FELDd8= -cloud.google.com/go/texttospeech v1.5.0/go.mod h1:oKPLhR4n4ZdQqWKURdwxMy0uiTS1xU161C8W57Wkea4= -cloud.google.com/go/texttospeech v1.6.0/go.mod h1:YmwmFT8pj1aBblQOI3TfKmwibnsfvhIBzPXcW4EBovc= -cloud.google.com/go/texttospeech v1.7.1/go.mod h1:m7QfG5IXxeneGqTapXNxv2ItxP/FS0hCZBwXYqucgSk= -cloud.google.com/go/texttospeech v1.7.2/go.mod h1:VYPT6aTOEl3herQjFHYErTlSZJ4vB00Q2ZTmuVgluD4= -cloud.google.com/go/tpu v1.3.0/go.mod h1:aJIManG0o20tfDQlRIej44FcwGGl/cD0oiRyMKG19IQ= -cloud.google.com/go/tpu v1.4.0/go.mod h1:mjZaX8p0VBgllCzF6wcU2ovUXN9TONFLd7iz227X2Xg= -cloud.google.com/go/tpu v1.5.0/go.mod h1:8zVo1rYDFuW2l4yZVY0R0fb/v44xLh3llq7RuV61fPM= -cloud.google.com/go/tpu v1.6.1/go.mod h1:sOdcHVIgDEEOKuqUoi6Fq53MKHJAtOwtz0GuKsWSH3E= -cloud.google.com/go/tpu v1.6.2/go.mod h1:NXh3NDwt71TsPZdtGWgAG5ThDfGd32X1mJ2cMaRlVgU= -cloud.google.com/go/trace v1.3.0/go.mod h1:FFUE83d9Ca57C+K8rDl/Ih8LwOzWIV1krKgxg6N0G28= -cloud.google.com/go/trace v1.4.0/go.mod h1:UG0v8UBqzusp+z63o7FK74SdFE+AXpCLdFb1rshXG+Y= -cloud.google.com/go/trace v1.8.0/go.mod h1:zH7vcsbAhklH8hWFig58HvxcxyQbaIqMarMg9hn5ECA= -cloud.google.com/go/trace v1.9.0/go.mod h1:lOQqpE5IaWY0Ixg7/r2SjixMuc6lfTFeO4QGM4dQWOk= -cloud.google.com/go/trace v1.10.1/go.mod h1:gbtL94KE5AJLH3y+WVpfWILmqgc6dXcqgNXdOPAQTYk= -cloud.google.com/go/trace v1.10.2/go.mod h1:NPXemMi6MToRFcSxRl2uDnu/qAlAQ3oULUphcHGh1vA= -cloud.google.com/go/translate v1.3.0/go.mod h1:gzMUwRjvOqj5i69y/LYLd8RrNQk+hOmIXTi9+nb3Djs= -cloud.google.com/go/translate v1.4.0/go.mod h1:06Dn/ppvLD6WvA5Rhdp029IX2Mi3Mn7fpMRLPvXT5Wg= -cloud.google.com/go/translate v1.5.0/go.mod h1:29YDSYveqqpA1CQFD7NQuP49xymq17RXNaUDdc0mNu0= -cloud.google.com/go/translate v1.6.0/go.mod h1:lMGRudH1pu7I3n3PETiOB2507gf3HnfLV8qlkHZEyos= -cloud.google.com/go/translate v1.7.0/go.mod h1:lMGRudH1pu7I3n3PETiOB2507gf3HnfLV8qlkHZEyos= -cloud.google.com/go/translate v1.8.1/go.mod h1:d1ZH5aaOA0CNhWeXeC8ujd4tdCFw8XoNWRljklu5RHs= -cloud.google.com/go/translate v1.8.2/go.mod h1:d1ZH5aaOA0CNhWeXeC8ujd4tdCFw8XoNWRljklu5RHs= -cloud.google.com/go/translate v1.9.0/go.mod h1:d1ZH5aaOA0CNhWeXeC8ujd4tdCFw8XoNWRljklu5RHs= -cloud.google.com/go/translate v1.9.1/go.mod h1:TWIgDZknq2+JD4iRcojgeDtqGEp154HN/uL6hMvylS8= -cloud.google.com/go/video v1.8.0/go.mod h1:sTzKFc0bUSByE8Yoh8X0mn8bMymItVGPfTuUBUyRgxk= -cloud.google.com/go/video v1.9.0/go.mod h1:0RhNKFRF5v92f8dQt0yhaHrEuH95m068JYOvLZYnJSw= -cloud.google.com/go/video v1.12.0/go.mod h1:MLQew95eTuaNDEGriQdcYn0dTwf9oWiA4uYebxM5kdg= -cloud.google.com/go/video v1.13.0/go.mod h1:ulzkYlYgCp15N2AokzKjy7MQ9ejuynOJdf1tR5lGthk= -cloud.google.com/go/video v1.14.0/go.mod h1:SkgaXwT+lIIAKqWAJfktHT/RbgjSuY6DobxEp0C5yTQ= -cloud.google.com/go/video v1.15.0/go.mod h1:SkgaXwT+lIIAKqWAJfktHT/RbgjSuY6DobxEp0C5yTQ= -cloud.google.com/go/video v1.17.1/go.mod h1:9qmqPqw/Ib2tLqaeHgtakU+l5TcJxCJbhFXM7UJjVzU= -cloud.google.com/go/video v1.19.0/go.mod h1:9qmqPqw/Ib2tLqaeHgtakU+l5TcJxCJbhFXM7UJjVzU= -cloud.google.com/go/video v1.20.0/go.mod h1:U3G3FTnsvAGqglq9LxgqzOiBc/Nt8zis8S+850N2DUM= -cloud.google.com/go/video v1.20.1/go.mod h1:3gJS+iDprnj8SY6pe0SwLeC5BUW80NjhwX7INWEuWGU= -cloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU= -cloud.google.com/go/videointelligence v1.7.0/go.mod h1:k8pI/1wAhjznARtVT9U1llUaFNPh7muw8QyOUpavru4= -cloud.google.com/go/videointelligence v1.8.0/go.mod h1:dIcCn4gVDdS7yte/w+koiXn5dWVplOZkE+xwG9FgK+M= -cloud.google.com/go/videointelligence v1.9.0/go.mod h1:29lVRMPDYHikk3v8EdPSaL8Ku+eMzDljjuvRs105XoU= -cloud.google.com/go/videointelligence v1.10.0/go.mod h1:LHZngX1liVtUhZvi2uNS0VQuOzNi2TkY1OakiuoUOjU= -cloud.google.com/go/videointelligence v1.11.1/go.mod h1:76xn/8InyQHarjTWsBR058SmlPCwQjgcvoW0aZykOvo= -cloud.google.com/go/videointelligence v1.11.2/go.mod h1:ocfIGYtIVmIcWk1DsSGOoDiXca4vaZQII1C85qtoplc= -cloud.google.com/go/vision v1.2.0/go.mod h1:SmNwgObm5DpFBme2xpyOyasvBc1aPdjvMk2bBk0tKD0= -cloud.google.com/go/vision/v2 v2.2.0/go.mod h1:uCdV4PpN1S0jyCyq8sIM42v2Y6zOLkZs+4R9LrGYwFo= -cloud.google.com/go/vision/v2 v2.3.0/go.mod h1:UO61abBx9QRMFkNBbf1D8B1LXdS2cGiiCRx0vSpZoUo= -cloud.google.com/go/vision/v2 v2.4.0/go.mod h1:VtI579ll9RpVTrdKdkMzckdnwMyX2JILb+MhPqRbPsY= -cloud.google.com/go/vision/v2 v2.5.0/go.mod h1:MmaezXOOE+IWa+cS7OhRRLK2cNv1ZL98zhqFFZaaH2E= -cloud.google.com/go/vision/v2 v2.6.0/go.mod h1:158Hes0MvOS9Z/bDMSFpjwsUrZ5fPrdwuyyvKSGAGMY= -cloud.google.com/go/vision/v2 v2.7.0/go.mod h1:H89VysHy21avemp6xcf9b9JvZHVehWbET0uT/bcuY/0= -cloud.google.com/go/vision/v2 v2.7.2/go.mod h1:jKa8oSYBWhYiXarHPvP4USxYANYUEdEsQrloLjrSwJU= -cloud.google.com/go/vision/v2 v2.7.3/go.mod h1:V0IcLCY7W+hpMKXK1JYE0LV5llEqVmj+UJChjvA1WsM= -cloud.google.com/go/vmmigration v1.2.0/go.mod h1:IRf0o7myyWFSmVR1ItrBSFLFD/rJkfDCUTO4vLlJvsE= -cloud.google.com/go/vmmigration v1.3.0/go.mod h1:oGJ6ZgGPQOFdjHuocGcLqX4lc98YQ7Ygq8YQwHh9A7g= -cloud.google.com/go/vmmigration v1.5.0/go.mod h1:E4YQ8q7/4W9gobHjQg4JJSgXXSgY21nA5r8swQV+Xxc= -cloud.google.com/go/vmmigration v1.6.0/go.mod h1:bopQ/g4z+8qXzichC7GW1w2MjbErL54rk3/C843CjfY= -cloud.google.com/go/vmmigration v1.7.1/go.mod h1:WD+5z7a/IpZ5bKK//YmT9E047AD+rjycCAvyMxGJbro= -cloud.google.com/go/vmmigration v1.7.2/go.mod h1:iA2hVj22sm2LLYXGPT1pB63mXHhrH1m/ruux9TwWLd8= -cloud.google.com/go/vmwareengine v0.1.0/go.mod h1:RsdNEf/8UDvKllXhMz5J40XxDrNJNN4sagiox+OI208= -cloud.google.com/go/vmwareengine v0.2.2/go.mod h1:sKdctNJxb3KLZkE/6Oui94iw/xs9PRNC2wnNLXsHvH8= -cloud.google.com/go/vmwareengine v0.3.0/go.mod h1:wvoyMvNWdIzxMYSpH/R7y2h5h3WFkx6d+1TIsP39WGY= -cloud.google.com/go/vmwareengine v0.4.1/go.mod h1:Px64x+BvjPZwWuc4HdmVhoygcXqEkGHXoa7uyfTgSI0= -cloud.google.com/go/vmwareengine v1.0.0/go.mod h1:Px64x+BvjPZwWuc4HdmVhoygcXqEkGHXoa7uyfTgSI0= -cloud.google.com/go/vmwareengine v1.0.1/go.mod h1:aT3Xsm5sNx0QShk1Jc1B8OddrxAScYLwzVoaiXfdzzk= -cloud.google.com/go/vpcaccess v1.4.0/go.mod h1:aQHVbTWDYUR1EbTApSVvMq1EnT57ppDmQzZ3imqIk4w= -cloud.google.com/go/vpcaccess v1.5.0/go.mod h1:drmg4HLk9NkZpGfCmZ3Tz0Bwnm2+DKqViEpeEpOq0m8= -cloud.google.com/go/vpcaccess v1.6.0/go.mod h1:wX2ILaNhe7TlVa4vC5xce1bCnqE3AeH27RV31lnmZes= -cloud.google.com/go/vpcaccess v1.7.1/go.mod h1:FogoD46/ZU+JUBX9D606X21EnxiszYi2tArQwLY4SXs= -cloud.google.com/go/vpcaccess v1.7.2/go.mod h1:mmg/MnRHv+3e8FJUjeSibVFvQF1cCy2MsFaFqxeY1HU= -cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xXZmFiHmGE= -cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg= -cloud.google.com/go/webrisk v1.6.0/go.mod h1:65sW9V9rOosnc9ZY7A7jsy1zoHS5W9IAXv6dGqhMQMc= -cloud.google.com/go/webrisk v1.7.0/go.mod h1:mVMHgEYH0r337nmt1JyLthzMr6YxwN1aAIEc2fTcq7A= -cloud.google.com/go/webrisk v1.8.0/go.mod h1:oJPDuamzHXgUc+b8SiHRcVInZQuybnvEW72PqTc7sSg= -cloud.google.com/go/webrisk v1.9.1/go.mod h1:4GCmXKcOa2BZcZPn6DCEvE7HypmEJcJkr4mtM+sqYPc= -cloud.google.com/go/webrisk v1.9.2/go.mod h1:pY9kfDgAqxUpDBOrG4w8deLfhvJmejKB0qd/5uQIPBc= -cloud.google.com/go/websecurityscanner v1.3.0/go.mod h1:uImdKm2wyeXQevQJXeh8Uun/Ym1VqworNDlBXQevGMo= -cloud.google.com/go/websecurityscanner v1.4.0/go.mod h1:ebit/Fp0a+FWu5j4JOmJEV8S8CzdTkAS77oDsiSqYWQ= -cloud.google.com/go/websecurityscanner v1.5.0/go.mod h1:Y6xdCPy81yi0SQnDY1xdNTNpfY1oAgXUlcfN3B3eSng= -cloud.google.com/go/websecurityscanner v1.6.1/go.mod h1:Njgaw3rttgRHXzwCB8kgCYqv5/rGpFCsBOvPbYgszpg= -cloud.google.com/go/websecurityscanner v1.6.2/go.mod h1:7YgjuU5tun7Eg2kpKgGnDuEOXWIrh8x8lWrJT4zfmas= -cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0= -cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M= -cloud.google.com/go/workflows v1.8.0/go.mod h1:ysGhmEajwZxGn1OhGOGKsTXc5PyxOc0vfKf5Af+to4M= -cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA= -cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcPALq2CxzdePw= -cloud.google.com/go/workflows v1.11.1/go.mod h1:Z+t10G1wF7h8LgdY/EmRcQY8ptBD/nvofaL6FqlET6g= -cloud.google.com/go/workflows v1.12.0/go.mod h1:PYhSk2b6DhZ508tj8HXKaBh+OFe+xdl0dHF/tJdzPQM= -cloud.google.com/go/workflows v1.12.1/go.mod h1:5A95OhD/edtOhQd/O741NSfIMezNTbCwLM1P1tBRGHM= -collectd.org v0.3.0/go.mod h1:A/8DzQBkF6abtvrT2j/AU/4tiBgJWYyh0y/oB/4MlWE= dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= -gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= -git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc= -github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= -github.com/Azure/azure-sdk-for-go/sdk/azcore v0.21.1/go.mod h1:fBF9PQNqB8scdgpZ3ufzaLntG0AG7C1WjPMsiFOmfHM= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.0.0/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.0.0/go.mod h1:+6sju8gk8FRmSajX3Oz4G5Gm7P+mbqE9FVaXXFYTkCM= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0/go.mod h1:OQeznEEkTZ9OrhHJoDD8ZDq51FHgXjqtP9z6bEwBq9U= -github.com/Azure/azure-sdk-for-go/sdk/internal v0.8.3/go.mod h1:KLF4gFr6DcKFZwSuH8w8yEK6DpFl3LP5rhdvAb7Yz5I= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal v1.0.0/go.mod h1:ceIuwmxDWptoW3eCqSXlnPsZFKh4X+R38dWPv7GS9Vs= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.0.0/go.mod h1:s1tW/At+xHqjNFvWU4G0c0Qv33KOhvbGNj0RCTQDV8s= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.2.0/go.mod h1:c+Lifp3EDEamAkPVzMooRNOK6CZjNSdEnf1A7jsI9u4= -github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.3.0/go.mod h1:tPaiy8S5bQ+S5sOiDlINkp7+Ef339+Nz5L5XO+cnOHo= -github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.2.0/go.mod h1:+6KLcKIVgxoBDMqMO/Nvy7bZ9a0nbU3I1DtFQK3YvB4= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= -github.com/AzureAD/microsoft-authentication-library-for-go v0.4.0/go.mod h1:Vt9sXTKwMyGcOxSmLDMnGPgqsUg7m8pe215qMLrDXw4= -github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0/go.mod h1:kgDmCTgBzIEPFElEF+FK0SdjAor06dRq2Go927dnQ6o= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/CloudyKit/fastprinter v0.0.0-20170127035650-74b38d55f37a/go.mod h1:EFZQ978U7x8IRnstaskI3IysnWY5Ao3QgZUKOXlsAdw= -github.com/CloudyKit/jet v2.1.3-0.20180809161101-62edd43e4f88+incompatible/go.mod h1:HPYO+50pSWkPoj9Q/eq0aRGByCL6ScRlUmiEX5Zgm+w= -github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= -github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8= github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= -github.com/GoogleCloudPlatform/cloudsql-proxy v0.0.0-20190129172621-c8b1d7a94ddf/go.mod h1:aJ4qN3TfrelA6NZ6AXsXRfmEVaYin3EDbSPJrKS8OXo= -github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= -github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk= -github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY= -github.com/Joker/jade v1.0.1-0.20190614124447-d475f43051e7/go.mod h1:6E6s8o2AE4KhCrqr6GRJjdC/gNfTdxkIXvuGZZda2VM= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= @@ -995,126 +45,66 @@ github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCv github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= github.com/SaveTheRbtz/mph v0.1.1-0.20240117162131-4166ec7869bc h1:DCHzPQOcU/7gwDTWbFQZc5qHMPS1g0xTO56k8NXsv9M= github.com/SaveTheRbtz/mph v0.1.1-0.20240117162131-4166ec7869bc/go.mod h1:LJM5a3zcIJ/8TmZwlUczvROEJT8ntOdhdG9jjcR1B0I= -github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0= -github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= 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.6.0/go.mod h1:0qHz5QP0GMX4pfmMA/zt5RgfNuXJrTP0zS7DqpHGGTw= -github.com/VictoriaMetrics/fastcache v1.12.1/go.mod h1:tX04vaqcNoQeGLD+ra5pU5sWkuxnzWhEzLwhP9w653o= github.com/VictoriaMetrics/fastcache v1.12.2 h1:N0y9ASrJ0F6h0QaC3o6uJb3NIZ9VKLjCM7NQbSmF7WI= github.com/VictoriaMetrics/fastcache v1.12.2/go.mod h1:AmC+Nzz1+3G2eCPapF6UcsnkThDcMsQicp4xDukwJYI= github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4= -github.com/aclements/go-gg v0.0.0-20170118225347-6dbb4e4fefb0/go.mod h1:55qNq4vcpkIuHowELi5C8e+1yUHtoLoOUR9QU5j7Tes= -github.com/aclements/go-moremath v0.0.0-20210112150236-f10218a38794/go.mod h1:7e+I0LQFUI9AXWxOfsQROs9xPhoJtbsyWcjJqDd4KPY= -github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= -github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY= -github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk= -github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= -github.com/ajstarks/svgo v0.0.0-20210923152817-c3b6e2f0c527/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= -github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= -github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= -github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/apache/arrow/go/arrow v0.0.0-20191024131854-af6fa24be0db/go.mod h1:VTxUBvSJ3s3eHAg65PNgrsn5BtqCRPdmyXh6rAfdxN0= -github.com/apache/arrow/go/v10 v10.0.1/go.mod h1:YvhnlEePVnBS4+0z3fhPfUy7W1Ikj0Ih0vcRo/gZ1M0= -github.com/apache/arrow/go/v11 v11.0.0/go.mod h1:Eg5OsL5H+e299f7u5ssuXsuHQVEGC4xei5aX110hRiI= -github.com/apache/arrow/go/v12 v12.0.0/go.mod h1:d+tV/eHZZ7Dz7RPrFKtPK02tpr+c9/PEd/zm8mDS9Vg= github.com/apache/arrow/go/v14 v14.0.2 h1:N8OkaJEOfI3mEZt07BIkvo4sC6XDbL+48MBPWO5IONw= github.com/apache/arrow/go/v14 v14.0.2/go.mod h1:u3fgh3EdgN/YQ8cVQRguVW3R+seMybFg8QBQ5LU+eBY= -github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/aws/aws-sdk-go-v2 v1.2.0/go.mod h1:zEQs02YRBw1DjK0PoJv3ygDYOFTre1ejlJWl8FwAuQo= github.com/aws/aws-sdk-go-v2 v1.9.0/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= -github.com/aws/aws-sdk-go-v2 v1.21.2/go.mod h1:ErQhvNuEMhJjweavOYhxVkn2RUx7kQXVATHrjKtxIpM= -github.com/aws/aws-sdk-go-v2 v1.23.1/go.mod h1:i1XDttT4rnf6vxc9AuskLc6s7XBee8rlLilKlc03uAA= github.com/aws/aws-sdk-go-v2 v1.27.0 h1:7bZWKoXhzI+mMR/HjdMx8ZCC5+6fY0lS5tr0bbgiLlo= github.com/aws/aws-sdk-go-v2 v1.27.0/go.mod h1:ffIFB97e2yNsv4aTSGkqtHnppsIJzw7G7BReUZ3jCXM= -github.com/aws/aws-sdk-go-v2/config v1.1.1/go.mod h1:0XsVy9lBI/BCXm+2Tuvt39YmdHwS5unDQmxZOYe8F5Y= github.com/aws/aws-sdk-go-v2/config v1.8.0/go.mod h1:w9+nMZ7soXCe5nT46Ri354SNhXDQ6v+V5wqDjnZE+GY= -github.com/aws/aws-sdk-go-v2/config v1.18.45/go.mod h1:ZwDUgFnQgsazQTnWfeLWk5GjeqTQTL8lMkoE1UXzxdE= -github.com/aws/aws-sdk-go-v2/config v1.25.5/go.mod h1:Bf4gDvy4ZcFIK0rqDu1wp9wrubNba2DojiPB2rt6nvI= github.com/aws/aws-sdk-go-v2/config v1.27.15 h1:uNnGLZ+DutuNEkuPh6fwqK7LpEiPmzb7MIMA1mNWEUc= github.com/aws/aws-sdk-go-v2/config v1.27.15/go.mod h1:7j7Kxx9/7kTmL7z4LlhwQe63MYEE5vkVV6nWg4ZAI8M= -github.com/aws/aws-sdk-go-v2/credentials v1.1.1/go.mod h1:mM2iIjwl7LULWtS6JCACyInboHirisUUdkBPoTHMOUo= github.com/aws/aws-sdk-go-v2/credentials v1.4.0/go.mod h1:dgGR+Qq7Wjcd4AOAW5Rf5Tnv3+x7ed6kETXyS9WCuAY= -github.com/aws/aws-sdk-go-v2/credentials v1.13.43/go.mod h1:zWJBz1Yf1ZtX5NGax9ZdNjhhI4rgjfgsyk6vTY1yfVg= -github.com/aws/aws-sdk-go-v2/credentials v1.16.4/go.mod h1:Kdh/okh+//vQ/AjEt81CjvkTo64+/zIE4OewP7RpfXk= github.com/aws/aws-sdk-go-v2/credentials v1.17.15 h1:YDexlvDRCA8ems2T5IP1xkMtOZ1uLJOCJdTr0igs5zo= github.com/aws/aws-sdk-go-v2/credentials v1.17.15/go.mod h1:vxHggqW6hFNaeNC0WyXS3VdyjcV0a4KMUY4dKJ96buU= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.0.2/go.mod h1:3hGg3PpiEjHnrkrlasTfxFqUsZ2GCk/fMUn4CbKgSkM= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.5.0/go.mod h1:CpNzHK9VEFUCknu50kkB8z58AH2B5DvPP7ea1LHve/Y= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.13/go.mod h1:f/Ib/qYjhV2/qdsf79H3QP/eRE4AkVyEf6sk7XfZ1tg= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.5/go.mod h1:VhnExhw6uXy9QzetvpXDolo1/hjhx4u9qukBGkuUwjs= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.3 h1:dQLK4TjtnlRGb0czOht2CevZ5l6RSyRWAnKeGd7VAFE= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.3/go.mod h1:TL79f2P6+8Q7dTsILpiVST+AL9lkF6PPGI167Ny0Cjw= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.5.1 h1:VGkV9KmhGqOQWnHyi4gLG98kE6OecT42fdrCGFWxJsc= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.5.1/go.mod h1:PLlnMiki//sGnCJiW+aVpvP/C8Kcm8mEj/IVm9+9qk4= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.43/go.mod h1:auo+PiyLl0n1l8A0e8RIeR8tOzYPfZZH/JNlrJ8igTQ= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.4/go.mod h1:xEhvbJcyUf/31yfGSQBe01fukXwXJ0gxDp7rLfymWE0= github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.7 h1:lf/8VTF2cM+N4SLzaYJERKEWAXq8MOMpZfU6wEPWsPk= github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.7/go.mod h1:4SjkU7QiqK2M9oozyMzfZ/23LmUY+h3oFqhdeP5OMiI= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.37/go.mod h1:Qe+2KtKml+FEsQF/DHmDV+xjtche/hwoF75EG4UlHW8= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.4/go.mod h1:dYvTNAggxDZy6y1AF7YDwXsPuHFy/VNEpEI/2dWK9IU= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.7 h1:4OYVp0705xu8yjdyoWix0r9wPIRXnIzzOoUpQVHIJ/g= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.7/go.mod h1:vd7ESTEvI76T2Na050gODNmNU7+OyKrIKroYTu4ABiI= github.com/aws/aws-sdk-go-v2/internal/ini v1.2.2/go.mod h1:BQV0agm+JEhqR+2RT5e1XTFIDcAAV0eW6z2trp+iduw= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.45/go.mod h1:lD5M20o09/LCuQ2mE62Mb/iSdSlCNuj6H5ci7tW7OsE= -github.com/aws/aws-sdk-go-v2/internal/ini v1.7.1/go.mod h1:6fQQgfuGmw8Al/3M2IgIllycxV7ZW7WCdVSqfBeUiCY= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.3.0/go.mod h1:v8ygadNyATSm6elwJ/4gzJwcFhri9RqS8skgHKiwXPU= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.1/go.mod h1:l9ymW25HOqymeU2m1gbUQ3rUIsTwKs8gYHXkqDQUhiI= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2 h1:Ji0DY1xUsUr3I8cHps0G+XM3WWU16lP6yG8qu1GAZAs= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2/go.mod h1:5CsjAbs3NlGQyZNFACh+zztPDI7fU6eW9QsxjfnuBKg= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.0.2/go.mod h1:45MfaXZ0cNbeuT0KQ1XJylq8A6+OpVV2E5kvY/Kq+u8= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.3.0/go.mod h1:R1KK+vY8AfalhG1AOu5e35pOD2SdoPKQCFLTvnxiohk= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.37/go.mod h1:vBmDnwWXWxNPFRMmG2m/3MKOe+xEcMDo1tanpaWCcck= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.4/go.mod h1:aYCGNjyUCUelhofxlZyj63srdxWUSsBSGg5l6MCuXuE= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.9 h1:Wx0rlZoEJR7JwlSZcHnEa7CNjrSIyVxMFWGAaXy4fJY= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.9/go.mod h1:aVMHdE0aHO3v+f/iw01fmXV/5DbfQ3Bi9nN7nd9bE9Y= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.7.0 h1:HWsM0YQWX76V6MOp07YuTYacm8k7h69ObJuw7Nck+og= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.7.0/go.mod h1:LKb3cKNQIMh+itGnEpKGcnL/6OIjPZqrtYah1w5f+3o= -github.com/aws/aws-sdk-go-v2/service/kms v1.26.3/go.mod h1:N3++/sLV97B8Zliz7KRqNcojOX7iMBZWKiuit5FKtH0= -github.com/aws/aws-sdk-go-v2/service/route53 v1.1.1/go.mod h1:rLiOUrPLW/Er5kRcQ7NkwbjlijluLsrIbu/iyl35RO4= -github.com/aws/aws-sdk-go-v2/service/route53 v1.30.2/go.mod h1:TQZBt/WaQy+zTHoW++rnl8JBrmZ0VO6EUbVua1+foCA= github.com/aws/aws-sdk-go-v2/service/s3 v1.15.0 h1:nPLfLPfglacc29Y949sDxpr3X/blaY40s3B85WT2yZU= github.com/aws/aws-sdk-go-v2/service/s3 v1.15.0/go.mod h1:Iv2aJVtVSm/D22rFoX99cLG4q4uB7tppuCsulGe98k4= -github.com/aws/aws-sdk-go-v2/service/sso v1.1.1/go.mod h1:SuZJxklHxLAXgLTc1iFXbEWkXs7QRTQpCLGaKIprQW0= github.com/aws/aws-sdk-go-v2/service/sso v1.4.0/go.mod h1:+1fpWnL96DL23aXPpMGbsmKe8jLTEfbjuQoA4WS1VaA= -github.com/aws/aws-sdk-go-v2/service/sso v1.15.2/go.mod h1:gsL4keucRCgW+xA85ALBpRFfdSLH4kHOVSnLMSuBECo= -github.com/aws/aws-sdk-go-v2/service/sso v1.17.3/go.mod h1:oA6VjNsLll2eVuUoF2D+CMyORgNzPEW/3PyUdq6WQjI= github.com/aws/aws-sdk-go-v2/service/sso v1.20.8 h1:Kv1hwNG6jHC/sxMTe5saMjH6t6ZLkgfvVxyEjfWL1ks= github.com/aws/aws-sdk-go-v2/service/sso v1.20.8/go.mod h1:c1qtZUWtygI6ZdvKppzCSXsDOq5I4luJPZ0Ud3juFCA= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.17.3/go.mod h1:a7bHA82fyUXOm+ZSWKU6PIoBxrjSprdLoM8xPYvzYVg= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.20.1/go.mod h1:hHL974p5auvXlZPIjJTblXJpbkfK4klBczlsEaMCGVY= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.2 h1:nWBZ1xHCF+A7vv9sDzJOq4NWIdzFYm0kH7Pr4OjHYsQ= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.2/go.mod h1:9lmoVDVLz/yUZwLaQ676TK02fhCu4+PgRSmMaKR1ozk= -github.com/aws/aws-sdk-go-v2/service/sts v1.1.1/go.mod h1:Wi0EBZwiz/K44YliU0EKxqTCJGUfYTWXrrBwkq736bM= github.com/aws/aws-sdk-go-v2/service/sts v1.7.0/go.mod h1:0qcSMCyASQPN2sk/1KQLQ2Fh6yq8wm0HSDAimPhzCoM= -github.com/aws/aws-sdk-go-v2/service/sts v1.23.2/go.mod h1:Eows6e1uQEsc4ZaHANmsPRzAKcVDrcmjjWiih2+HUUQ= -github.com/aws/aws-sdk-go-v2/service/sts v1.25.4/go.mod h1:feTnm2Tk/pJxdX+eooEsxvlvTWBvDm6CasRZ+JOs2IY= github.com/aws/aws-sdk-go-v2/service/sts v1.28.9 h1:Qp6Boy0cGDloOE3zI6XhNLNZgjNS8YmiFQFHe71SaW0= github.com/aws/aws-sdk-go-v2/service/sts v1.28.9/go.mod h1:0Aqn1MnEuitqfsCNyKsdKLhDUOr4txD/g19EfiUqgws= -github.com/aws/smithy-go v1.1.0/go.mod h1:EzMw8dbp/YJL4A5/sbhGddag+NPT7q084agLbB9LgIw= github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= -github.com/aws/smithy-go v1.15.0/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= -github.com/aws/smithy-go v1.17.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE= github.com/aws/smithy-go v1.20.2 h1:tbp628ireGtzcHDDmLT/6ADHidqnwgF57XOXZe6tp4Q= github.com/aws/smithy-go v1.20.2/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E= -github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= @@ -1123,97 +113,49 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bits-and-blooms/bitset v1.5.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= -github.com/bits-and-blooms/bitset v1.7.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= 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/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c= -github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= -github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= -github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= -github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU= -github.com/btcsuite/btcd/btcec/v2 v2.2.1/go.mod h1:9/CSmJxmuvqzX9Wh2fXMWToLOHhPd11lSPuIupwTkI8= github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ= github.com/btcsuite/btcd/btcec/v2 v2.3.4/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= -github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 h1:KdUfX2zKommPRa+PD0sWZUyXe9w277ABlgELO7H04IM= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= -github.com/bytecodealliance/wasmtime-go/v7 v7.0.0/go.mod h1:bu6fic7trDt20w+LMooX7j3fsOwv4/ln6j8gAdP6vmA= -github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34= -github.com/c-bata/go-prompt v0.2.6/go.mod h1:/LMAke8wD2FsNu9EXNdHxNLbd9MedkPnCdfpU9wwHfY= github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk= github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 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/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs= github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= -github.com/cloudflare/cloudflare-go v0.14.0/go.mod h1:EnwdgGMaFOruiPZRFSgn+TsQ3hQ7C/YWzIGLeu5c304= -github.com/cloudflare/cloudflare-go v0.79.0/go.mod h1:gkHQf9xEubaQPEuerBuoinR9P8bf8a05Lq0X6WKy1Oc= 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/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20230310173818-32f1caf87195/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20230428030218-4003588d1b74/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa h1:jQCWAUqqlij9Pgj2i/PB79y4KOPYVyFYdROxgaCwdTQ= github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa/go.mod h1:x/1Gn8zydmfq8dk6e9PdstVsDgu9RuyIIJqAaF//0IM= -github.com/cockroachdb/datadriven v1.0.0/go.mod h1:5Ib8Meh+jk1RlHIXej6Pzevx/NLlNvQB9pmSBZErGA4= 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.6.1/go.mod h1:tm6FTP5G81vwJ5lC0SizQo374JNCOPrHyXGitRJoDqM= -github.com/cockroachdb/errors v1.8.1/go.mod h1:qGwQn6JmZ+oMjuLwjWzUNqblqk0xl4CVV3SQbGwK7Ac= github.com/cockroachdb/errors v1.11.3 h1:5bA+k2Y6r+oz/6Z/RFlNeVCesGARKuC6YymtcDrbC/I= github.com/cockroachdb/errors v1.11.3/go.mod h1:m4UIW4CDjx+R5cybPsNrRbreomiFqt8o1h1wUVazSd8= github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce h1:giXvy4KSc/6g/esnpM7Geqxka4WSqI1SZc7sMJFd3y4= github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce/go.mod h1:9/y3cnZ5GKakj/H4y9r9GTjCvAFta7KLgSHPJJYc52M= -github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= -github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593/go.mod h1:6hk1eMY/u5t+Cf18q5lFMUA1Rc+Sm5I6Ra1QuPyxXCo= github.com/cockroachdb/pebble v1.1.1 h1:XnKU22oiCLy2Xn8vp1re67cXg4SAasg/WDt1NtcRFaw= github.com/cockroachdb/pebble v1.1.1/go.mod h1:4exszw1r40423ZsmkG/09AFEG83I0uDgfujJdbL6kYU= -github.com/cockroachdb/redact v1.0.8/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= 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/sentry-go v0.6.1-cockroachdb.2/go.mod h1:8BT+cPK6xvFOcRlk0R8eg+OTkcqI6baNH4xAkpiYVvQ= 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/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= -github.com/consensys/bavard v0.1.8-0.20210406032232-f3452dc9b572/go.mod h1:Bpd0/3mZuaj6Sj+PqrmIquiOKy397AKGThQPaGzNXAQ= 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.4.1-0.20210426202927-39ac3d4b3f1f/go.mod h1:815PAHg3wvysy0SyIqanF8gZ0Y1wjk/hrDHD/iT88+Q= -github.com/consensys/gnark-crypto v0.10.0/go.mod h1:Iq/P3HHl0ElSjsg2E1gsMwhAyxnxoKK5nVyZKd+/KhU= 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/containerd/cgroups v0.0.0-20201119153540-4cbc285b3327/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= @@ -1239,52 +181,31 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:ma 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.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/crate-crypto/go-ipa v0.0.0-20230601170251-1830d0757c80/go.mod h1:gzbVz57IDJgQ9rLQwfSk696JGWof8ftznEL9GoAv3NI= 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 v0.7.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= 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/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/cskr/pubsub v1.0.2 h1:vlOzMhl6PFn60gRlTQQsIfVwaPB/B/8MziK8FhEPt/0= github.com/cskr/pubsub v1.0.2/go.mod h1:/8MzYXk/NJAz782G8RPkFzXTZVu63VotefPnR9TIRis= -github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/danieljoos/wincred v1.2.0/go.mod h1:FzQLLMKBFdvu+osBrnFODiv32YGwCfx0SkRa/eYHgec= github.com/dapperlabs/testingdock v0.4.5-0.20231020233342-a2853fe18724 h1:zOOpPLu5VvH8ixyoDWHnQHWoEHtryT1ne31vwz0G7Fo= github.com/dapperlabs/testingdock v0.4.5-0.20231020233342-a2853fe18724/go.mod h1:U0cEcbf9hAwPSuuoPVqXKhcWV+IU4CStK75cJ52f2/A= -github.com/dave/astrid v0.0.0-20170323122508-8c2895878b14/go.mod h1:Sth2QfxfATb/nW4EsrSi2KyJmbcniZ8TgTaji17D6ms= -github.com/dave/brenda v1.1.0/go.mod h1:4wCUr6gSlu5/1Tk7akE5X7UorwiQ8Rij0SKH3/BGMOM= -github.com/dave/courtney v0.3.0/go.mod h1:BAv3hA06AYfNUjfjQr+5gc6vxeBVOupLqrColj+QSD8= -github.com/dave/dst v0.27.2/go.mod h1:jHh6EOibnHgcUW3WjKHisiooEkYwqpHLBSX1iOBhEyc= -github.com/dave/gopackages v0.0.0-20170318123100-46e7023ec56e/go.mod h1:i00+b/gKdIDIxuLDFob7ustLAVqhsZRk2qVZrArELGQ= -github.com/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg= -github.com/dave/jennifer v1.5.0/go.mod h1:4MnyiFIlZS3l5tSDn8VnzE6ffAhYBMB2SZntBsZGUok= -github.com/dave/kerr v0.0.0-20170318121727-bc25dd6abe8e/go.mod h1:qZqlPyPvfsDJt+3wHJ1EvSXDuVjFTK0j2p/ca+gtsb8= -github.com/dave/patsy v0.0.0-20210517141501-957256f50cba/go.mod h1:qfR88CgEGLoiqDaE+xxDCi5QA5v4vUoW0UCX2Nd5Tlc= -github.com/dave/rebecca v0.9.1/go.mod h1:N6XYdMD/OKw3lkF3ywh8Z6wPGuwNFDNtWYEMFWEmXBA= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c h1:pFUpOrbxDR6AkioZ1ySsx5yxlDQZ8stG2b88gTPxgJU= github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c/go.mod h1:6UhI8N9EjYm1c2odKpFpAYeR8dsBeM7PtzQhRgxRr9U= -github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo= -github.com/deckarep/golang-set/v2 v2.1.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= 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/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= -github.com/deepmap/oapi-codegen v1.6.0/go.mod h1:ryDa9AgbELGeB+YEXE1dR53yAjHwFvE9iAUlWl9Al3M= -github.com/deepmap/oapi-codegen v1.8.2/go.mod h1:YLgSKSDv/bZQB7N4ws6luhozi3cEdRktEqrX88CvjIw= -github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= github.com/dgraph-io/badger/v2 v2.2007.3/go.mod h1:26P/7fbL4kUZVEVKLAKXkBXKOydDmM2p1e+NhhnBCAE= github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o= github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk= @@ -1292,24 +213,16 @@ github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KP github.com/dgraph-io/ristretto v0.1.0 h1:Jv3CGQHp9OjuMBSne1485aDpUkTKEcUqF+jm/LuerPI= github.com/dgraph-io/ristretto v0.1.0/go.mod h1:fux0lOrBhrVCJd3lcTHsIJhq1T2rokOu6v9Vcb3Q9ug= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-bitstream v0.0.0-20180413035011-3522498ce2c8/go.mod h1:VMaSuZ+SZcx/wljOQKvp5srsbCiKDEb6K2wC4+PiBmQ= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= -github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= -github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= -github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko= -github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= github.com/docker/cli v24.0.6+incompatible h1:fF+XCQCgJjjQNIMjzaSmiKJSCcfcXb3TWTcc7GAneOY= github.com/docker/cli v24.0.6+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v1.6.2/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v24.0.6+incompatible h1:hceabKCtUgDqPu+qm0NgsaXf28Ljf4/pWFL7xjWWDgE= github.com/docker/docker v24.0.6+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.8.0 h1:YQFtbBQb4VrpoPxhFuzEBPQ9E16qz5SpHLS+uswaCp8= @@ -1321,20 +234,11 @@ github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHz github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/dop251/goja v0.0.0-20211022113120-dc8c55024d06/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk= -github.com/dop251/goja v0.0.0-20220405120441-9037c2b61cbf/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk= -github.com/dop251/goja v0.0.0-20230806174421-c933cf95e127/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4= -github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y= -github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d/go.mod h1:DngW8aVqWbuLRMHItjPUyqdj+HWPvnQe8V8y1nDpIbM= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts= -github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/ef-ds/deque v1.0.4 h1:iFAZNmveMT9WERAkqLJ+oaABF9AcVQ5AjXem/hroniI= github.com/ef-ds/deque v1.0.4/go.mod h1:gXDnTC3yqvBcHbq2lcExjtAcVrOnJCbMcZXmuj8Z4tg= -github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= github.com/elastic/gosigar v0.12.0/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= github.com/elastic/gosigar v0.14.2 h1:Dg80n8cr90OZ7x+bAax/QjoW/XqTI11RmA79ZwIm9/4= github.com/elastic/gosigar v0.14.2/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= @@ -1345,51 +249,21 @@ github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FM 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/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= -github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= -github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= -github.com/envoyproxy/go-control-plane v0.10.3/go.mod h1:fJJn/j26vwOu972OllsvAgJJM//w9BV6Fxbg2LuVd34= -github.com/envoyproxy/go-control-plane v0.11.0/go.mod h1:VnHyVMpzcLvCFt9yUz1UnCwHLhwx1WguiVDV7pTG/tI= -github.com/envoyproxy/go-control-plane v0.11.1-0.20230524094728-9239064ad72f/go.mod h1:sfYdkwUW4BA3PbKjySwjJy+O4Pu0h62rlqCMHNk+K+Q= -github.com/envoyproxy/go-control-plane v0.11.1/go.mod h1:uhMcXKCQMEJHiAb0w+YGefQLaTEw+YhGluxZkrTmD0g= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo= -github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w= -github.com/envoyproxy/protoc-gen-validate v0.10.0/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= -github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= -github.com/envoyproxy/protoc-gen-validate v1.0.1/go.mod h1:0vj8bNkYbSTNS2PIyH87KZaeN4x9zpL9Qt8fQC7d+vs= -github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A= github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew= -github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= -github.com/ethereum/c-kzg-4844 v0.4.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= 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.10.26/go.mod h1:EYFyF19u3ezGLD4RqOkLq+ZCXzYbLoNDdZlMt7kyKFg= -github.com/ethereum/go-ethereum v1.13.5/go.mod h1:yMTu38GSuyxaYzQMViqNmQ1s3cE84abZexQmTgenWk0= github.com/ethereum/go-ethereum v1.13.10 h1:Ppdil79nN+Vc+mXfge0AuUgmKWuVv4eMqzoIVSdqZek= github.com/ethereum/go-ethereum v1.13.10/go.mod h1:sc48XYQxCzH3fG9BcrXCOOgQk2JfZzNAmIKnceogzsA= 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/fanliao/go-promise v0.0.0-20141029170127-1890db352a72/go.mod h1:PjfxuH4FZdUyfMdtBio2lsRr1AKEaVPwelzuHuh8Lqc= -github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/fjl/gencodec v0.0.0-20220412091415-8bb9e558978c/go.mod h1:AzA8Lj6YtixmJWL+wkKoBGsLWy9gFrAzi4g+5bCKwpY= -github.com/fjl/gencodec v0.0.0-20230517082657-f9840df7b83e/go.mod h1:AzA8Lj6YtixmJWL+wkKoBGsLWy9gFrAzi4g+5bCKwpY= -github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= -github.com/flosch/pongo2 v0.0.0-20190707114632-bbf5a6c351f4/go.mod h1:T9YF2M40nIgbVgp3rreNmTged+9HrbNTIQf1PsaIiTA= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/flynn/noise v1.0.1 h1:vPp/jdQLXC6ppsXSj/pM3W1BIJ5FEHE2TulSJBpb43Y= github.com/flynn/noise v1.0.1/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag= -github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= -github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -1398,7 +272,6 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/fxamacker/cbor/v2 v2.4.1-0.20220515183430-ad2eae63303f/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= github.com/fxamacker/cbor/v2 v2.4.1-0.20230228173756-c0c9f774e40c h1:5tm/Wbs9d9r+qZaUFXk59CWDD0+77PBqDREffYkyi5c= github.com/fxamacker/cbor/v2 v2.4.1-0.20230228173756-c0c9f774e40c/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= github.com/fxamacker/circlehash v0.3.0 h1:XKdvTtIJV9t7DDUtsf0RIpC1OcxZtPbmgIH7ekx28WA= @@ -1409,38 +282,17 @@ github.com/gammazero/deque v0.1.0 h1:f9LnNmq66VDeuAlSAapemq/U7hJ2jpIWa4c09q8Dlik github.com/gammazero/deque v0.1.0/go.mod h1:KQw7vFau1hHuM8xmI9RbgKFbAsQFWmBpqQ2KenFLk6M= github.com/gammazero/workerpool v1.1.2 h1:vuioDQbgrz4HoaCi2q1HLlOXdpbap5AET7xu5/qj87g= github.com/gammazero/workerpool v1.1.2/go.mod h1:UelbXcO0zCIGFcufcirHhq2/xtLXJdQ29qZNlXG9OjQ= -github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61/go.mod h1:Q0X6pkwTILDlzrGEckF6HKjXe48EgsY/l7K7vhY4MW8= -github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI= github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= -github.com/gballet/go-verkle v0.0.0-20230607174250-df487255f46b/go.mod h1:CDncRYVRSDqwakm282WEkjfaAj1hxU/v5RXxk5nXOiI= -github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= -github.com/getkin/kin-openapi v0.61.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps= github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= -github.com/ghemawat/stream v0.0.0-20171120220530-696b145b53b9/go.mod h1:106OIgooyS7OzLDOpUGgm9fA3bQENb/cFSyyBmMoJDs= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= -github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= -github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ= -github.com/glebarez/go-sqlite v1.22.0/go.mod h1:PlBIdHe0+aUEFn+r2/uthrWq4FxbzugL0L8Li6yQJbc= github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4= -github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= -github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= -github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= -github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= 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-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g= -github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks= -github.com/go-fonts/latin-modern v0.3.0/go.mod h1:ysEQXnuT/sCDOAONxC7ImeEDVINbltClhasMAqEtRK0= -github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= -github.com/go-fonts/liberation v0.2.0/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= -github.com/go-fonts/liberation v0.3.0/go.mod h1:jdJ+cqF+F4SUL2V+qxBth8fvBpBDS7yloUL5Fi8GTGY= -github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmnUIzUY= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU= @@ -1449,41 +301,24 @@ github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMj github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= github.com/go-git/go-git/v5 v5.11.0 h1:XIZc1p+8YzypNr34itUfSvYJcv+eYdTnTvOZ2vD3cA4= github.com/go-git/go-git/v5 v5.11.0/go.mod h1:6GFcX2P3NM7FPBfpePbpLd21XxsgdAt+lKqXmCUiUCY= -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-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.12.0 h1:e4o3o3IsBfAKQh5Qbbiqyfu97Ku7jrO/JbohvztANh4= github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEaizzs= -github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= -github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U= -github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81/go.mod h1:SX0U8uGpxhq9o2S/CELCSUxEWWAuoCUcVCQWv7G2OCk= -github.com/go-latex/latex v0.0.0-20230307184459-12ec69307ad9/go.mod h1:gWuR/CrFDDeVRFQwHPvsv9soJVB/iqymhuZQuJ3a9OM= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= -github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.2.6/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-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= -github.com/go-pdf/fpdf v0.6.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= @@ -1492,20 +327,11 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.14.1 h1:9c50NUPC30zyuKprjL3vNZ0m5oG+jU0zvx4AqHGnv4k= github.com/go-playground/validator/v10 v10.14.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= -github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= -github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= -github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= -github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/go-yaml/yaml v2.1.0+incompatible h1:RYi2hDdss1u4YE7GwixGzWwVo47T8UQwnTLB6vQiq+o= github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= -github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= -github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= -github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= -github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -1514,51 +340,27 @@ github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/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 v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM= -github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= -github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang-jwt/jwt/v4 v4.3.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= -github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= -github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= -github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68= github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -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/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.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/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.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= @@ -1568,129 +370,60 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD 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/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 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/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y= -github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= -github.com/gonum/blas v0.0.0-20181208220705-f22b278b28ac/go.mod h1:P32wAyui1PQ58Oce/KYkOqQv8cVw1zAapXOl+dRFGbc= -github.com/gonum/floats v0.0.0-20181209220543-c233463c7e82/go.mod h1:PxC8OnwL11+aosOB5+iEPoV3picfs8tUpkVd0pDo+Kg= -github.com/gonum/internal v0.0.0-20181124074243-f884aa714029/go.mod h1:Pu4dmpkhSyOzRwuXkOgAvijx4o+4YMUJJo9OvPYMkks= -github.com/gonum/lapack v0.0.0-20181123203213-e4cdc5a0bff9/go.mod h1:XA3DeT6rxh2EAE789SSiSJNqxPaC0aE9J8NTOI0Jo/A= -github.com/gonum/matrix v0.0.0-20181209220409-c518dec07be9/go.mod h1:0EXg4mc1CNP0HCqCz+K4ts155PXIlUywf0wqN+GfPZw= 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/flatbuffers v1.11.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= -github.com/google/flatbuffers v2.0.8+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/flatbuffers v23.5.26+incompatible h1:M9dgRyhJemaM4Sw8+66GHBu8ioaQmyPLg1b8VwK5WJg= github.com/google/flatbuffers v23.5.26+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= 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.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 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/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= -github.com/google/go-pkcs11 v0.2.0/go.mod h1:6eQoGcuNJpa7jnd5pMGdkSaQpNDYvPlXWMcjXXThLlY= -github.com/google/go-pkcs11 v0.2.1-0.20230907215043-c6f79328ddf9/go.mod h1:6eQoGcuNJpa7jnd5pMGdkSaQpNDYvPlXWMcjXXThLlY= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= -github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa/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/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= 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/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw= github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-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-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg= github.com/google/pprof v0.0.0-20231229205709-960ae82b1e42 h1:dHLYa5D8/Ta0aLR2XcPsrkpAgGeFs6thhMcQK0oQ0n8= github.com/google/pprof v0.0.0-20231229205709-960ae82b1e42/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/s2a-go v0.1.0/go.mod h1:OJpEgntRZo8ugHpF9hkoLJbS5dSI20XZeXJ9JVywLlM= -github.com/google/s2a-go v0.1.3/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= -github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= -github.com/google/safehtml v0.0.2/go.mod h1:L4KWwDsUJdECRAEpZoBn3O64bQaywRscowZjJAzjHnU= github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= -github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= -github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= -github.com/googleapis/enterprise-certificate-proxy v0.2.1/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= -github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= -github.com/googleapis/enterprise-certificate-proxy v0.2.4/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= -github.com/googleapis/enterprise-certificate-proxy v0.2.5/go.mod h1:RxW0N9901Cko1VOCW3SXCpWP+mlIEkk2tP7jnHy9a3w= github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= -github.com/googleapis/gax-go v0.0.0-20161107002406-da06d194a00e/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= -github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= -github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= -github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= -github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= -github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo= -github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY= -github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= -github.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= -github.com/googleapis/gax-go/v2 v2.8.0/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= -github.com/googleapis/gax-go/v2 v2.10.0/go.mod h1:4UOEnMCrxsSqQ940WnTiD6qJ63le2ev3xfyagutxiPw= -github.com/googleapis/gax-go/v2 v2.11.0/go.mod h1:DxmR61SGKkGLa2xigwuZIQpkCI2S5iydzRfb3peWZJI= github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= -github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= -github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c h1:7lF+Vz0LqiRidnzC1Oq86fpX1q/iEv2KJdrCtttYjT4= github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -1698,10 +431,8 @@ github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB7 github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/graph-gophers/graphql-go v1.3.0/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 h1:pRhl55Yx1eC7BZ1N+BBWwnKaMyD8uC+34TLdndZMAKk= @@ -1710,37 +441,21 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92Bcuy github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w= github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 h1:Wqo399gCIufwto+VfwCSvsnfGpF/w5E9CNxSwbpD6No= github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0/go.mod h1:qmOFXW2epJhM0qSnUUYpldc7gVz2KMQwJ/QYCDIa7XU= -github.com/guptarohit/asciigraph v0.5.5/go.mod h1:dYl5wwK4gNsnFf9Zp+l06rFiDZ5YtXM6x7SRWZ3KGag= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= -github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= -github.com/hashicorp/go-hclog v1.2.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-retryablehttp v0.7.4/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= -github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -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/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= -github.com/holiman/uint256 v1.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw= -github.com/holiman/uint256 v1.2.3/go.mod h1:SC8Ryt4n+UBbPbIBKaG9zbbDlp4jOru9xFZmPzLUTxw= github.com/holiman/uint256 v1.3.0 h1:4wdcm/tnd0xXdu7iS3ruNvxkWwrb4aeBQv19ayYn8F4= github.com/holiman/uint256 v1.3.0/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= @@ -1751,31 +466,11 @@ github.com/huandu/go-clone v1.7.2/go.mod h1:ReGivhG6op3GYr+UY3lS6mxjKp7MIGTknuU5 github.com/huandu/go-clone/generic v1.7.2 h1:47pQphxs1Xc9cVADjOHN+Bm5D0hNagwH9UXErbxgVKA= github.com/huandu/go-clone/generic v1.7.2/go.mod h1:xgd9ZebcMsBWWcBx5mVMCoqMX24gLWr5lQicr+nVXNs= github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis= -github.com/huin/goupnp v1.0.3/go.mod h1:ZxNlw5WqJj6wSsRK5+YfflQGXYfccj5VgQsMNixHM7Y= github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= -github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= -github.com/hydrogen18/memlistener v0.0.0-20141126152155-54553eb933fb/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE= -github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= -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/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= -github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/influxdata/flux v0.65.1/go.mod h1:J754/zds0vvpfwuq7Gc2wRdVwEodfpCFM7mYlOw2LqY= -github.com/influxdata/influxdb v1.8.3/go.mod h1:JugdFhsvvI8gadxOI6noqNeeBHvWNTbfYGtiAn+2jhI= -github.com/influxdata/influxdb-client-go/v2 v2.4.0/go.mod h1:vLNHdxTJkIf2mSLvGrpj8TCcISApPoXkaxP8g9uRlW8= -github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= -github.com/influxdata/influxql v1.1.1-0.20200828144457-65d3ef77d385/go.mod h1:gHp9y86a/pxhjJ+zMjNXiQAA197Xk9wLxaz+fGG+kWk= -github.com/influxdata/line-protocol v0.0.0-20180522152040-32c6aa80de5e/go.mod h1:4kt73NQhadE3daL3WhR5EJ/J2ocX0PZzwxQ0gXJ7oFE= -github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo= -github.com/influxdata/line-protocol v0.0.0-20210311194329-9aa0e372d097/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo= -github.com/influxdata/promql/v2 v2.12.0/go.mod h1:fxOPu+DY0bqCTCECchSRtWfc+0X19ybifQhZoQNF5D8= -github.com/influxdata/roaring v0.4.13-0.20180809181101-fc520f41fab6/go.mod h1:bSgUQ7q5ZLSO+bKBGqJiCBGAl+9DxyW63zLTujjUlOE= -github.com/influxdata/tdigest v0.0.0-20181121200506-bf2b5ad3c0a9/go.mod h1:Js0mqiSBE6Ffsg94weZZ2c+v/ciT8QRHFOap7EKDrR0= -github.com/influxdata/usage-client v0.0.0-20160829180054-6d3895376368/go.mod h1:Wbbw6tYNvwa5dlB6304Sd+82Z3f7PmVZHVKU637d4po= github.com/insomniacslk/dhcp v0.0.0-20230516061539-49801966e6cb/go.mod h1:7474bZ1YNCvarT6WFKie4kEET6J0KYRDC4XJqqXzQW4= github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs= github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0= @@ -1821,10 +516,6 @@ github.com/ipfs/go-peertaskqueue v0.8.1 h1:YhxAs1+wxb5jk7RvS0LHdyiILpNmRIRnZVzte github.com/ipfs/go-peertaskqueue v0.8.1/go.mod h1:Oxxd3eaK279FxeydSPPVGHzbwVeHjatZ2GA8XD+KbPU= github.com/ipld/go-ipld-prime v0.21.0 h1:n4JmcpOlPDIxBcY037SVfpd1G+Sj1nKZah0m6QH9C2E= github.com/ipld/go-ipld-prime v0.21.0/go.mod h1:3RLqy//ERg/y5oShXXdx5YIp50cFGOanyMctpPjsvxQ= -github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI= -github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0= -github.com/iris-contrib/i18n v0.0.0-20171121225848-987a633949d0/go.mod h1:pMCz62A0xJL6I+umB2YTlFRwWXaDFA0jy+5HzGiJjqI= -github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw= 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/jbenet/go-cienv v0.1.0/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA= @@ -1834,8 +525,6 @@ github.com/jbenet/go-temp-err-catcher v0.1.0 h1:zpb3ZH6wIE8Shj2sKS+khgRvf7T7RABo github.com/jbenet/go-temp-err-catcher v0.1.0/go.mod h1:0kJRvmDZXNMIiJirNPEYfhpPwbGVtZVWC34vc5WLsDk= github.com/jbenet/goprocess v0.1.4 h1:DRGOFReOMqqDNXwW70QkacFW0YN9QnwLV0Vqk+3oU0o= github.com/jbenet/goprocess v0.1.4/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4= -github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e/go.mod h1:G1CVv03EnqU1wYL2dFwXxW2An0az9JTl/ZsqXQeBlkU= -github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267/go.mod h1:h1nSAbGFqGVzn6Jyl1R/iCcBUHN4g+gW1u9CoBTrb9E= github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= @@ -1845,99 +534,55 @@ github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22 github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw= github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ= github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok= github.com/jsimonetti/rtnetlink v0.0.0-20201110080708-d2c240429e6c/go.mod h1:huN4d1phzjhlOsNIjFsw2SVRbwIHj3fJDMEU2SDPTmg= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 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/jsternberg/zap-logfmt v1.0.0/go.mod h1:uvPs/4X51zdkcm5jXl5SYoN+4RK21K8mysFmDaM/h+o= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q= -github.com/juju/loggo v0.0.0-20180524022052-584905176618/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U= -github.com/juju/testing v0.0.0-20180920084828-472a3e8b2073/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= -github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= -github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef/go.mod h1:Ct9fl0F6iIOGgxJ5npU/IUOhOhqlVrGjyIZc8/MagT0= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 h1:uC1QfSlInpQF+M0ao65imhwqKnz3Q2z/d8PWZRMQvDM= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw= github.com/k0kubun/pp v3.0.1+incompatible h1:3tqvf7QgUnZ5tXO6pNAZlrvHgl6DvifjDrd9g2S9Z40= github.com/k0kubun/pp v3.0.1+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg= -github.com/k0kubun/pp/v3 v3.2.0/go.mod h1:ODtJQbQcIRfAD3N+theGCV1m/CBxweERz2dapdz1EwA= -github.com/karalabe/usb v0.0.2/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= -github.com/kataras/golog v0.0.9/go.mod h1:12HJgwBIZFNGL0EJnMRhmvGA0PQGx8VFwrZtM4CqbAk= -github.com/kataras/iris/v12 v12.0.1/go.mod h1:udK4vLQKkdDqMGJJVd/msuMtN6hpYJhg/lSzuxjhO+U= -github.com/kataras/neffos v0.0.10/go.mod h1:ZYmJC07hQPW67eKuzlfY7SO3bC0mw83A3j6im82hfqw= -github.com/kataras/pio v0.0.0-20190103105442-ea782b38602d/go.mod h1:NV88laa9UiiDuX9AhMbDPkGYSPugBOV6yTZB1l2K9Z0= -github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= -github.com/kevinburke/go-bindata v3.23.0+incompatible/go.mod h1:/pEEZ72flUW2p0yi30bslSp9YqD9pysLxunQDdb2CPM= github.com/kevinburke/go-bindata v3.24.0+incompatible h1:qajFA3D0pH94OTLU4zcCCKCDgR+Zr2cZK/RPJHDdFoY= github.com/kevinburke/go-bindata v3.24.0+incompatible/go.mod h1:/pEEZ72flUW2p0yi30bslSp9YqD9pysLxunQDdb2CPM= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= -github.com/kilic/bls12-381 v0.1.0/go.mod h1:vDTTHJONJ6G+P2R74EhnyotQDTliQDnFEwhdmfzw1ig= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= 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/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= -github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/compress v1.9.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= -github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= -github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4= github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= -github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= -github.com/klauspost/cpuid/v2 v2.2.0/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= -github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= -github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6/go.mod h1:+ZoRqAPRLkC4NPOvfYeR5KNOrY6TD+/sAC3HXPZgDYg= -github.com/klauspost/pgzip v1.0.2-0.20170402124221-0bf5dcad4ada/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= 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.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/koron/go-ssdp v0.0.4 h1:1IDwrghSKYM7yLf7XCzbByg2sJ/JcNOZRXS2jczTwz0= github.com/koron/go-ssdp v0.0.4/go.mod h1:oDXq+E5IL5q0U8uSBcoAXzTzInwy5lEgC91HoKtbmZk= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= 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.3/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 v0.0.0-20170224010052-a616ab194758/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= 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/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g= -github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg= -github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= 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/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= -github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/libp2p/go-addr-util v0.1.0 h1:acKsntI33w2bTU7tC9a0SaPimJGfSI0bFKC18ChxeVI= @@ -1975,75 +620,39 @@ github.com/libp2p/go-reuseport v0.4.0 h1:nR5KU7hD0WxXCJbmw7r2rhRYruNRl2koHw8fQsc github.com/libp2p/go-reuseport v0.4.0/go.mod h1:ZtI03j/wO5hZVDFo2jKywN6bYKWLOy8Se6DrI2E1cLU= github.com/libp2p/go-yamux/v4 v4.0.1 h1:FfDR4S1wj6Bw2Pqbc8Uz7pCxeRBPbwsBbEdfwiCypkQ= github.com/libp2p/go-yamux/v4 v4.0.1/go.mod h1:NWjl8ZTLOGlozrXSOZ/HlfG++39iKNnM5wwmtQP1YB4= -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/logrusorgru/aurora/v4 v4.0.0 h1:sRjfPpun/63iADiSvGGjgA1cAYegEWMPCJdUpJYn9JA= github.com/logrusorgru/aurora/v4 v4.0.0/go.mod h1:lP0iIa2nrnT/qoFXcOZSrZQpJ1o6n2CUf/hyHi2Q4ZQ= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= -github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= -github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= -github.com/lyft/protoc-gen-star/v2 v2.0.1/go.mod h1:RcCdONR2ScXaYnQC5tUzxzlpA3WVYF7/opLeUgcQs/o= -github.com/lyft/protoc-gen-star/v2 v2.0.3/go.mod h1:amey7yeodaJhXSbf/TlLvWiqQfLOSpEk//mLlc+axEk= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 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.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd h1:br0buuQ854V8u83wA0rVZ8ttrq5CpaPZdvrK0LP2lOk= github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd/go.mod h1:QuCEs1Nt24+FYQEqAAncTDPJIuGs+LxK1MCiFL25pMU= -github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ= -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.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.4/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.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= -github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.17/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.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -github.com/mattn/go-sqlite3 v1.14.5/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI= -github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= -github.com/mattn/go-tty v0.0.0-20180907095812-13ff1204f104/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE= -github.com/mattn/go-tty v0.0.3/go.mod h1:ihxohKRERHTVzN+aSVRwACLCeqIoZAWpoICkkvrWyR0= -github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA= github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M= github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY= github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o= github.com/mdlayher/packet v1.1.1/go.mod h1:DRvYY5mH4M4lUqAnMg04E60U4fjUKMZ/4g2cHElZkKo= github.com/mdlayher/socket v0.4.0/go.mod h1:xxFqz5GRCUN3UEOm9CZqEJsAbe1C8OwSK46NlmWuVoc= -github.com/mediocregopher/mediocre-go-lib v0.0.0-20181029021733-cb65787f37ed/go.mod h1:dSsfyI2zABAdhcbvkXqgxOxrCsbYeHCPgrZkku60dSg= -github.com/mediocregopher/radix/v3 v3.3.0/go.mod h1:EmfVyvspXz1uZEyPBMyGK+kjWiKQGvsUt6O3Pj+LDCQ= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= -github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM= github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk= @@ -2053,9 +662,7 @@ github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b h1:z78hV3sbSMAUoyUM github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b/go.mod h1:lxPUiZwKoFL8DUUmalo2yJJUCxbPKtm8OKfqr2/FTNU= github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc h1:PTfri+PuQmWDqERdnNMiD9ZejrlswWrCpBEZgWOiTrc= github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc/go.mod h1:cGKTAVKx4SxOuR/czcZ/E2RSJ3sfHs8FpHhQ5CWMf9s= -github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= -github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= @@ -2063,10 +670,8 @@ github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2Em github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 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/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= @@ -2078,19 +683,13 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/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/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= -github.com/montanaflynn/stats v0.6.6/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= -github.com/montanaflynn/stats v0.7.0/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= -github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg= github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA= github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE= github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI= @@ -2125,61 +724,44 @@ github.com/multiformats/go-varint v0.0.6/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXS github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8= github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0= -github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E= -github.com/nats-io/nats.go v1.8.1/go.mod h1:BrFz9vVn0fU3AcH9Vn4Kd7W0NpJ651tD5omQ3M8LwxM= -github.com/nats-io/nkeys v0.0.2/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7TDb/4= -github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= 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/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= 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/onflow/atree v0.6.1-0.20230711151834-86040b30171f/go.mod h1:xvP61FoOs95K7IYdIYRnNcYQGf4nbF/uuJ0tHf4DRuM= -github.com/onflow/atree v0.8.0-rc.6 h1:GWgaylK24b5ta2Hq+TvyOF7X5tZLiLzMMn7lEt59fsA= -github.com/onflow/atree v0.8.0-rc.6/go.mod h1:yccR+LR7xc1Jdic0mrjocbHvUD7lnVvg8/Ct1AA5zBo= -github.com/onflow/cadence v1.0.0-M3/go.mod h1:odXGZZ/wGNA5mwT8bC9v8u8EXACHllB2ABSZK65TGL8= -github.com/onflow/cadence v1.0.0-preview.52 h1:hZ92e6lL2+PQa3C1i5jJh0zZYFdW89+X1MS0Bkd6Ayo= -github.com/onflow/cadence v1.0.0-preview.52/go.mod h1:7wvvecnAZtYOspLOS3Lh+FuAmMeSrXhAWiycC3kQ1UU= -github.com/onflow/crypto v0.25.0/go.mod h1:C8FbaX0x8y+FxWjbkHy0Q4EASCDR9bSPWZqlpCLYyVI= +github.com/onflow/atree v0.8.0 h1:qg5c6J1gVDNObughpEeWm8oxqhPGdEyGrda121GM4u0= +github.com/onflow/atree v0.8.0/go.mod h1:yccR+LR7xc1Jdic0mrjocbHvUD7lnVvg8/Ct1AA5zBo= +github.com/onflow/cadence v1.2.2 h1:LwigF/2lPiXlwX5rFn71KeMpmW5Iu/f/JtsPLLULBCc= +github.com/onflow/cadence v1.2.2/go.mod h1:PYX1xLejqswtDsQzN93x/VpfSKNyjUk6hrkc/mpv7xs= github.com/onflow/crypto v0.25.2 h1:GjHunqVt+vPcdqhxxhAXiMIF3YiLX7gTuTR5O+VG2ns= github.com/onflow/crypto v0.25.2/go.mod h1:fY7eLqUdMKV8EGOw301unP8h7PvLVy8/6gVR++/g0BY= -github.com/onflow/flow-core-contracts/lib/go/contracts v1.3.1 h1:q9tXLIALwQ76bO4bmSrhtTkyc2cZF4/gH11ix9E3F5k= -github.com/onflow/flow-core-contracts/lib/go/contracts v1.3.1/go.mod h1:u/mkP/B+PbV33tEG3qfkhhBlydSvAKxfLZSfB4lsJHg= -github.com/onflow/flow-core-contracts/lib/go/templates v1.3.1 h1:FfhMBAb78p6VAWkJ+iqdKLErGQVQgxk5w6DP5ZruWX8= -github.com/onflow/flow-core-contracts/lib/go/templates v1.3.1/go.mod h1:NgbMOYnMh0GN48VsNKZuiwK7uyk38Wyo8jN9+C9QE30= -github.com/onflow/flow-emulator v1.0.0-preview.41.0.20240829134601-0be55d6970b5 h1:Z5PC3Sqvl2UemY27uwUwzkLb8EXUf+m/aEimxFzOXuc= -github.com/onflow/flow-emulator v1.0.0-preview.41.0.20240829134601-0be55d6970b5/go.mod h1:gFafyGD4Qxs+XT2BRSIjQJ86ILSmgm1VYUoCr1nVxVI= -github.com/onflow/flow-ft/lib/go/contracts v1.0.0 h1:mToacZ5NWqtlWwk/7RgIl/jeKB/Sy/tIXdw90yKHcV0= -github.com/onflow/flow-ft/lib/go/contracts v1.0.0/go.mod h1:PwsL8fC81cjnUnTfmyL/HOIyHnyaw/JA474Wfj2tl6A= -github.com/onflow/flow-ft/lib/go/templates v1.0.0 h1:6cMS/lUJJ17HjKBfMO/eh0GGvnpElPgBXx7h5aoWJhs= -github.com/onflow/flow-ft/lib/go/templates v1.0.0/go.mod h1:uQ8XFqmMK2jxyBSVrmyuwdWjTEb+6zGjRYotfDJ5pAE= -github.com/onflow/flow-go-sdk v1.0.0-M1/go.mod h1:TDW0MNuCs4SvqYRUzkbRnRmHQL1h4X8wURsCw9P9beo= -github.com/onflow/flow-go-sdk v1.0.0-preview.55 h1:tUM8K7GcWltM0YSzei/g2Gq4z3BwGFTdpq2QwvB6ubk= -github.com/onflow/flow-go-sdk v1.0.0-preview.55/go.mod h1:rBRNboXaTprn7M0MeO6/R1bxNpctbrx66I2FLp0V6fM= -github.com/onflow/flow-nft/lib/go/contracts v1.2.1 h1:woAAS5z651sDpi7ihAHll8NvRS9uFXIXkL6xR+bKFZY= -github.com/onflow/flow-nft/lib/go/contracts v1.2.1/go.mod h1:2gpbza+uzs1k7x31hkpBPlggIRkI53Suo0n2AyA2HcE= -github.com/onflow/flow-nft/lib/go/templates v1.2.0 h1:JSQyh9rg0RC+D1930BiRXN8lrtMs+ubVMK6aQPon6Yc= -github.com/onflow/flow-nft/lib/go/templates v1.2.0/go.mod h1:p+2hRvtjLUR3MW1NsoJe5Gqgr2eeH49QB6+s6ze00w0= -github.com/onflow/flow/protobuf/go/flow v0.3.2-0.20231121210617-52ee94b830c2/go.mod h1:NA2pX2nw8zuaxfKphhKsk00kWLwfd+tv8mS23YXO4Sk= -github.com/onflow/flow/protobuf/go/flow v0.4.6 h1:KE/CsRVfyG5lGBtm1aNcjojMciQyS5GfPF3ixOWRfi0= -github.com/onflow/flow/protobuf/go/flow v0.4.6/go.mod h1:NA2pX2nw8zuaxfKphhKsk00kWLwfd+tv8mS23YXO4Sk= +github.com/onflow/flow-core-contracts/lib/go/contracts v1.4.0 h1:R86HaOuk6vpuECZnriEUE7bw9inC2AtdSn8lL/iwQLQ= +github.com/onflow/flow-core-contracts/lib/go/contracts v1.4.0/go.mod h1:9asTBnB6Tw2UlVVtQKyS/egYv3xr4zVlJnJ75z1dfac= +github.com/onflow/flow-core-contracts/lib/go/templates v1.4.0 h1:u2DAG8pk0xFH7TwS70t1gSZ/FtIIZWMSNyiu4SeXBYg= +github.com/onflow/flow-core-contracts/lib/go/templates v1.4.0/go.mod h1:pN768Al/wLRlf3bwugv9TyxniqJxMu4sxnX9eQJam64= +github.com/onflow/flow-ft/lib/go/contracts v1.0.1 h1:Ts5ob+CoCY2EjEd0W6vdLJ7hLL3SsEftzXG2JlmSe24= +github.com/onflow/flow-ft/lib/go/contracts v1.0.1/go.mod h1:PwsL8fC81cjnUnTfmyL/HOIyHnyaw/JA474Wfj2tl6A= +github.com/onflow/flow-ft/lib/go/templates v1.0.1 h1:FDYKAiGowABtoMNusLuRCILIZDtVqJ/5tYI4VkF5zfM= +github.com/onflow/flow-ft/lib/go/templates v1.0.1/go.mod h1:uQ8XFqmMK2jxyBSVrmyuwdWjTEb+6zGjRYotfDJ5pAE= +github.com/onflow/flow-go-sdk v1.2.3 h1:jb+0dIXBO12Zt8x3c2xDXYPv6k3sRTUvhe59M+EcXTI= +github.com/onflow/flow-go-sdk v1.2.3/go.mod h1:jMaffBTlAIdutx+pBhRIigLZFIBYSDDST0Uax1rW2qo= +github.com/onflow/flow-nft/lib/go/contracts v1.2.2 h1:XFERNVUDGbZ4ViZjt7P1cGD80mO1PzUJYPfdhXFsGbQ= +github.com/onflow/flow-nft/lib/go/contracts v1.2.2/go.mod h1:eZ9VMMNfCq0ho6kV25xJn1kXeCfxnkhj3MwF3ed08gY= +github.com/onflow/flow-nft/lib/go/templates v1.2.1 h1:SAALMZPDw9Eb9p5kSLnmnFxjyig1MLiT4JUlLp0/bSE= +github.com/onflow/flow-nft/lib/go/templates v1.2.1/go.mod h1:W6hOWU0xltPqNpv9gQX8Pj8Jtf0OmRxc1XX2V0kzJaI= +github.com/onflow/flow/protobuf/go/flow v0.4.7 h1:iP6DFx4wZ3ETORsyeqzHu7neFT3d1CXF6wdK+AOOjmc= +github.com/onflow/flow/protobuf/go/flow v0.4.7/go.mod h1:NA2pX2nw8zuaxfKphhKsk00kWLwfd+tv8mS23YXO4Sk= github.com/onflow/go-ethereum v1.14.7 h1:gg3awYqI02e3AypRdpJKEvNTJ6kz/OhAqRti0h54Wlc= github.com/onflow/go-ethereum v1.14.7/go.mod h1:zV14QLrXyYu5ucvcwHUA0r6UaqveqbXaehAVQJlSW+I= -github.com/onflow/sdks v0.5.1-0.20230912225508-b35402f12bba/go.mod h1:F0dj0EyHC55kknLkeD10js4mo14yTdMotnWMslPirrU= github.com/onflow/sdks v0.6.0-preview.1 h1:mb/cUezuqWEP1gFZNAgUI4boBltudv4nlfxke1KBp9k= github.com/onflow/sdks v0.6.0-preview.1/go.mod h1:F0dj0EyHC55kknLkeD10js4mo14yTdMotnWMslPirrU= github.com/onflow/wal v1.0.2 h1:5bgsJVf2O3cfMNK12fiiTyYZ8cOrUiELt3heBJfHOhc= github.com/onflow/wal v1.0.2/go.mod h1:iMC8gkLqu4nkbkAla5HkSBb+FGyQOZiWz3DYm2wSXCk= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= @@ -2197,28 +779,17 @@ github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/ github.com/opencontainers/runtime-spec v1.1.0 h1:HHUyrt9mwHUjtasSbXSMvs4cyFxh+Bll4AjJ9odEGpg= github.com/opencontainers/runtime-spec v1.1.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/opentracing/opentracing-go v1.0.3-0.20180606204148-bd9c31933947/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= -github.com/paulbellamy/ratecounter v0.2.0/go.mod h1:Hfx1hDpSGoqxkVVpBi/IlYD7kChlfo5C6hzIHwPqfFE= 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.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= 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/peterh/liner v1.0.1-0.20180619022028-8c1271fcf47f/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc= -github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= -github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= -github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY= -github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= -github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= -github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM= github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= -github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pierrec/lz4/v4 v4.1.17/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pierrec/lz4/v4 v4.1.18 h1:xaKrnTkyoqfh1YItXl56+6KJNVYWlEEPuAQW9xsplYQ= github.com/pierrec/lz4/v4 v4.1.18/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= @@ -2226,41 +797,27 @@ github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4 github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= -github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ= -github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 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/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= -github.com/pkg/term v0.0.0-20180730021639-bffc007b7fd5/go.mod h1:eCbImbZ95eXtAUIbLAuAVnBnwf83mjf6QIVH8SHYwqQ= -github.com/pkg/term v1.2.0-beta.2/go.mod h1:E25nymQcrSllhX42Ok8MRm1+hyBdHY0dCeiKZ9jpNGw= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/polydawn/refmt v0.89.0 h1:ADJTApkvkeBZsN0tBTx8QjpD9JkmxbKp0cxfr9qszm4= github.com/polydawn/refmt v0.89.0/go.mod h1:/zvteZs/GwLtCgZ4BL6CBsk9IKIlexP43ObX9AxTqTw= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= -github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.0/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk= github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.1-0.20210607210712-147c58e9608a/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= -github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= -github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= @@ -2268,9 +825,6 @@ github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7q github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/common v0.46.0 h1:doXzt5ybi1HBKpsZOL0sSkaNHJJqkyfEWZGGqqScV0Y= github.com/prometheus/common v0.46.0/go.mod h1:Tp0qkxpb9Jsg54QMe+EAmqXkSV7Evdy1BTn+g2pa/hQ= github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= @@ -2278,13 +832,9 @@ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/protolambda/bls12-381-util v0.0.0-20220416220906-d8552aa452c7/go.mod h1:IToEjHuttnUzwZI5KBSM/LOOW3qLbbrHOEfp3SbECGY= github.com/psiemens/graceland v1.0.0 h1:L580AVV4Q2XLcPpmvxJRH9UpEAYr/eu2jBKmMglhvM8= github.com/psiemens/graceland v1.0.0/go.mod h1:1Tof+vt1LbmcZFE0lzgdwMN0QBymAChG3FRgDx8XisU= github.com/psiemens/sconfig v0.1.0 h1:xfWqW+TRpih7mXZIqKYTmpRhlZLQ1kbxV8EjllPv76s= @@ -2299,41 +849,25 @@ github.com/quic-go/webtransport-go v0.6.0 h1:CvNsKqc4W2HljHJnoT+rMmbRJybShZ0YPFD github.com/quic-go/webtransport-go v0.6.0/go.mod h1:9KjU4AEBqEQidGHNDkZrb8CAa1abRaosM2yGOyiikEc= github.com/raulk/go-watchdog v1.3.0 h1:oUmdlHxdkXRJlwfG0O9omj8ukerm8MEQavSiDTEtBsk= github.com/raulk/go-watchdog v1.3.0/go.mod h1:fIvOnLbF0b0ZwkB9YU4mOW9Did//4vPZtDqv66NfsMU= -github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= -github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -github.com/retailnext/hllpp v1.0.1-0.20180308014038-101a6d2f8b52/go.mod h1:RDpi1RftBQPUCDRw6SmxeaREsAaRKnOclghuzp/WRzc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRrjvIXnJho= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -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.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= 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/rootless-containers/rootlesskit v1.1.1 h1:F5psKWoWY9/VjZ3ifVcaosjvFZJOagX85U22M0/EQZE= github.com/rootless-containers/rootlesskit v1.1.1/go.mod h1:UD5GoA3dqKCJrnvnhVgQQnweMF2qZnf9KLw8EewcMZI= -github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.29.0 h1:Zes4hju04hjbvkVkOhdl2HpZa+0PmVwigmo8XoORE5w= github.com/rs/zerolog v1.29.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= -github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk= -github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/schollz/progressbar/v3 v3.13.1 h1:o8rySDYiQ59Mwzy2FELeHY5ZARXZTVJC7iHD6PEFUiE= github.com/schollz/progressbar/v3 v3.13.1/go.mod h1:xvrbki8kfT1fzWzBT/UZd9L6GA+jdL7HAgq2RFnO6fQ= -github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= -github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= -github.com/segmentio/kafka-go v0.2.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sethvargo/go-retry v0.2.3 h1:oYlgvIvsju3jNbottWABtbnoLC+GDtLdBHxKWxQm/iU= @@ -2370,8 +904,6 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sirupsen/logrus v1.9.2/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= @@ -2397,18 +929,12 @@ github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasO github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= -github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= -github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= 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.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= -github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= -github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= @@ -2421,7 +947,6 @@ github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DM github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/spf13/viper v1.15.0 h1:js3yy885G8xwJa6iOISGFwd+qlUo5AvyXb7CiihdtiU= github.com/spf13/viper v1.15.0/go.mod h1:fFcTBJxvhhzSJiZy8n+PeW6t8l+KeT/uTARa0jHOQLA= -github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q= 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= @@ -2430,25 +955,20 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS 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.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 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.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 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.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= -github.com/supranational/blst v0.3.8-0.20220526154634-513d2456b344/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= 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.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= @@ -2456,18 +976,13 @@ github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45 github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= github.com/texttheater/golang-levenshtein/levenshtein v0.0.0-20200805054039-cae8b0eaed6c h1:HelZ2kAFadG0La9d+4htN4HzQ68Bm2iM9qKMSMES6xg= github.com/texttheater/golang-levenshtein/levenshtein v0.0.0-20200805054039-cae8b0eaed6c/go.mod h1:JlzghshsemAMDGZLytTFY8C1JQxQPhnatWqNwUXjggo= -github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= -github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= -github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI= 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.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM= github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/turbolent/prettier v0.0.0-20220320183459-661cc755135d h1:5JInRQbk5UBX8JfUvKh2oYTLMVwj3p6n+wapDDm7hko= github.com/turbolent/prettier v0.0.0-20220320183459-661cc755135d/go.mod h1:Nlx5Y115XQvNcIdIy7dZXaNSUpzwBSge4/Ivk93/Yog= -github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs= 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/u-root/uio v0.0.0-20230220225925-ffce2a382923/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264= @@ -2476,17 +991,7 @@ github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGr github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.10/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= -github.com/urfave/cli/v2 v2.10.2/go.mod h1:f8iq5LtQ/bLxafbdBSLPPNsgaW0l/2fYYEHhAyPlwvo= -github.com/urfave/cli/v2 v2.24.1/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc= github.com/urfave/cli/v2 v2.25.5/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc= -github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= -github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= -github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.6.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w= -github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= -github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= -github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= @@ -2503,30 +1008,18 @@ github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0/go.mod h1:x6AKhvS github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 h1:EKhdznlJHPMoKr0XTrX+IlJs1LH3lyx2nfr1dOlZ79k= github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1/go.mod h1:8UvriyWtv5Q5EOgjHaSseUEdkQfvwFv1I/In/O2M9gc= github.com/whyrusleeping/go-logging v0.0.0-20170515211332-0457bb6b88fc/go.mod h1:bopw91TMyo8J3tvftk8xmU2kPmlrt4nScJQZU2hE5EM= -github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= -github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= -github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= -github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= -github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI= github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.11-flow-expose-msg.0.20240220190333-03695dea34a3 h1:GyrwPbleN4FGHa/Ku1aiNKowV4l4FCKRzZfCbvbv5P4= github.com/yhassanzadeh13/go-libp2p-pubsub v0.6.11-flow-expose-msg.0.20240220190333-03695dea34a3/go.mod h1:Irbd2TlWD6Bk0i9ggIqd+WPz0Axp8wP9VuNCm2+Ibrg= -github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= -github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= -github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc= -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.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= @@ -2543,55 +1036,36 @@ go.einride.tech/pid v0.1.0 h1:7eO7+9gXBMb+G3HIn/68wOfLhCu1gtdt55Jkj754/gg= go.einride.tech/pid v0.1.0/go.mod h1:wWWiiuBM69aJ3o/KK3OCDYlkhMKB5F+sVkybR/wRJVk= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= -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.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0 h1:UNQQKPfTDe1J81ViolILjTKPr9WetKW6uei2hFgJmFs= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0/go.mod h1:r9vWsPS/3AQItv3OSlEJ/E4mbrhUbbw18meOjArPtKQ= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 h1:sv9kVfal0MK0wBMCOGr+HeJm9v803BkJxGrk2au7j08= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0/go.mod h1:SK2UL73Zy1quvRPonmOmRDiWk1KBV3LyIeeIxcEApWw= -go.opentelemetry.io/otel v1.8.0/go.mod h1:2pkj+iMj0o03Y+cW6/m8Y4WkRdYN3AvCXCnzRMp9yvM= -go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 h1:cl5P5/GIfFh4t6xyruOgJP5QiA1pw4fYYdv6nc6CBWw= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0/go.mod h1:zgBdWWAu7oEEMC06MMKc5NLbA/1YDXV1sMpSqEeLQLg= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 h1:tIqheXEFWAZ7O8A7m+J0aPTmpJN3YQ7qetUAdkkkKpk= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0/go.mod h1:nUeKExfxAQVbiVFn32YXpXZZHZ61Cc3s3Rn1pDBGAb0= -go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4= go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw= go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg= -go.opentelemetry.io/otel/trace v1.8.0/go.mod h1:0Bt3PXY8w+3pheS3hQUt+wow8b1ojPaTBoTCh2zIFI4= -go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= -go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= -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.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.uber.org/automaxprocs v1.5.2/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0= go.uber.org/dig v1.17.1 h1:Tga8Lz8PcYNsWsyHMZ1Vm0OQOUaJNDyvPImgbAu9YSc= go.uber.org/dig v1.17.1/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE= go.uber.org/fx v1.20.1 h1:zVwVQGS8zYvhh9Xxcu4w1M6ESyeMzebzj2NbSayZ4Mk= go.uber.org/fx v1.20.1/go.mod h1:iSYNbHf2y55acNCwCXKx7LbWb5WG1Bnue5RDXz1OREg= -go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= 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/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= @@ -2602,7 +1076,6 @@ go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9i go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.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.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= @@ -2616,109 +1089,35 @@ golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190909091759-094676da4a83/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200602180216-279210d13fed/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= -golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= -golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= -golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= -golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= -golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= -golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= -golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= +golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/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-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE= -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-20220426173459-3bcf042a4bf5/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= -golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= -golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= -golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= -golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= -golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA= golang.org/x/exp v0.0.0-20240119083558-1b970713d09a/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= -golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= -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/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20210216034530-4410531fe030/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20210607152325-775e3b0c77b9/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= -golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= -golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= -golang.org/x/image v0.0.0-20220302094943-723b81ca9867/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= -golang.org/x/image v0.5.0/go.mod h1:FVC7BI/5Ym8R25iw5OLsgshdUBbT1h5jZTpA+mvAdZ4= -golang.org/x/image v0.6.0/go.mod h1:MXLdDR43H7cDJq5GEGXEVeeNhPgi+YYEQ2pC1byI1x0= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-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.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= -golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -2733,142 +1132,52 @@ golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190227160552-c95aed5357e7/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-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190327091125-710a502c58a2/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-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 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-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191007182048-72f939374954/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-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 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-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d/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-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= -golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ= -golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= -golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= -golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= -golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= 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-20170207211851-4464e7848382/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/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-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= -golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= -golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.0.0-20221006150949-b44042a4b9c1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec= -golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= -golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= -golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= -golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= -golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= -golang.org/x/oauth2 v0.11.0/go.mod h1:LdF7O/8bLR/qWK9DrpXmbHLTouvRHK0SgJl0GmDBchk= -golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0= golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ= golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA= golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= -golang.org/x/perf v0.0.0-20230113213139-801c7ef9e5c5/go.mod h1:UBKtEnL8aqnd+0JHqZ+2qoMDwtuy6cYhhKNoHLBiTQc= 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.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180810173357-98c5dad5d1a0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -2885,635 +1194,154 @@ golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/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-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/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-20191008105621-543471e840be/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-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200107162124-548cf772de50/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-20200124204421-9fbb57f87de9/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-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200918174421-af09f7315aff/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/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-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210304124612-50617c2ba197/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-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210420205809-ac73e9fd8988/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603125802-9665404d3644/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-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211020174200-9d6173849985/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211210111614-af8b64212486/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-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/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-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/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-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/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-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= -golang.org/x/sys v0.23.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/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= -golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo= -golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= -golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= -golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= -golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= -golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= -golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= -golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= +golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= 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.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= -golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 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-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.3.0/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-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181221001348-537d06c36207/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-20190206041539-40960b6deb8e/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-20190327201419-c70d86f8b7cf/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-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-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/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-20191108193012-7d206e10da11/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-20191126055441-b0650ceb63d9/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-20200108203644-89082a384178/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-20201124115921-2c860bdd6e78/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-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.8-0.20211029000441-d6a9af8af023/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= -golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= -golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= -golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= -golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4= -golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= -golang.org/x/tools v0.10.0/go.mod h1:UJwyiVBsOA2uwvK/e5OY3GTpDUJriEd+/YlqAwLPmyM= -golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= -golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= 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-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= -gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= -gonum.org/v1/gonum v0.0.0-20181121035319-3f7ecaa7e8ca/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= -gonum.org/v1/gonum v0.6.0/go.mod h1:9mxDZsDKxgMAuccQkewq682L+0eCu4dCN2yonUJTCLU= -gonum.org/v1/gonum v0.6.1/go.mod h1:9mxDZsDKxgMAuccQkewq682L+0eCu4dCN2yonUJTCLU= -gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= -gonum.org/v1/gonum v0.9.3/go.mod h1:TZumC3NeyVQskjXqmyWt4S3bINhy7B4eYwW69EbyX+0= -gonum.org/v1/gonum v0.11.0/go.mod h1:fSG4YDCxxUZQJ7rKsQrj0gMOg00Il0Z96/qMA4bVQhA= -gonum.org/v1/gonum v0.13.0/go.mod h1:/WPYRckkfWrhWefxyYTfrTtQR0KH4iyHNuzxqXAKyAU= gonum.org/v1/gonum v0.14.0 h1:2NiG67LD1tEH0D7kM+ps2V+fXmsAnpUeec7n8tcr4S0= gonum.org/v1/gonum v0.14.0/go.mod h1:AoWeoz0becf9QMWtE8iWXNXc27fK4fNeHNf/oMejGfU= -gonum.org/v1/netlib v0.0.0-20181029234149-ec6d1f5cefe6/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= -gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= -gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= -gonum.org/v1/plot v0.9.0/go.mod h1:3Pcqqmp6RHvJI72kgb8fThyUnav364FOsdDo2aGW5lY= -gonum.org/v1/plot v0.10.0/go.mod h1:JWIHJ7U20drSQb/aDpTetJzfC1KlAPldJLpkSy88dvQ= -gonum.org/v1/plot v0.10.1/go.mod h1:VZW5OlhkL1mysU9vaqNHnsy86inf6Ot+jB3r+BczCEo= -google.golang.org/api v0.0.0-20170206182103-3d017632ea10/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= -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.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= -google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= -google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= -google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= -google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= -google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= -google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= -google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= -google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= -google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= -google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= -google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= -google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= -google.golang.org/api v0.77.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= -google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw= -google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg= -google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o= -google.golang.org/api v0.85.0/go.mod h1:AqZf8Ep9uZ2pyTvgL+x0D3Zt0eoT9b5E8fmzfu6FO2g= -google.golang.org/api v0.90.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= -google.golang.org/api v0.93.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= -google.golang.org/api v0.95.0/go.mod h1:eADj+UBuxkh5zlrSntJghuNeg8HwQ1w5lTKkuqaETEI= -google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= -google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= -google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= -google.golang.org/api v0.99.0/go.mod h1:1YOf74vkVndF7pG6hIHuINsM7eWwpVTAfNMNiL91A08= -google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70= -google.golang.org/api v0.102.0/go.mod h1:3VFl6/fzoA+qNuS1N1/VfXY4LjoXN/wzeIp7TweWwGo= -google.golang.org/api v0.103.0/go.mod h1:hGtW6nK1AC+d9si/UBhw8Xli+QMOf6xyNAyJw4qU9w0= -google.golang.org/api v0.106.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= -google.golang.org/api v0.107.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= -google.golang.org/api v0.108.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= -google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60ca7iI= -google.golang.org/api v0.111.0/go.mod h1:qtFHvU9mhgTJegR31csQ+rwxyUTHOKFqCKWp1J0fdw0= -google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg= -google.golang.org/api v0.118.0/go.mod h1:76TtD3vkgmZ66zZzp72bUUklpmQmKlhh6sYtIjYK+5E= -google.golang.org/api v0.122.0/go.mod h1:gcitW0lvnyWjSp9nKxAbdHKIZ6vF4aajGueeslZOyms= -google.golang.org/api v0.124.0/go.mod h1:xu2HQurE5gi/3t1aFCvhPD781p0a3p11sdunTJ2BlP4= -google.golang.org/api v0.125.0/go.mod h1:mBwVAtz+87bEN6CbA1GtZPDOqY2R5ONPqJeIlvyo4Aw= -google.golang.org/api v0.126.0/go.mod h1:mBwVAtz+87bEN6CbA1GtZPDOqY2R5ONPqJeIlvyo4Aw= -google.golang.org/api v0.128.0/go.mod h1:Y611qgqaE92On/7g65MQgxYul3c0rEB894kniWLY750= -google.golang.org/api v0.139.0/go.mod h1:CVagp6Eekz9CjGZ718Z+sloknzkDJE7Vc1Ckj9+viBk= -google.golang.org/api v0.149.0/go.mod h1:Mwn1B7JTXrzXtnvmzQE2BD6bYZQ8DShKZDZbeN9I7qI= -google.golang.org/api v0.151.0/go.mod h1:ccy+MJ6nrYFgE3WgRx/AMXOxOmU8Q4hSa+jjibzhxcg= google.golang.org/api v0.162.0 h1:Vhs54HkaEpkMBdgGdOT2P6F0csGG/vxDS0hWHJzmmps= google.golang.org/api v0.162.0/go.mod h1:6SulDkfoBIg4NFmCuZ39XeeAgSHCPecfSUuDyYlAHs0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 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/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= -google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -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-20190716160619-c506a9f90610/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -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-20200108215221-bd8f9a0ef82f/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-20210108203827-ffc7fda8c3d7/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-20210226172003-ab064af71705/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-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= -google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= -google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= -google.golang.org/genproto v0.0.0-20220329172620-7be39ac1afc7/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220722212130-b98a9ff5e252/go.mod h1:GkXuJDJ6aQ7lnJcRF+SJVgFdQhypqgl3LB1C9vabdRE= -google.golang.org/genproto v0.0.0-20220801145646-83ce21fca29f/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc= -google.golang.org/genproto v0.0.0-20220815135757-37a418bb8959/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220817144833-d7fd3f11b9b1/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220829144015-23454907ede3/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220913154956-18f8339a66a5/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220914142337-ca0e39ece12f/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220915135415-7fd63a7952de/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220916172020-2692e8806bfa/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220919141832-68c03719ef51/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006/go.mod h1:ht8XFiar2npT/g4vkk7O0WYS1sHOHbdujxbEp7CJWbw= -google.golang.org/genproto v0.0.0-20220926165614-551eb538f295/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= -google.golang.org/genproto v0.0.0-20220926220553-6981cbe3cfce/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= -google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqwhZAwq4wsRUaVG555sVgsNmIjRtO7t/JH29U= -google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= -google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= -google.golang.org/genproto v0.0.0-20221024153911-1573dae28c9c/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= -google.golang.org/genproto v0.0.0-20221024183307-1bc688fe9f3e/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= -google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c/go.mod h1:CGI5F/G+E5bKwmfYo09AXuVN4dD894kIKUFmVbP2/Fo= -google.golang.org/genproto v0.0.0-20221109142239-94d6d90a7d66/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221114212237-e4508ebdbee1/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221117204609-8f9c96812029/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221201204527-e3fa12d562f3/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd/go.mod h1:cTsE614GARnxrLsqKREzmNYJACSWWpAWdNMwnD7c2BE= -google.golang.org/genproto v0.0.0-20221207170731-23e4bf6bdc37/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230112194545-e10362b5ecf9/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230113154510-dbe35b8444a5/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230123190316-2c411cf9d197/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230124163310-31e0e69b6fc2/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230125152338-dcaf20b6aeaa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230127162408-596548ed4efa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230216225411-c8e22ba71e44/go.mod h1:8B0gmkoRebU8ukX6HP+4wrVQUY1+6PkQ44BSyIlflHA= -google.golang.org/genproto v0.0.0-20230222225845-10f96fb3dbec/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= -google.golang.org/genproto v0.0.0-20230223222841-637eb2293923/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= -google.golang.org/genproto v0.0.0-20230303212802-e74f57abe488/go.mod h1:TvhZT5f700eVlTNwND1xoEZQeWTB2RY/65kplwl/bFA= -google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= -google.golang.org/genproto v0.0.0-20230320184635-7606e756e683/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= -google.golang.org/genproto v0.0.0-20230323212658-478b75c54725/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= -google.golang.org/genproto v0.0.0-20230330154414-c0448cd141ea/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= -google.golang.org/genproto v0.0.0-20230331144136-dcfb400f0633/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= -google.golang.org/genproto v0.0.0-20230403163135-c38d8f061ccd/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= -google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= -google.golang.org/genproto v0.0.0-20230525234025-438c736192d0/go.mod h1:9ExIQyXL5hZrHzQceCwuSYwZZ5QZBazOcprJ5rgs3lY= -google.golang.org/genproto v0.0.0-20230526161137-0005af68ea54/go.mod h1:zqTuNwFlFRsw5zIts5VnzLQxSRqh+CGOTVMlYbY0Eyk= -google.golang.org/genproto v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:zqTuNwFlFRsw5zIts5VnzLQxSRqh+CGOTVMlYbY0Eyk= -google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:xZnkP7mREFX5MORlOPEzLMr+90PPZQ2QWzrVTWfAq64= -google.golang.org/genproto v0.0.0-20230629202037-9506855d4529/go.mod h1:xZnkP7mREFX5MORlOPEzLMr+90PPZQ2QWzrVTWfAq64= -google.golang.org/genproto v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:O9kGHb51iE/nOGvQaDUuadVYqovW56s5emA88lQnj6Y= -google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98/go.mod h1:S7mY02OqCJTD0E1OiQy1F72PWFB4bZJ87cAtLPYgDR0= -google.golang.org/genproto v0.0.0-20230726155614-23370e0ffb3e/go.mod h1:0ggbjUrZYpy1q+ANUS30SEoGZ53cdfwtbuG7Ptgy108= -google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5/go.mod h1:oH/ZOT02u4kWEp7oYBGYFFkCdKS/uYR9Z7+0/xuuFp8= -google.golang.org/genproto v0.0.0-20230821184602-ccc8af3d0e93/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4= -google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4= -google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4= -google.golang.org/genproto v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:CCviP9RmpZ1mxVr8MUjCnSiY09IbAXZxhLE6EhHIdPU= -google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97/go.mod h1:t1VqOqqvce95G3hIDCT5FeO3YUc6Q4Oe24L/+rNMxRk= -google.golang.org/genproto v0.0.0-20231012201019-e917dd12ba7a/go.mod h1:EMfReVxb80Dq1hhioy0sOsY9jCE46YDgHlJ7fWVUWRE= -google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:CgAqfJo+Xmu0GwA0411Ht3OU3OntXwsGmrmjI8ioGXI= google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY= google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo= -google.golang.org/genproto/googleapis/api v0.0.0-20230525234020-1aefcd67740a/go.mod h1:ts19tUU+Z0ZShN1y3aPyq2+O3d5FUNNgT6FtOzmrNn8= -google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= -google.golang.org/genproto/googleapis/api v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= -google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= -google.golang.org/genproto/googleapis/api v0.0.0-20230629202037-9506855d4529/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= -google.golang.org/genproto/googleapis/api v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:mPBs5jNgx2GuQGvFwUvVKqtn6HsUw9nP64BedgvqEsQ= -google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ= -google.golang.org/genproto/googleapis/api v0.0.0-20230726155614-23370e0ffb3e/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ= -google.golang.org/genproto/googleapis/api v0.0.0-20230803162519-f966b187b2e5/go.mod h1:5DZzOUPCLYL3mNkQ0ms0F3EuUNZ7py1Bqeq6sxzI7/Q= -google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk= -google.golang.org/genproto/googleapis/api v0.0.0-20230913181813-007df8e322eb/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk= -google.golang.org/genproto/googleapis/api v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:RdyHbowztCGQySiCvQPgWQWgWhGnouTdCflKoDBt32U= -google.golang.org/genproto/googleapis/api v0.0.0-20231002182017-d307bd883b97/go.mod h1:iargEX0SFPm3xcfMI0d1domjg0ZF4Aa0p2awqyxhvF0= -google.golang.org/genproto/googleapis/api v0.0.0-20231012201019-e917dd12ba7a/go.mod h1:SUBoKXbI1Efip18FClrQVGjWcyd0QZd8KkvdP34t7ww= -google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:IBQ646DjkDkvUIsVq/cc03FUFQ9wbZu7yE396YcL870= google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de h1:jFNzHPIeuzhdRwVhbZdiym9q0ory/xY3sA+v2wPg8I0= google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:5iCWqnniDlqZHrd3neWVTOwvh/v6s3232omMecelax8= -google.golang.org/genproto/googleapis/bytestream v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:ylj+BE99M198VPbBh6A8d9n3w8fChvyLK3wwBOjXBFA= -google.golang.org/genproto/googleapis/bytestream v0.0.0-20230807174057-1744710a1577/go.mod h1:NjCQG/D8JandXxM57PZbAJL1DCNL6EypA0vPPwfsc7c= -google.golang.org/genproto/googleapis/bytestream v0.0.0-20231030173426-d783a09b4405/go.mod h1:GRUCuLdzVqZte8+Dl/D4N25yLzcGqqWaYkeVOwulFqw= google.golang.org/genproto/googleapis/bytestream v0.0.0-20240125205218-1f4bbc51befe h1:weYsP+dNijSQVoLAb5bpUos3ciBpNU/NEVlHFKrk8pg= google.golang.org/genproto/googleapis/bytestream v0.0.0-20240125205218-1f4bbc51befe/go.mod h1:SCz6T5xjNXM4QFPRwxHcfChp7V+9DcXR3ay2TkHR8Tg= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234015-3fc162c6f38a/go.mod h1:xURIpW9ES5+/GZhnV6beoEtxQrnkRGIfP5VQG2tCBLc= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230629202037-9506855d4529/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:8mL13HKkDa+IuJ8yruA3ci0q+0vsUz4m//+ottjwS5o= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230731190214-cbb8c96f2d6d/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230803162519-f966b187b2e5/go.mod h1:zBEcrKX2ZOcEkHWxBPAIvYUWOKKMIhYcmNiUIu2ji3I= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230920183334-c177e329c48b/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:KSqppvjFjtoCI+KGd4PELB0qLNxdJHRGqRI09mB6pQA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97/go.mod h1:v7nGkzlmW8P3n/bKmWBn2WpBjpOEx8Q6gMueudAmKfY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231012201019-e917dd12ba7a/go.mod h1:4cYg8o5yUbm77w8ZX00LhMVNl/YVBFJRYWDc0uYWMs0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:swOH3j0KzcDDgGUWr+SNpyTen5YrXjS3eyPzFYKc6lc= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405/go.mod h1:67X1fPuzjcrkymZzZV1vvkFeTn2Rvc6lYF9MYFGCcwE= google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de h1:cZGRis4/ot9uVm639a+rHCUaG0JJHEsdyzSQTMX+suY= google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY= -google.golang.org/grpc v0.0.0-20170208002647-2a6bf6142e96/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= -google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= 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.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -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.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= -google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= -google.golang.org/grpc v1.52.0/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY= -google.golang.org/grpc v1.52.3/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY= -google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= -google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= -google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= -google.golang.org/grpc v1.56.1/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= -google.golang.org/grpc v1.56.2/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= -google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= -google.golang.org/grpc v1.58.2/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= -google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= -google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0 h1:TLkBREm4nIsEcexnCjgQd5GQWaHcqMzwQV0TX9pq8S0= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0/go.mod h1:DNq5QpG7LJqD2AamLZ7zvKE0DEpVl2BSEVjFycAAjRY= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= @@ -3524,15 +1352,10 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi 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.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= @@ -3540,22 +1363,14 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks 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-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/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/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= -gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= 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/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= -gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= -gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98= 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/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= @@ -3563,16 +1378,13 @@ gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRN gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 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.5/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.0/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= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= @@ -3585,82 +1397,12 @@ grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJd honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-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= -honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= -lukechampine.com/blake3 v1.1.7/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA= -lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE= lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= -lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= -lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= -modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= -modernc.org/cc/v3 v3.36.2/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= -modernc.org/cc/v3 v3.36.3/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= -modernc.org/cc/v3 v3.37.0/go.mod h1:vtL+3mdHx/wcj3iEGz84rQa8vEqR6XM84v5Lcvfph20= -modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0= -modernc.org/ccgo/v3 v3.0.0-20220428102840-41399a37e894/go.mod h1:eI31LL8EwEBKPpNpA4bU1/i+sKOwOrQy8D87zWUcRZc= -modernc.org/ccgo/v3 v3.0.0-20220430103911-bc99d88307be/go.mod h1:bwdAnOoaIt8Ax9YdWGjxWsdkPcZyRPHqrOvJxaKAKGw= -modernc.org/ccgo/v3 v3.0.0-20220904174949-82d86e1b6d56/go.mod h1:YSXjPL62P2AMSxBphRHPn7IkzhVHqkvOnRKAKh+W6ZI= -modernc.org/ccgo/v3 v3.16.4/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= -modernc.org/ccgo/v3 v3.16.6/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= -modernc.org/ccgo/v3 v3.16.8/go.mod h1:zNjwkizS+fIFDrDjIAgBSCLkWbJuHF+ar3QRn+Z9aws= -modernc.org/ccgo/v3 v3.16.9/go.mod h1:zNMzC9A9xeNUepy6KuZBbugn3c0Mc9TeiJO4lgvkJDo= -modernc.org/ccgo/v3 v3.16.13-0.20221017192402-261537637ce8/go.mod h1:fUB3Vn0nVPReA+7IG7yZDfjv1TMWjhQP8gCxrFAtL5g= -modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY= -modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= -modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= -modernc.org/libc v0.0.0-20220428101251-2d5f3daf273b/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= -modernc.org/libc v1.16.0/go.mod h1:N4LD6DBE9cf+Dzf9buBlzVJndKr/iJHG97vGLHYnb5A= -modernc.org/libc v1.16.1/go.mod h1:JjJE0eu4yeK7tab2n4S1w8tlWd9MxXLRzheaRnAKymU= -modernc.org/libc v1.16.17/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU= -modernc.org/libc v1.16.19/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= -modernc.org/libc v1.17.0/go.mod h1:XsgLldpP4aWlPlsjqKRdHPqCxCjISdHfM/yeWC5GyW0= -modernc.org/libc v1.17.1/go.mod h1:FZ23b+8LjxZs7XtFMbSzL/EhPxNbfZbErxEHc7cbD9s= -modernc.org/libc v1.17.4/go.mod h1:WNg2ZH56rDEwdropAJeZPQkXmDwh+JCA1s/htl6r2fA= -modernc.org/libc v1.18.0/go.mod h1:vj6zehR5bfc98ipowQOM2nIDUZnVew/wNC/2tOGS+q0= -modernc.org/libc v1.20.3/go.mod h1:ZRfIaEkgrYgZDl6pa4W39HgN5G/yDW+NRmNKZBDFrk0= -modernc.org/libc v1.21.4/go.mod h1:przBsL5RDOZajTVslkugzLBj1evTue36jEomFQOoYuI= -modernc.org/libc v1.22.2/go.mod h1:uvQavJ1pZ0hIoC/jfqNoMLURIMhKzINIWypNM17puug= -modernc.org/libc v1.37.6 h1:orZH3c5wmhIQFTXF+Nt+eeauyd+ZIt2BX6ARe+kD+aw= -modernc.org/libc v1.37.6/go.mod h1:YAXkAZ8ktnkCKaN9sw/UDeUVkGYJ/YquGO4FTi5nmHE= -modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= -modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= -modernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= -modernc.org/memory v1.2.0/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= -modernc.org/memory v1.2.1/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= -modernc.org/memory v1.3.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= -modernc.org/memory v1.4.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= -modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= -modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E= -modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E= -modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/sqlite v1.18.1/go.mod h1:6ho+Gow7oX5V+OiOQ6Tr4xeqbx13UZ6t+Fw9IRUG4d4= -modernc.org/sqlite v1.18.2/go.mod h1:kvrTLEWgxUcHa2GfHBQtanR1H9ht3hTJNtKpzH9k1u0= -modernc.org/sqlite v1.28.0 h1:Zx+LyDDmXczNnEQdvPuEfcFVA2ZPyaD7UCZDjef3BHQ= -modernc.org/sqlite v1.28.0/go.mod h1:Qxpazz0zH8Z1xCFyi5GSL3FzbtZ3fvbjmywNogldEW0= -modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= -modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= -modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw= -modernc.org/tcl v1.13.2/go.mod h1:7CLiGIPo1M8Rv1Mitpv5akc2+8fxUd2y2UzC/MfMzy0= -modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= -modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= -modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= -modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8= -pgregory.net/rapid v0.4.7/go.mod h1:UYpPVyjFHzYBGHIxLFoupi8vwk6rXNzRY9OMvVxFIOU= pgregory.net/rapid v1.1.0 h1:CMa0sjHSru3puNx+J0MIAuiiEV4N0qj8/cMWGBBCsjw= pgregory.net/rapid v1.1.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= -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= sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= diff --git a/integration/internal/emulator/blockTicker.go b/integration/internal/emulator/blockTicker.go new file mode 100644 index 00000000000..40ef1ce7e34 --- /dev/null +++ b/integration/internal/emulator/blockTicker.go @@ -0,0 +1,56 @@ +/* + * Flow Emulator + * + * Copyright Flow Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package emulator + +import ( + "time" +) + +type BlocksTicker struct { + emulator Emulator + ticker *time.Ticker + done chan bool +} + +func NewBlocksTicker( + emulator Emulator, + blockTime time.Duration, +) *BlocksTicker { + return &BlocksTicker{ + emulator: emulator, + ticker: time.NewTicker(blockTime), + done: make(chan bool, 1), + } +} + +func (t *BlocksTicker) Start() error { + for { + select { + case <-t.ticker.C: + _, _ = t.emulator.ExecuteBlock() + _, _ = t.emulator.CommitBlock() + case <-t.done: + return nil + } + } +} + +func (t *BlocksTicker) Stop() { + t.done <- true +} diff --git a/integration/internal/emulator/blockchain.go b/integration/internal/emulator/blockchain.go new file mode 100644 index 00000000000..b16a042f868 --- /dev/null +++ b/integration/internal/emulator/blockchain.go @@ -0,0 +1,1075 @@ +/* + * Flow Emulator + * + * Copyright Flow Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Package emulator provides an emulated version of the Flow emulator that can be used +// for development purposes. +// +// This package can be used as a library or as a standalone application. +// +// When used as a library, this package provides tools to write programmatic tests for +// Flow applications. +// +// When used as a standalone application, this package implements the Flow Access API +// and is fully-compatible with Flow gRPC client libraries. +package emulator + +import ( + "context" + _ "embed" + "encoding/hex" + "errors" + "fmt" + "strings" + "sync" + "time" + + "github.com/rs/zerolog" + + "github.com/onflow/cadence" + "github.com/onflow/cadence/runtime" + + "github.com/onflow/flow-core-contracts/lib/go/templates" + + "github.com/onflow/flow-go/access" + "github.com/onflow/flow-go/engine" + "github.com/onflow/flow-go/fvm" + "github.com/onflow/flow-go/fvm/environment" + fvmerrors "github.com/onflow/flow-go/fvm/errors" + reusableRuntime "github.com/onflow/flow-go/fvm/runtime" + flowgo "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/module/metrics" +) + +// systemChunkTransactionTemplate looks for the RandomBeaconHistory +// heartbeat resource on the service account and calls it. +// +//go:embed templates/systemChunkTransactionTemplate.cdc +var systemChunkTransactionTemplate string + +var _ Emulator = &Blockchain{} + +// New instantiates a new emulated emulator with the provided options. +func New(opts ...Option) (*Blockchain, error) { + + // apply options to the default config + conf := defaultConfig + for _, opt := range opts { + opt(&conf) + } + b := &Blockchain{ + storage: conf.GetStore(), + broadcaster: engine.NewBroadcaster(), + serviceKey: conf.GetServiceKey(), + conf: conf, + entropyProvider: &blockHashEntropyProvider{}, + } + return b.ReloadBlockchain() +} + +func (b *Blockchain) Now() time.Time { + if b.clockOverride != nil { + return b.clockOverride() + } + return time.Now().UTC() +} + +// Blockchain emulates the functionality of the Flow emulator. +type Blockchain struct { + // committed chain state: blocks, transactions, registers, events + storage EmulatorStorage + broadcaster *engine.Broadcaster + + // mutex protecting pending block + mu sync.RWMutex + + // pending block containing block info, register state, pending transactions + pendingBlock *pendingBlock + clockOverride func() time.Time + // used to execute transactions and scripts + vm *fvm.VirtualMachine + vmCtx fvm.Context + transactionValidator *access.TransactionValidator + serviceKey ServiceKey + conf config + entropyProvider *blockHashEntropyProvider +} + +func (b *Blockchain) Broadcaster() *engine.Broadcaster { + return b.broadcaster +} + +func (b *Blockchain) ReloadBlockchain() (*Blockchain, error) { + + b.vm = fvm.NewVirtualMachine() + b.vmCtx = fvm.NewContext( + fvm.WithLogger(b.conf.Logger), + fvm.WithCadenceLogging(true), + fvm.WithChain(b.conf.GetChainID().Chain()), + fvm.WithBlocks(b.storage), + fvm.WithContractDeploymentRestricted(false), + fvm.WithContractRemovalRestricted(!b.conf.ContractRemovalEnabled), + fvm.WithComputationLimit(b.conf.ScriptGasLimit), + fvm.WithAccountStorageLimit(b.conf.StorageLimitEnabled), + fvm.WithTransactionFeesEnabled(b.conf.TransactionFeesEnabled), + fvm.WithReusableCadenceRuntimePool( + reusableRuntime.NewReusableCadenceRuntimePool( + 0, + runtime.Config{}), + ), + fvm.WithEntropyProvider(b.entropyProvider), + fvm.WithEVMEnabled(true), + fvm.WithAuthorizationChecksEnabled(b.conf.TransactionValidationEnabled), + fvm.WithSequenceNumberCheckAndIncrementEnabled(b.conf.TransactionValidationEnabled), + ) + + latestBlock, latestLedger, err := configureLedger( + b.conf, + b.storage, + b.vm, + b.vmCtx) + if err != nil { + return nil, err + } + + b.pendingBlock = newPendingBlock(latestBlock, latestLedger, b.Now()) + err = b.configureTransactionValidator() + if err != nil { + return nil, err + } + + return b, nil +} + +func (b *Blockchain) EnableAutoMine() { + b.conf.AutoMine = true +} + +func (b *Blockchain) DisableAutoMine() { + b.conf.AutoMine = false +} + +func (b *Blockchain) Ping() error { + return nil +} + +func (b *Blockchain) GetChain() flowgo.Chain { + return b.vmCtx.Chain +} + +func (b *Blockchain) GetNetworkParameters() access.NetworkParameters { + return access.NetworkParameters{ + ChainID: b.GetChain().ChainID(), + } +} + +// `blockHashEntropyProvider implements `environment.EntropyProvider` +// which provides a source of entropy to fvm context (required for Cadence's randomness), +// by using the latest block hash. +type blockHashEntropyProvider struct { + LatestBlock flowgo.Identifier +} + +func (gen *blockHashEntropyProvider) RandomSource() ([]byte, error) { + return gen.LatestBlock[:], nil +} + +// make sure `blockHashEntropyProvider implements `environment.EntropyProvider` +var _ environment.EntropyProvider = &blockHashEntropyProvider{} + +func (b *Blockchain) configureTransactionValidator() error { + validator, err := access.NewTransactionValidator( + b.storage, + b.conf.GetChainID().Chain(), + metrics.NewNoopCollector(), + access.TransactionValidationOptions{ + Expiry: b.conf.TransactionExpiry, + ExpiryBuffer: 0, + AllowEmptyReferenceBlockID: b.conf.TransactionExpiry == 0, + AllowUnknownReferenceBlockID: false, + MaxGasLimit: b.conf.TransactionMaxGasLimit, + CheckScriptsParse: true, + MaxTransactionByteSize: flowgo.DefaultMaxTransactionByteSize, + MaxCollectionByteSize: flowgo.DefaultMaxCollectionByteSize, + CheckPayerBalanceMode: access.Disabled, + }, + nil, + ) + if err != nil { + return err + } + b.transactionValidator = validator + return nil +} + +func (b *Blockchain) setFVMContextFromHeader(header *flowgo.Header) fvm.Context { + b.vmCtx = fvm.NewContextFromParent( + b.vmCtx, + fvm.WithBlockHeader(header), + ) + return b.vmCtx +} + +// ServiceKey returns the service private key for this emulator. +func (b *Blockchain) ServiceKey() ServiceKey { + serviceAccount, err := b.getAccount(b.serviceKey.Address) + if err != nil { + return b.serviceKey + } + + if len(serviceAccount.Keys) > 0 { + b.serviceKey.Index = 0 + b.serviceKey.SequenceNumber = serviceAccount.Keys[0].SeqNumber + b.serviceKey.Weight = serviceAccount.Keys[0].Weight + } + + return b.serviceKey +} + +// PendingBlockID returns the ID of the pending block. +func (b *Blockchain) PendingBlockID() flowgo.Identifier { + return b.pendingBlock.ID() +} + +// PendingBlockView returns the view of the pending block. +func (b *Blockchain) PendingBlockView() uint64 { + return b.pendingBlock.view +} + +// PendingBlockTimestamp returns the Timestamp of the pending block. +func (b *Blockchain) PendingBlockTimestamp() time.Time { + return b.pendingBlock.Block().Header.Timestamp +} + +// GetLatestBlock gets the latest sealed block. +func (b *Blockchain) GetLatestBlock() (*flowgo.Block, error) { + b.mu.RLock() + defer b.mu.RUnlock() + return b.getLatestBlock() +} + +func (b *Blockchain) getLatestBlock() (*flowgo.Block, error) { + block, err := b.storage.LatestBlock(context.Background()) + if err != nil { + return nil, err + } + + return &block, nil +} + +// GetBlockByID gets a block by ID. +func (b *Blockchain) GetBlockByID(id flowgo.Identifier) (*flowgo.Block, error) { + b.mu.RLock() + defer b.mu.RUnlock() + return b.getBlockByID(id) +} + +func (b *Blockchain) getBlockByID(id flowgo.Identifier) (*flowgo.Block, error) { + block, err := b.storage.BlockByID(context.Background(), id) + if err != nil { + if errors.Is(err, ErrNotFound) { + return nil, &BlockNotFoundByIDError{ID: id} + } + + return nil, err + } + + return block, nil +} + +// GetBlockByHeight gets a block by height. +func (b *Blockchain) GetBlockByHeight(height uint64) (*flowgo.Block, error) { + b.mu.RLock() + defer b.mu.RUnlock() + + block, err := b.getBlockByHeight(height) + if err != nil { + return nil, err + } + + return block, nil +} + +func (b *Blockchain) getBlockByHeight(height uint64) (*flowgo.Block, error) { + block, err := b.storage.BlockByHeight(context.Background(), height) + if err != nil { + if errors.Is(err, ErrNotFound) { + return nil, &BlockNotFoundByHeightError{Height: height} + } + return nil, err + } + + return block, nil +} + +func (b *Blockchain) GetCollectionByID(colID flowgo.Identifier) (*flowgo.LightCollection, error) { + b.mu.RLock() + defer b.mu.RUnlock() + return b.getCollectionByID(colID) +} + +func (b *Blockchain) getCollectionByID(colID flowgo.Identifier) (*flowgo.LightCollection, error) { + col, err := b.storage.CollectionByID(context.Background(), colID) + if err != nil { + if errors.Is(err, ErrNotFound) { + return nil, &CollectionNotFoundError{ID: colID} + } + return nil, err + } + + return &col, nil +} + +func (b *Blockchain) GetFullCollectionByID(colID flowgo.Identifier) (*flowgo.Collection, error) { + b.mu.RLock() + defer b.mu.RUnlock() + return b.getFullCollectionByID(colID) +} + +func (b *Blockchain) getFullCollectionByID(colID flowgo.Identifier) (*flowgo.Collection, error) { + col, err := b.storage.FullCollectionByID(context.Background(), colID) + if err != nil { + if errors.Is(err, ErrNotFound) { + return nil, &CollectionNotFoundError{ID: colID} + } + return nil, err + } + + return &col, nil +} + +// GetTransaction gets an existing transaction by ID. +// +// The function first looks in the pending block, then the current emulator state. +func (b *Blockchain) GetTransaction(txID flowgo.Identifier) (*flowgo.TransactionBody, error) { + b.mu.RLock() + defer b.mu.RUnlock() + return b.getTransaction(txID) +} + +func (b *Blockchain) getTransaction(txID flowgo.Identifier) (*flowgo.TransactionBody, error) { + pendingTx := b.pendingBlock.GetTransaction(txID) + if pendingTx != nil { + return pendingTx, nil + } + + tx, err := b.storage.TransactionByID(context.Background(), txID) + if err != nil { + if errors.Is(err, ErrNotFound) { + return nil, &TransactionNotFoundError{ID: txID} + } + return nil, err + } + + return &tx, nil +} + +func (b *Blockchain) GetTransactionResult(txID flowgo.Identifier) (*access.TransactionResult, error) { + b.mu.RLock() + defer b.mu.RUnlock() + + return b.getTransactionResult(txID) +} + +func (b *Blockchain) getTransactionResult(txID flowgo.Identifier) (*access.TransactionResult, error) { + if b.pendingBlock.ContainsTransaction(txID) { + return &access.TransactionResult{ + Status: flowgo.TransactionStatusPending, + }, nil + } + + storedResult, err := b.storage.TransactionResultByID(context.Background(), txID) + if err != nil { + if errors.Is(err, ErrNotFound) { + return &access.TransactionResult{ + Status: flowgo.TransactionStatusUnknown, + }, nil + } + return nil, err + } + + statusCode := 0 + if storedResult.ErrorCode > 0 { + statusCode = 1 + } + result := access.TransactionResult{ + Status: flowgo.TransactionStatusSealed, + StatusCode: uint(statusCode), + ErrorMessage: storedResult.ErrorMessage, + Events: storedResult.Events, + TransactionID: txID, + BlockHeight: storedResult.BlockHeight, + BlockID: storedResult.BlockID, + } + + return &result, nil +} + +// GetAccountByIndex returns the account for the given address. +func (b *Blockchain) GetAccountByIndex(index uint) (*flowgo.Account, error) { + + address, err := b.vmCtx.Chain.ChainID().Chain().AddressAtIndex(uint64(index)) + if err != nil { + return nil, err + } + + latestBlock, err := b.getLatestBlock() + if err != nil { + return nil, err + } + return b.getAccountAtBlock(address, latestBlock.Header.Height) + +} + +// GetAccount returns the account for the given address. +func (b *Blockchain) GetAccount(address flowgo.Address) (*flowgo.Account, error) { + b.mu.RLock() + defer b.mu.RUnlock() + return b.getAccount(address) +} + +// getAccount returns the account for the given address. +func (b *Blockchain) getAccount(address flowgo.Address) (*flowgo.Account, error) { + latestBlock, err := b.getLatestBlock() + if err != nil { + return nil, err + } + return b.getAccountAtBlock(address, latestBlock.Header.Height) +} + +// GetAccountAtBlockHeight returns the account for the given address at specified block height. +func (b *Blockchain) GetAccountAtBlockHeight(address flowgo.Address, blockHeight uint64) (*flowgo.Account, error) { + b.mu.RLock() + defer b.mu.RUnlock() + + account, err := b.getAccountAtBlock(address, blockHeight) + if err != nil { + return nil, err + } + + return account, nil +} + +// GetAccountAtBlock returns the account for the given address at specified block height. +func (b *Blockchain) getAccountAtBlock(address flowgo.Address, blockHeight uint64) (*flowgo.Account, error) { + ledger, err := b.storage.LedgerByHeight(context.Background(), blockHeight) + if err != nil { + return nil, err + } + + account, err := fvm.GetAccount(b.vmCtx, address, ledger) + if fvmerrors.IsAccountNotFoundError(err) { + return nil, &AccountNotFoundError{Address: address} + } + + return account, nil +} + +func (b *Blockchain) GetEventsForBlockIDs(eventType string, blockIDs []flowgo.Identifier) (result []flowgo.BlockEvents, err error) { + b.mu.RLock() + defer b.mu.RUnlock() + + for _, blockID := range blockIDs { + block, err := b.storage.BlockByID(context.Background(), blockID) + if err != nil { + break + } + events, err := b.storage.EventsByHeight(context.Background(), block.Header.Height, eventType) + if err != nil { + break + } + result = append(result, flowgo.BlockEvents{ + BlockID: block.ID(), + BlockHeight: block.Header.Height, + BlockTimestamp: block.Header.Timestamp, + Events: events, + }) + } + + return result, err +} + +func (b *Blockchain) GetEventsForHeightRange(eventType string, startHeight, endHeight uint64) (result []flowgo.BlockEvents, err error) { + b.mu.RLock() + defer b.mu.RUnlock() + + for blockHeight := startHeight; blockHeight <= endHeight; blockHeight++ { + block, err := b.storage.BlockByHeight(context.Background(), blockHeight) + if err != nil { + break + } + + events, err := b.storage.EventsByHeight(context.Background(), blockHeight, eventType) + if err != nil { + break + } + + result = append(result, flowgo.BlockEvents{ + BlockID: block.ID(), + BlockHeight: block.Header.Height, + BlockTimestamp: block.Header.Timestamp, + Events: events, + }) + } + + return result, err +} + +// GetEventsByHeight returns the events in the block at the given height, optionally filtered by type. +func (b *Blockchain) GetEventsByHeight(blockHeight uint64, eventType string) ([]flowgo.Event, error) { + b.mu.RLock() + defer b.mu.RUnlock() + + return b.storage.EventsByHeight(context.Background(), blockHeight, eventType) +} + +// SendTransaction submits a transaction to the network. +func (b *Blockchain) SendTransaction(flowTx *flowgo.TransactionBody) error { + b.mu.Lock() + defer b.mu.Unlock() + + err := b.addTransaction(*flowTx) + if err != nil { + return err + } + + if b.conf.AutoMine { + _, _, err := b.executeAndCommitBlock() + if err != nil { + return err + } + } + + return nil +} + +// AddTransaction validates a transaction and adds it to the current pending block. +func (b *Blockchain) AddTransaction(tx flowgo.TransactionBody) error { + b.mu.Lock() + defer b.mu.Unlock() + + return b.addTransaction(tx) +} + +func (b *Blockchain) addTransaction(tx flowgo.TransactionBody) error { + + // If index > 0, pending block has begun execution (cannot add more transactions) + if b.pendingBlock.ExecutionStarted() { + return &PendingBlockMidExecutionError{BlockID: b.pendingBlock.ID()} + } + + if b.pendingBlock.ContainsTransaction(tx.ID()) { + return &DuplicateTransactionError{TxID: tx.ID()} + } + + _, err := b.storage.TransactionByID(context.Background(), tx.ID()) + if err == nil { + // Found the transaction, this is a duplicate + return &DuplicateTransactionError{TxID: tx.ID()} + } else if !errors.Is(err, ErrNotFound) { + // Error in the storage provider + return fmt.Errorf("failed to check storage for transaction %w", err) + } + + err = b.transactionValidator.Validate(context.Background(), &tx) + if err != nil { + return ConvertAccessError(err) + } + + // add transaction to pending block + b.pendingBlock.AddTransaction(tx) + + return nil +} + +// ExecuteBlock executes the remaining transactions in pending block. +func (b *Blockchain) ExecuteBlock() ([]*TransactionResult, error) { + b.mu.Lock() + defer b.mu.Unlock() + + return b.executeBlock() +} + +func (b *Blockchain) executeBlock() ([]*TransactionResult, error) { + results := make([]*TransactionResult, 0) + + // empty blocks do not require execution, treat as a no-op + if b.pendingBlock.Empty() { + return results, nil + } + + header := b.pendingBlock.Block().Header + blockContext := b.setFVMContextFromHeader(header) + + // cannot execute a block that has already executed + if b.pendingBlock.ExecutionComplete() { + return results, &PendingBlockTransactionsExhaustedError{ + BlockID: b.pendingBlock.ID(), + } + } + + // continue executing transactions until execution is complete + for !b.pendingBlock.ExecutionComplete() { + result, err := b.executeNextTransaction(blockContext) + if err != nil { + return results, err + } + + results = append(results, result) + } + + return results, nil +} + +// ExecuteNextTransaction executes the next indexed transaction in pending block. +func (b *Blockchain) ExecuteNextTransaction() (*TransactionResult, error) { + b.mu.Lock() + defer b.mu.Unlock() + + header := b.pendingBlock.Block().Header + blockContext := b.setFVMContextFromHeader(header) + return b.executeNextTransaction(blockContext) +} + +// executeNextTransaction is a helper function for ExecuteBlock and ExecuteNextTransaction that +// executes the next transaction in the pending block. +func (b *Blockchain) executeNextTransaction(ctx fvm.Context) (*TransactionResult, error) { + // check if there are remaining txs to be executed + if b.pendingBlock.ExecutionComplete() { + return nil, &PendingBlockTransactionsExhaustedError{ + BlockID: b.pendingBlock.ID(), + } + } + + txnBody := b.pendingBlock.NextTransaction() + txnId := txnBody.ID() + + // use the computer to execute the next transaction + output, err := b.pendingBlock.ExecuteNextTransaction(b.vm, ctx) + if err != nil { + // fail fast if fatal error occurs + return nil, err + } + + tr, err := VMTransactionResultToEmulator(txnId, output) + if err != nil { + // fail fast if fatal error occurs + return nil, err + } + + return tr, nil +} + +// CommitBlock seals the current pending block and saves it to storage. +// +// This function clears the pending transaction pool and resets the pending block. +func (b *Blockchain) CommitBlock() (*flowgo.Block, error) { + b.mu.Lock() + defer b.mu.Unlock() + + block, err := b.commitBlock() + if err != nil { + return nil, err + } + + return block, nil +} + +func (b *Blockchain) commitBlock() (*flowgo.Block, error) { + // pending block cannot be committed before execution starts (unless empty) + if !b.pendingBlock.ExecutionStarted() && !b.pendingBlock.Empty() { + return nil, &PendingBlockCommitBeforeExecutionError{BlockID: b.pendingBlock.ID()} + } + + // pending block cannot be committed before execution completes + if b.pendingBlock.ExecutionStarted() && !b.pendingBlock.ExecutionComplete() { + return nil, &PendingBlockMidExecutionError{BlockID: b.pendingBlock.ID()} + } + + block := b.pendingBlock.Block() + collections := b.pendingBlock.Collections() + transactions := b.pendingBlock.Transactions() + transactionResults, err := convertToSealedResults(b.pendingBlock.TransactionResults(), b.pendingBlock.ID(), b.pendingBlock.height) + if err != nil { + return nil, err + } + + // lastly we execute the system chunk transaction + err = b.executeSystemChunkTransaction() + if err != nil { + return nil, err + } + + executionSnapshot := b.pendingBlock.Finalize() + events := b.pendingBlock.Events() + + // commit the pending block to storage + err = b.storage.CommitBlock( + context.Background(), + *block, + collections, + transactions, + transactionResults, + executionSnapshot, + events) + if err != nil { + return nil, err + } + + ledger, err := b.storage.LedgerByHeight( + context.Background(), + block.Header.Height, + ) + if err != nil { + return nil, err + } + + // notify listeners on new block + b.broadcaster.Publish() + + // reset pending block using current block and ledger state + b.pendingBlock = newPendingBlock(block, ledger, b.Now()) + b.entropyProvider.LatestBlock = block.ID() + + return block, nil +} + +// ExecuteAndCommitBlock is a utility that combines ExecuteBlock with CommitBlock. +func (b *Blockchain) ExecuteAndCommitBlock() (*flowgo.Block, []*TransactionResult, error) { + b.mu.Lock() + defer b.mu.Unlock() + + return b.executeAndCommitBlock() +} + +// ExecuteAndCommitBlock is a utility that combines ExecuteBlock with CommitBlock. +func (b *Blockchain) executeAndCommitBlock() (*flowgo.Block, []*TransactionResult, error) { + + results, err := b.executeBlock() + if err != nil { + return nil, nil, err + } + + block, err := b.commitBlock() + if err != nil { + return nil, results, err + } + + blockID := block.ID() + b.conf.ServerLogger.Debug().Fields(map[string]any{ + "blockHeight": block.Header.Height, + "blockID": hex.EncodeToString(blockID[:]), + }).Msgf("📦 Block #%d committed", block.Header.Height) + + return block, results, nil +} + +// ResetPendingBlock clears the transactions in pending block. +func (b *Blockchain) ResetPendingBlock() error { + b.mu.Lock() + defer b.mu.Unlock() + + latestBlock, err := b.storage.LatestBlock(context.Background()) + if err != nil { + return err + } + + latestLedger, err := b.storage.LedgerByHeight( + context.Background(), + latestBlock.Header.Height, + ) + if err != nil { + return err + } + + // reset pending block using latest committed block and ledger state + b.pendingBlock = newPendingBlock(&latestBlock, latestLedger, b.Now()) + + return nil +} + +// ExecuteScript executes a read-only script against the world state and returns the result. +func (b *Blockchain) ExecuteScript( + script []byte, + arguments [][]byte, +) (*ScriptResult, error) { + b.mu.RLock() + defer b.mu.RUnlock() + + latestBlock, err := b.getLatestBlock() + if err != nil { + return nil, err + } + + return b.executeScriptAtBlockID(script, arguments, latestBlock.Header.ID()) +} + +func (b *Blockchain) ExecuteScriptAtBlockID(script []byte, arguments [][]byte, id flowgo.Identifier) (*ScriptResult, error) { + b.mu.RLock() + defer b.mu.RUnlock() + + return b.executeScriptAtBlockID(script, arguments, id) +} + +func (b *Blockchain) executeScriptAtBlockID(script []byte, arguments [][]byte, id flowgo.Identifier) (*ScriptResult, error) { + requestedBlock, err := b.storage.BlockByID(context.Background(), id) + if err != nil { + return nil, err + } + + requestedLedgerSnapshot, err := b.storage.LedgerByHeight( + context.Background(), + requestedBlock.Header.Height, + ) + if err != nil { + return nil, err + } + + blockContext := fvm.NewContextFromParent( + b.vmCtx, + fvm.WithBlockHeader(requestedBlock.Header), + ) + + scriptProc := fvm.Script(script).WithArguments(arguments...) + + _, output, err := b.vm.Run( + blockContext, + scriptProc, + requestedLedgerSnapshot) + if err != nil { + return nil, err + } + + scriptID := flowgo.MakeIDFromFingerPrint(script) + + var scriptError error = nil + var convertedValue cadence.Value = nil + + if output.Err == nil { + convertedValue = output.Value + } else { + scriptError = VMErrorToEmulator(output.Err) + } + + scriptResult := &ScriptResult{ + ScriptID: scriptID, + Value: convertedValue, + Error: scriptError, + Logs: output.Logs, + Events: output.Events, + ComputationUsed: output.ComputationUsed, + MemoryEstimate: output.MemoryEstimate, + } + + return scriptResult, nil +} + +func (b *Blockchain) ExecuteScriptAtBlockHeight( + script []byte, + arguments [][]byte, + blockHeight uint64, +) (*ScriptResult, error) { + b.mu.RLock() + defer b.mu.RUnlock() + + requestedBlock, err := b.getBlockByHeight(blockHeight) + if err != nil { + return nil, err + } + + return b.executeScriptAtBlockID(script, arguments, requestedBlock.Header.ID()) +} + +func convertToSealedResults( + results map[flowgo.Identifier]IndexedTransactionResult, + blockID flowgo.Identifier, + blockHeight uint64, +) (map[flowgo.Identifier]*StorableTransactionResult, error) { + + output := make(map[flowgo.Identifier]*StorableTransactionResult) + + for id, result := range results { + temp, err := ToStorableResult(result.ProcedureOutput, blockID, blockHeight) + if err != nil { + return nil, err + } + output[id] = &temp + } + + return output, nil +} + +func (b *Blockchain) GetTransactionsByBlockID(blockID flowgo.Identifier) ([]*flowgo.TransactionBody, error) { + b.mu.RLock() + defer b.mu.RUnlock() + + block, err := b.getBlockByID(blockID) + if err != nil { + return nil, fmt.Errorf("failed to get block %s: %w", blockID, err) + } + + var transactions []*flowgo.TransactionBody + for i, guarantee := range block.Payload.Guarantees { + c, err := b.getCollectionByID(guarantee.CollectionID) + if err != nil { + return nil, fmt.Errorf("failed to get collection [%d] %s: %w", i, guarantee.CollectionID, err) + } + + for j, txID := range c.Transactions { + tx, err := b.getTransaction(txID) + if err != nil { + return nil, fmt.Errorf("failed to get transaction [%d] %s: %w", j, txID, err) + } + transactions = append(transactions, tx) + } + } + return transactions, nil +} + +func (b *Blockchain) GetTransactionResultsByBlockID(blockID flowgo.Identifier) ([]*access.TransactionResult, error) { + b.mu.RLock() + defer b.mu.RUnlock() + + block, err := b.getBlockByID(blockID) + if err != nil { + return nil, fmt.Errorf("failed to get block %s: %w", blockID, err) + } + + var results []*access.TransactionResult + for i, guarantee := range block.Payload.Guarantees { + c, err := b.getCollectionByID(guarantee.CollectionID) + if err != nil { + return nil, fmt.Errorf("failed to get collection [%d] %s: %w", i, guarantee.CollectionID, err) + } + + for j, txID := range c.Transactions { + result, err := b.getTransactionResult(txID) + if err != nil { + return nil, fmt.Errorf("failed to get transaction result [%d] %s: %w", j, txID, err) + } + results = append(results, result) + } + } + return results, nil +} + +func (b *Blockchain) GetLogs(identifier flowgo.Identifier) ([]string, error) { + txResult, err := b.storage.TransactionResultByID(context.Background(), identifier) + if err != nil { + return nil, err + + } + return txResult.Logs, nil +} + +// SetClock sets the given clock on blockchain's pending block. +func (b *Blockchain) SetClock(clock func() time.Time) { + b.clockOverride = clock + b.pendingBlock.SetTimestamp(clock()) +} + +// NewScriptEnvironment returns an environment.Environment by +// using as a storage snapshot the blockchain's ledger state. +// Useful for tools that use the emulator's blockchain as a library. +func (b *Blockchain) NewScriptEnvironment() environment.Environment { + return environment.NewScriptEnvironmentFromStorageSnapshot( + b.vmCtx.EnvironmentParams, + b.pendingBlock.ledgerState.NewChild(), + ) +} + +func (b *Blockchain) systemChunkTransaction() (*flowgo.TransactionBody, error) { + serviceAddress := b.GetChain().ServiceAddress() + + script := templates.ReplaceAddresses( + systemChunkTransactionTemplate, + templates.Environment{ + RandomBeaconHistoryAddress: serviceAddress.Hex(), + }, + ) + + // TODO: move this to `templates.Environment` struct + script = strings.ReplaceAll( + script, + `import EVM from "EVM"`, + fmt.Sprintf( + "import EVM from %s", + serviceAddress.HexWithPrefix(), + ), + ) + + tx := flowgo.NewTransactionBody(). + SetScript([]byte(script)). + SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + AddAuthorizer(serviceAddress). + SetPayer(serviceAddress). + SetReferenceBlockID(b.pendingBlock.parentID) + + return tx, nil +} + +func (b *Blockchain) executeSystemChunkTransaction() error { + txn, err := b.systemChunkTransaction() + if err != nil { + return err + } + ctx := fvm.NewContextFromParent( + b.vmCtx, + fvm.WithLogger(zerolog.Nop()), + fvm.WithAuthorizationChecksEnabled(false), + fvm.WithSequenceNumberCheckAndIncrementEnabled(false), + fvm.WithRandomSourceHistoryCallAllowed(true), + fvm.WithBlockHeader(b.pendingBlock.Block().Header), + ) + + executionSnapshot, output, err := b.vm.Run( + ctx, + fvm.Transaction(txn, uint32(len(b.pendingBlock.Transactions()))), + b.pendingBlock.ledgerState, + ) + if err != nil { + return err + } + + if output.Err != nil { + return output.Err + } + + b.pendingBlock.events = append(b.pendingBlock.events, output.Events...) + + err = b.pendingBlock.ledgerState.Merge(executionSnapshot) + if err != nil { + return err + } + + return nil +} + +func (b *Blockchain) GetRegisterValues(registerIDs flowgo.RegisterIDs, height uint64) (values []flowgo.RegisterValue, err error) { + ledger, err := b.storage.LedgerByHeight(context.Background(), height) + if err != nil { + return nil, err + } + for _, registerID := range registerIDs { + value, err := ledger.Get(registerID) + if err != nil { + return nil, err + } + values = append(values, value) + } + return values, nil +} diff --git a/integration/internal/emulator/config.go b/integration/internal/emulator/config.go new file mode 100644 index 00000000000..d87be0a162c --- /dev/null +++ b/integration/internal/emulator/config.go @@ -0,0 +1,274 @@ +package emulator + +import ( + "fmt" + + "github.com/rs/zerolog" + + "github.com/onflow/cadence" + "github.com/onflow/crypto" + "github.com/onflow/crypto/hash" + + "github.com/onflow/flow-go/fvm" + "github.com/onflow/flow-go/fvm/meter" + flowgo "github.com/onflow/flow-go/model/flow" +) + +// config is a set of configuration options for an emulated emulator. +type config struct { + ServiceKey *ServiceKey + Store EmulatorStorage + SimpleAddresses bool + GenesisTokenSupply cadence.UFix64 + TransactionMaxGasLimit uint64 + ScriptGasLimit uint64 + TransactionExpiry uint + StorageLimitEnabled bool + TransactionFeesEnabled bool + ExecutionEffortWeights meter.ExecutionEffortWeights + ContractRemovalEnabled bool + MinimumStorageReservation cadence.UFix64 + StorageMBPerFLOW cadence.UFix64 + Logger zerolog.Logger + ServerLogger zerolog.Logger + TransactionValidationEnabled bool + ChainID flowgo.ChainID + AutoMine bool +} + +const defaultGenesisTokenSupply = "1000000000.0" +const defaultScriptGasLimit = 100000 +const defaultTransactionMaxGasLimit = flowgo.DefaultMaxTransactionGasLimit + +// defaultConfig is the default configuration for an emulated emulator. +var defaultConfig = func() config { + genesisTokenSupply, err := cadence.NewUFix64(defaultGenesisTokenSupply) + if err != nil { + panic(fmt.Sprintf("Failed to parse default genesis token supply: %s", err.Error())) + } + + return config{ + ServiceKey: DefaultServiceKey(), + Store: nil, + SimpleAddresses: false, + GenesisTokenSupply: genesisTokenSupply, + ScriptGasLimit: defaultScriptGasLimit, + TransactionMaxGasLimit: defaultTransactionMaxGasLimit, + MinimumStorageReservation: fvm.DefaultMinimumStorageReservation, + StorageMBPerFLOW: fvm.DefaultStorageMBPerFLOW, + TransactionExpiry: 0, // TODO: replace with sensible default + StorageLimitEnabled: true, + Logger: zerolog.Nop(), + ServerLogger: zerolog.Nop(), + TransactionValidationEnabled: true, + ChainID: flowgo.Emulator, + AutoMine: false, + } +}() + +func (conf config) GetStore() EmulatorStorage { + if conf.Store == nil { + conf.Store = NewMemoryStore() + } + return conf.Store +} + +func (conf config) GetChainID() flowgo.ChainID { + if conf.SimpleAddresses { + return flowgo.MonotonicEmulator + } + return conf.ChainID +} + +func (conf config) GetServiceKey() ServiceKey { + // set up service key + serviceKey := conf.ServiceKey + if serviceKey == nil { + serviceKey = DefaultServiceKey() + } + serviceKey.Address = conf.GetChainID().Chain().ServiceAddress() + serviceKey.Weight = fvm.AccountKeyWeightThreshold + return *serviceKey +} + +// Option is a function applying a change to the emulator config. +type Option func(*config) + +// WithLogger sets the fvm logger +func WithLogger( + logger zerolog.Logger, +) Option { + return func(c *config) { + c.Logger = logger + } +} + +// WithServerLogger sets the logger +func WithServerLogger( + logger zerolog.Logger, +) Option { + return func(c *config) { + c.ServerLogger = logger + } +} + +// WithServicePublicKey sets the service key from a public key. +func WithServicePublicKey( + servicePublicKey crypto.PublicKey, + sigAlgo crypto.SigningAlgorithm, + hashAlgo hash.HashingAlgorithm, +) Option { + return func(c *config) { + c.ServiceKey = &ServiceKey{ + PublicKey: servicePublicKey, + SigAlgo: sigAlgo, + HashAlgo: hashAlgo, + } + } +} + +// WithServicePrivateKey sets the service key from private key. +func WithServicePrivateKey( + privateKey crypto.PrivateKey, + sigAlgo crypto.SigningAlgorithm, + hashAlgo hash.HashingAlgorithm, +) Option { + return func(c *config) { + c.ServiceKey = &ServiceKey{ + PrivateKey: privateKey, + PublicKey: privateKey.PublicKey(), + HashAlgo: hashAlgo, + SigAlgo: sigAlgo, + } + } +} + +// WithStore sets the persistent storage provider. +func WithStore(store EmulatorStorage) Option { + return func(c *config) { + c.Store = store + } +} + +// WithSimpleAddresses enables simple addresses, which are sequential starting with 0x01. +func WithSimpleAddresses() Option { + return func(c *config) { + c.SimpleAddresses = true + } +} + +// WithGenesisTokenSupply sets the genesis token supply. +func WithGenesisTokenSupply(supply cadence.UFix64) Option { + return func(c *config) { + c.GenesisTokenSupply = supply + } +} + +// WithTransactionMaxGasLimit sets the maximum gas limit for transactions. +// +// Individual transactions will still be bounded by the limit they declare. +// This function sets the maximum limit that any transaction can declare. +// +// This limit does not affect script executions. Use WithScriptGasLimit +// to set the gas limit for script executions. +func WithTransactionMaxGasLimit(maxLimit uint64) Option { + return func(c *config) { + c.TransactionMaxGasLimit = maxLimit + } +} + +// WithScriptGasLimit sets the gas limit for scripts. +// +// This limit does not affect transactions, which declare their own limit. +// Use WithTransactionMaxGasLimit to set the maximum gas limit for transactions. +func WithScriptGasLimit(limit uint64) Option { + return func(c *config) { + c.ScriptGasLimit = limit + } +} + +// WithTransactionExpiry sets the transaction expiry measured in blocks. +// +// If set to zero, transaction expiry is disabled and the reference block ID field +// is not required. +func WithTransactionExpiry(expiry uint) Option { + return func(c *config) { + c.TransactionExpiry = expiry + } +} + +// WithStorageLimitEnabled enables/disables limiting account storage used to their storage capacity. +// +// If set to false, accounts can store any amount of data, +// otherwise they can only store as much as their storage capacity. +// The default is true. +func WithStorageLimitEnabled(enabled bool) Option { + return func(c *config) { + c.StorageLimitEnabled = enabled + } +} + +// WithMinimumStorageReservation sets the minimum account balance. +// +// The cost of creating new accounts is also set to this value. +// The default is taken from fvm.DefaultMinimumStorageReservation +func WithMinimumStorageReservation(minimumStorageReservation cadence.UFix64) Option { + return func(c *config) { + c.MinimumStorageReservation = minimumStorageReservation + } +} + +// WithStorageMBPerFLOW sets the cost of a megabyte of storage in FLOW +// +// the default is taken from fvm.DefaultStorageMBPerFLOW +func WithStorageMBPerFLOW(storageMBPerFLOW cadence.UFix64) Option { + return func(c *config) { + c.StorageMBPerFLOW = storageMBPerFLOW + } +} + +// WithTransactionFeesEnabled enables/disables transaction fees. +// +// If set to false transactions don't cost any flow. +// The default is false. +func WithTransactionFeesEnabled(enabled bool) Option { + return func(c *config) { + c.TransactionFeesEnabled = enabled + } +} + +// WithExecutionEffortWeights sets the execution effort weights. +// default is the Mainnet values. +func WithExecutionEffortWeights(weights meter.ExecutionEffortWeights) Option { + return func(c *config) { + c.ExecutionEffortWeights = weights + } +} + +// WithContractRemovalEnabled restricts/allows removal of already deployed contracts. +// +// The default is provided by on-chain value. +func WithContractRemovalEnabled(enabled bool) Option { + return func(c *config) { + c.ContractRemovalEnabled = enabled + } +} + +// WithTransactionValidationEnabled enables/disables transaction validation. +// +// If set to false, the emulator will not verify transaction signatures or validate sequence numbers. +// +// The default is true. +func WithTransactionValidationEnabled(enabled bool) Option { + return func(c *config) { + c.TransactionValidationEnabled = enabled + } +} + +// WithChainID sets chain type for address generation +// The default is emulator. +func WithChainID(chainID flowgo.ChainID) Option { + return func(c *config) { + c.ChainID = chainID + } +} diff --git a/integration/internal/emulator/convert.go b/integration/internal/emulator/convert.go new file mode 100644 index 00000000000..f9f8f1dfb6a --- /dev/null +++ b/integration/internal/emulator/convert.go @@ -0,0 +1,377 @@ +/* + * Flow Emulator + * + * Copyright Flow Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package emulator + +import ( + "fmt" + + "github.com/onflow/cadence" + "github.com/onflow/cadence/encoding/ccf" + + sdk "github.com/onflow/flow-go-sdk" + + "github.com/onflow/flow-go/access" + "github.com/onflow/flow-go/fvm" + fvmerrors "github.com/onflow/flow-go/fvm/errors" + flowgo "github.com/onflow/flow-go/model/flow" +) + +func SDKIdentifierToFlow(sdkIdentifier sdk.Identifier) flowgo.Identifier { + return flowgo.Identifier(sdkIdentifier) +} + +func SDKIdentifiersToFlow(sdkIdentifiers []sdk.Identifier) []flowgo.Identifier { + ret := make([]flowgo.Identifier, len(sdkIdentifiers)) + for i, sdkIdentifier := range sdkIdentifiers { + ret[i] = SDKIdentifierToFlow(sdkIdentifier) + } + return ret +} + +func FlowIdentifierToSDK(flowIdentifier flowgo.Identifier) sdk.Identifier { + return sdk.Identifier(flowIdentifier) +} + +func FlowIdentifiersToSDK(flowIdentifiers []flowgo.Identifier) []sdk.Identifier { + ret := make([]sdk.Identifier, len(flowIdentifiers)) + for i, flowIdentifier := range flowIdentifiers { + ret[i] = FlowIdentifierToSDK(flowIdentifier) + } + return ret +} + +func SDKProposalKeyToFlow(sdkProposalKey sdk.ProposalKey) flowgo.ProposalKey { + return flowgo.ProposalKey{ + Address: SDKAddressToFlow(sdkProposalKey.Address), + KeyIndex: sdkProposalKey.KeyIndex, + SequenceNumber: sdkProposalKey.SequenceNumber, + } +} + +func FlowProposalKeyToSDK(flowProposalKey flowgo.ProposalKey) sdk.ProposalKey { + return sdk.ProposalKey{ + Address: FlowAddressToSDK(flowProposalKey.Address), + KeyIndex: flowProposalKey.KeyIndex, + SequenceNumber: flowProposalKey.SequenceNumber, + } +} + +func SDKAddressToFlow(sdkAddress sdk.Address) flowgo.Address { + return flowgo.Address(sdkAddress) +} + +func FlowAddressToSDK(flowAddress flowgo.Address) sdk.Address { + return sdk.Address(flowAddress) +} + +func SDKAddressesToFlow(sdkAddresses []sdk.Address) []flowgo.Address { + ret := make([]flowgo.Address, len(sdkAddresses)) + for i, sdkAddress := range sdkAddresses { + ret[i] = SDKAddressToFlow(sdkAddress) + } + return ret +} + +func FlowAddressesToSDK(flowAddresses []flowgo.Address) []sdk.Address { + ret := make([]sdk.Address, len(flowAddresses)) + for i, flowAddress := range flowAddresses { + ret[i] = FlowAddressToSDK(flowAddress) + } + return ret +} + +func SDKTransactionSignatureToFlow(sdkTransactionSignature sdk.TransactionSignature) flowgo.TransactionSignature { + return flowgo.TransactionSignature{ + Address: SDKAddressToFlow(sdkTransactionSignature.Address), + SignerIndex: sdkTransactionSignature.SignerIndex, + KeyIndex: sdkTransactionSignature.KeyIndex, + Signature: sdkTransactionSignature.Signature, + } +} + +func FlowTransactionSignatureToSDK(flowTransactionSignature flowgo.TransactionSignature) sdk.TransactionSignature { + return sdk.TransactionSignature{ + Address: FlowAddressToSDK(flowTransactionSignature.Address), + SignerIndex: flowTransactionSignature.SignerIndex, + KeyIndex: flowTransactionSignature.KeyIndex, + Signature: flowTransactionSignature.Signature, + } +} + +func SDKTransactionSignaturesToFlow(sdkTransactionSignatures []sdk.TransactionSignature) []flowgo.TransactionSignature { + ret := make([]flowgo.TransactionSignature, len(sdkTransactionSignatures)) + for i, sdkTransactionSignature := range sdkTransactionSignatures { + ret[i] = SDKTransactionSignatureToFlow(sdkTransactionSignature) + } + return ret +} + +func FlowTransactionSignaturesToSDK(flowTransactionSignatures []flowgo.TransactionSignature) []sdk.TransactionSignature { + ret := make([]sdk.TransactionSignature, len(flowTransactionSignatures)) + for i, flowTransactionSignature := range flowTransactionSignatures { + ret[i] = FlowTransactionSignatureToSDK(flowTransactionSignature) + } + return ret +} + +func SDKTransactionToFlow(sdkTx sdk.Transaction) *flowgo.TransactionBody { + return &flowgo.TransactionBody{ + ReferenceBlockID: SDKIdentifierToFlow(sdkTx.ReferenceBlockID), + Script: sdkTx.Script, + Arguments: sdkTx.Arguments, + GasLimit: sdkTx.GasLimit, + ProposalKey: SDKProposalKeyToFlow(sdkTx.ProposalKey), + Payer: SDKAddressToFlow(sdkTx.Payer), + Authorizers: SDKAddressesToFlow(sdkTx.Authorizers), + PayloadSignatures: SDKTransactionSignaturesToFlow(sdkTx.PayloadSignatures), + EnvelopeSignatures: SDKTransactionSignaturesToFlow(sdkTx.EnvelopeSignatures), + } +} + +func FlowTransactionToSDK(flowTx flowgo.TransactionBody) sdk.Transaction { + transaction := sdk.Transaction{ + ReferenceBlockID: FlowIdentifierToSDK(flowTx.ReferenceBlockID), + Script: flowTx.Script, + Arguments: flowTx.Arguments, + GasLimit: flowTx.GasLimit, + ProposalKey: FlowProposalKeyToSDK(flowTx.ProposalKey), + Payer: FlowAddressToSDK(flowTx.Payer), + Authorizers: FlowAddressesToSDK(flowTx.Authorizers), + PayloadSignatures: FlowTransactionSignaturesToSDK(flowTx.PayloadSignatures), + EnvelopeSignatures: FlowTransactionSignaturesToSDK(flowTx.EnvelopeSignatures), + } + return transaction +} + +func FlowTransactionResultToSDK(result *access.TransactionResult) (*sdk.TransactionResult, error) { + + events, err := FlowEventsToSDK(result.Events) + if err != nil { + return nil, err + } + + if result.ErrorMessage != "" { + err = &ExecutionError{Code: int(result.StatusCode), Message: result.ErrorMessage} + } + + sdkResult := &sdk.TransactionResult{ + Status: sdk.TransactionStatus(result.Status), + Error: err, + Events: events, + TransactionID: sdk.Identifier(result.TransactionID), + BlockHeight: result.BlockHeight, + BlockID: sdk.Identifier(result.BlockID), + } + + return sdkResult, nil +} + +func SDKEventToFlow(event sdk.Event) (flowgo.Event, error) { + payload, err := ccf.EventsEncMode.Encode(event.Value) + if err != nil { + return flowgo.Event{}, err + } + + return flowgo.Event{ + Type: flowgo.EventType(event.Type), + TransactionID: SDKIdentifierToFlow(event.TransactionID), + TransactionIndex: uint32(event.TransactionIndex), + EventIndex: uint32(event.EventIndex), + Payload: payload, + }, nil +} + +func FlowEventToSDK(flowEvent flowgo.Event) (sdk.Event, error) { + cadenceValue, err := ccf.EventsDecMode.Decode(nil, flowEvent.Payload) + if err != nil { + return sdk.Event{}, err + } + + cadenceEvent, ok := cadenceValue.(cadence.Event) + if !ok { + return sdk.Event{}, fmt.Errorf("cadence value not of type event: %s", cadenceValue) + } + + return sdk.Event{ + Type: string(flowEvent.Type), + TransactionID: FlowIdentifierToSDK(flowEvent.TransactionID), + TransactionIndex: int(flowEvent.TransactionIndex), + EventIndex: int(flowEvent.EventIndex), + Value: cadenceEvent, + }, nil +} + +func FlowEventsToSDK(flowEvents []flowgo.Event) ([]sdk.Event, error) { + ret := make([]sdk.Event, len(flowEvents)) + var err error + for i, flowEvent := range flowEvents { + ret[i], err = FlowEventToSDK(flowEvent) + if err != nil { + return nil, err + } + } + return ret, nil +} + +func FlowAccountPublicKeyToSDK(flowPublicKey flowgo.AccountPublicKey, index uint32) (sdk.AccountKey, error) { + + return sdk.AccountKey{ + Index: index, + PublicKey: flowPublicKey.PublicKey, + SigAlgo: flowPublicKey.SignAlgo, + HashAlgo: flowPublicKey.HashAlgo, + Weight: flowPublicKey.Weight, + SequenceNumber: flowPublicKey.SeqNumber, + Revoked: flowPublicKey.Revoked, + }, nil +} + +func SDKAccountKeyToFlow(key *sdk.AccountKey) (flowgo.AccountPublicKey, error) { + + return flowgo.AccountPublicKey{ + Index: key.Index, + PublicKey: key.PublicKey, + SignAlgo: key.SigAlgo, + HashAlgo: key.HashAlgo, + Weight: key.Weight, + SeqNumber: key.SequenceNumber, + Revoked: key.Revoked, + }, nil +} + +func SDKAccountKeysToFlow(keys []*sdk.AccountKey) ([]flowgo.AccountPublicKey, error) { + accountKeys := make([]flowgo.AccountPublicKey, len(keys)) + + for i, key := range keys { + accountKey, err := SDKAccountKeyToFlow(key) + if err != nil { + return nil, err + } + + accountKeys[i] = accountKey + } + + return accountKeys, nil +} + +func FlowAccountPublicKeysToSDK(flowPublicKeys []flowgo.AccountPublicKey) ([]*sdk.AccountKey, error) { + ret := make([]*sdk.AccountKey, len(flowPublicKeys)) + for i, flowPublicKey := range flowPublicKeys { + v, err := FlowAccountPublicKeyToSDK(flowPublicKey, uint32(i)) + if err != nil { + return nil, err + } + + ret[i] = &v + } + return ret, nil +} + +func FlowAccountToSDK(flowAccount flowgo.Account) (*sdk.Account, error) { + sdkPublicKeys, err := FlowAccountPublicKeysToSDK(flowAccount.Keys) + if err != nil { + return &sdk.Account{}, err + } + + return &sdk.Account{ + Address: FlowAddressToSDK(flowAccount.Address), + Balance: flowAccount.Balance, + Code: nil, + Keys: sdkPublicKeys, + Contracts: flowAccount.Contracts, + }, nil +} + +func SDKAccountToFlow(account *sdk.Account) (*flowgo.Account, error) { + keys, err := SDKAccountKeysToFlow(account.Keys) + if err != nil { + return nil, err + } + + return &flowgo.Account{ + Address: SDKAddressToFlow(account.Address), + Balance: account.Balance, + Keys: keys, + Contracts: account.Contracts, + }, nil +} + +func FlowLightCollectionToSDK(flowCollection flowgo.LightCollection) sdk.Collection { + return sdk.Collection{ + TransactionIDs: FlowIdentifiersToSDK(flowCollection.Transactions), + } +} + +func VMTransactionResultToEmulator( + txnId flowgo.Identifier, + output fvm.ProcedureOutput, +) ( + *TransactionResult, + error, +) { + txID := FlowIdentifierToSDK(txnId) + + sdkEvents, err := FlowEventsToSDK(output.Events) + if err != nil { + return nil, err + } + + return &TransactionResult{ + TransactionID: txID, + ComputationUsed: output.ComputationUsed, + MemoryEstimate: output.MemoryEstimate, + Error: VMErrorToEmulator(output.Err), + Logs: output.Logs, + Events: sdkEvents, + }, nil +} + +func VMErrorToEmulator(vmError fvmerrors.CodedError) error { + if vmError == nil { + return nil + } + + return &FVMError{FlowError: vmError} +} + +func ToStorableResult( + output fvm.ProcedureOutput, + blockID flowgo.Identifier, + blockHeight uint64, +) ( + StorableTransactionResult, + error, +) { + var errorCode int + var errorMessage string + + if output.Err != nil { + errorCode = int(output.Err.Code()) + errorMessage = output.Err.Error() + } + + return StorableTransactionResult{ + BlockID: blockID, + BlockHeight: blockHeight, + ErrorCode: errorCode, + ErrorMessage: errorMessage, + Logs: output.Logs, + Events: output.Events, + }, nil +} diff --git a/integration/internal/emulator/doc.go b/integration/internal/emulator/doc.go new file mode 100644 index 00000000000..416e7d84b3c --- /dev/null +++ b/integration/internal/emulator/doc.go @@ -0,0 +1,11 @@ +// Package emulator is a minimal version of the Flow Emulator (https://github.com/onflow/flow-emulator) +// for use within some integration tests for flow-go. +// Using an Emulator is desirable for test cases where: +// - we don't want to, or can't, run the test case against a local Docker network (package integration/testnet) +// - we want the test to include execution of smart contract code in a realistic environment +// +// Before using this package, flow-go's integration tests used the Flow Emulator directly. +// This created a repository-wise circular dependency and complicated version upgrades (see https://github.com/onflow/flow-go/issues/2863). +// The main purpose for this package is to replace that dependency with minimal ongoing +// maintenance overhead. +package emulator diff --git a/integration/internal/emulator/emulator.go b/integration/internal/emulator/emulator.go new file mode 100644 index 00000000000..91d59be517b --- /dev/null +++ b/integration/internal/emulator/emulator.go @@ -0,0 +1,173 @@ +/* + * Flow Emulator + * + * Copyright Flow Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package emulator + +import ( + "fmt" + + sdkcrypto "github.com/onflow/flow-go-sdk/crypto" + + "github.com/onflow/crypto" + "github.com/onflow/crypto/hash" + + "github.com/onflow/flow-go/access" + flowgo "github.com/onflow/flow-go/model/flow" +) + +// SignatureAlgorithm is an identifier for a signature algorithm (and parameters if applicable). +type SignatureAlgorithm = crypto.SigningAlgorithm + +const ( + UnknownSignatureAlgorithm SignatureAlgorithm = crypto.UnknownSigningAlgorithm + // ECDSA_P256 is ECDSA on NIST P-256 curve + ECDSA_P256 = crypto.ECDSAP256 + // ECDSA_secp256k1 is ECDSA on secp256k1 curve + ECDSA_secp256k1 = crypto.ECDSASecp256k1 + // BLS_BLS12_381 is BLS on BLS12-381 curve + BLS_BLS12_381 = crypto.BLSBLS12381 +) + +// StringToSignatureAlgorithm converts a string to a SignatureAlgorithm. +func StringToSignatureAlgorithm(s string) SignatureAlgorithm { + switch s { + case ECDSA_P256.String(): + return ECDSA_P256 + case ECDSA_secp256k1.String(): + return ECDSA_secp256k1 + case BLS_BLS12_381.String(): + return BLS_BLS12_381 + default: + return UnknownSignatureAlgorithm + } +} + +type ServiceKey struct { + Index uint32 + Address flowgo.Address + SequenceNumber uint64 + PrivateKey crypto.PrivateKey + PublicKey crypto.PublicKey + HashAlgo hash.HashingAlgorithm + SigAlgo SignatureAlgorithm + Weight int +} + +const defaultServiceKeyPrivateKeySeed = "elephant ears space cowboy octopus rodeo potato cannon pineapple" +const DefaultServiceKeySigAlgo = sdkcrypto.ECDSA_P256 +const DefaultServiceKeyHashAlgo = sdkcrypto.SHA3_256 + +func DefaultServiceKey() *ServiceKey { + return GenerateDefaultServiceKey(DefaultServiceKeySigAlgo, DefaultServiceKeyHashAlgo) +} + +func GenerateDefaultServiceKey( + sigAlgo crypto.SigningAlgorithm, + hashAlgo hash.HashingAlgorithm, +) *ServiceKey { + privateKey, err := crypto.GeneratePrivateKey( + sigAlgo, + []byte(defaultServiceKeyPrivateKeySeed), + ) + if err != nil { + panic(fmt.Sprintf("Failed to generate default service key: %s", err.Error())) + } + + return &ServiceKey{ + PrivateKey: privateKey, + PublicKey: privateKey.PublicKey(), + SigAlgo: sigAlgo, + HashAlgo: hashAlgo, + } +} + +func (s ServiceKey) Signer() (sdkcrypto.Signer, error) { + return sdkcrypto.NewInMemorySigner(s.PrivateKey, s.HashAlgo) +} + +func (s ServiceKey) AccountKey() (crypto.PublicKey, crypto.PrivateKey) { + + var publicKey crypto.PublicKey + if s.PublicKey != nil { + publicKey = s.PublicKey + } + + if s.PrivateKey != nil { + publicKey = s.PrivateKey.PublicKey() + } + + return publicKey, s.PrivateKey + +} + +type AccessProvider interface { + Ping() error + GetNetworkParameters() access.NetworkParameters + + GetLatestBlock() (*flowgo.Block, error) + GetBlockByID(id flowgo.Identifier) (*flowgo.Block, error) + GetBlockByHeight(height uint64) (*flowgo.Block, error) + + GetCollectionByID(colID flowgo.Identifier) (*flowgo.LightCollection, error) + GetFullCollectionByID(colID flowgo.Identifier) (*flowgo.Collection, error) + + GetTransaction(txID flowgo.Identifier) (*flowgo.TransactionBody, error) + GetTransactionResult(txID flowgo.Identifier) (*access.TransactionResult, error) + GetTransactionsByBlockID(blockID flowgo.Identifier) ([]*flowgo.TransactionBody, error) + GetTransactionResultsByBlockID(blockID flowgo.Identifier) ([]*access.TransactionResult, error) + + GetAccount(address flowgo.Address) (*flowgo.Account, error) + GetAccountAtBlockHeight(address flowgo.Address, blockHeight uint64) (*flowgo.Account, error) + GetAccountByIndex(uint) (*flowgo.Account, error) + + GetEventsByHeight(blockHeight uint64, eventType string) ([]flowgo.Event, error) + GetEventsForBlockIDs(eventType string, blockIDs []flowgo.Identifier) ([]flowgo.BlockEvents, error) + GetEventsForHeightRange(eventType string, startHeight, endHeight uint64) ([]flowgo.BlockEvents, error) + + ExecuteScript(script []byte, arguments [][]byte) (*ScriptResult, error) + ExecuteScriptAtBlockHeight(script []byte, arguments [][]byte, blockHeight uint64) (*ScriptResult, error) + ExecuteScriptAtBlockID(script []byte, arguments [][]byte, id flowgo.Identifier) (*ScriptResult, error) + + SendTransaction(tx *flowgo.TransactionBody) error + AddTransaction(tx flowgo.TransactionBody) error +} + +type AutoMineCapable interface { + EnableAutoMine() + DisableAutoMine() +} + +type ExecutionCapable interface { + ExecuteAndCommitBlock() (*flowgo.Block, []*TransactionResult, error) + ExecuteNextTransaction() (*TransactionResult, error) + ExecuteBlock() ([]*TransactionResult, error) + CommitBlock() (*flowgo.Block, error) +} + +type Contract struct { + Name string + Source string +} + +// Emulator defines the method set of an emulated emulator. +type Emulator interface { + ServiceKey() ServiceKey + AccessProvider + AutoMineCapable + ExecutionCapable +} diff --git a/integration/internal/emulator/errors.go b/integration/internal/emulator/errors.go new file mode 100644 index 00000000000..e23b6822ce6 --- /dev/null +++ b/integration/internal/emulator/errors.go @@ -0,0 +1,274 @@ +/* + * Flow Emulator + * + * Copyright Flow Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package emulator + +import ( + "errors" + "fmt" + + "github.com/onflow/flow-go-sdk/crypto" + + "github.com/onflow/flow-go/access" + fvmerrors "github.com/onflow/flow-go/fvm/errors" + flowgo "github.com/onflow/flow-go/model/flow" +) + +var ErrNotFound = errors.New("could not find entity") + +type InvalidArgumentError struct { + msg string +} + +func (e InvalidArgumentError) Error() string { + return fmt.Sprintf("Invalid argument error: %s", e.msg) +} + +func NewInvalidArgumentError(msg string) *InvalidArgumentError { + return &InvalidArgumentError{msg: msg} +} + +type InternalError struct { + msg string +} + +func (e InternalError) Error() string { + return fmt.Sprintf("Internal error: %s", e.msg) +} + +func NewInternalError(msg string) *InternalError { + return &InternalError{msg: msg} +} + +// A NotFoundError indicates that an entity could not be found. +type NotFoundError interface { + isNotFoundError() +} + +// A BlockNotFoundError indicates that a block could not be found. +type BlockNotFoundError interface { + isBlockNotFoundError() +} + +// A BlockNotFoundByHeightError indicates that a block could not be found at the specified height. +type BlockNotFoundByHeightError struct { + Height uint64 +} + +func (e *BlockNotFoundByHeightError) isNotFoundError() {} +func (e *BlockNotFoundByHeightError) isBlockNotFoundError() {} + +func (e *BlockNotFoundByHeightError) Error() string { + return fmt.Sprintf("could not find block at height %d", e.Height) +} + +// A BlockNotFoundByIDError indicates that a block with the specified ID could not be found. +type BlockNotFoundByIDError struct { + ID flowgo.Identifier +} + +func (e *BlockNotFoundByIDError) isNotFoundError() {} +func (e *BlockNotFoundByIDError) isBlockNotFoundError() {} + +func (e *BlockNotFoundByIDError) Error() string { + return fmt.Sprintf("could not find block with ID %s", e.ID) +} + +// A CollectionNotFoundError indicates that a collection could not be found. +type CollectionNotFoundError struct { + ID flowgo.Identifier +} + +func (e *CollectionNotFoundError) isNotFoundError() {} + +func (e *CollectionNotFoundError) Error() string { + return fmt.Sprintf("could not find collection with ID %s", e.ID) +} + +// A TransactionNotFoundError indicates that a transaction could not be found. +type TransactionNotFoundError struct { + ID flowgo.Identifier +} + +func (e *TransactionNotFoundError) isNotFoundError() {} + +func (e *TransactionNotFoundError) Error() string { + return fmt.Sprintf("could not find transaction with ID %s", e.ID) +} + +// An AccountNotFoundError indicates that an account could not be found. +type AccountNotFoundError struct { + Address flowgo.Address +} + +func (e *AccountNotFoundError) isNotFoundError() {} + +func (e *AccountNotFoundError) Error() string { + return fmt.Sprintf("could not find account with address %s", e.Address) +} + +// A TransactionValidationError indicates that a submitted transaction is invalid. +type TransactionValidationError interface { + isTransactionValidationError() +} + +// A DuplicateTransactionError indicates that a transaction has already been submitted. +type DuplicateTransactionError struct { + TxID flowgo.Identifier +} + +func (e *DuplicateTransactionError) isTransactionValidationError() {} + +func (e *DuplicateTransactionError) Error() string { + return fmt.Sprintf("transaction with ID %s has already been submitted", e.TxID) +} + +// IncompleteTransactionError indicates that a transaction is missing one or more required fields. +type IncompleteTransactionError struct { + MissingFields []string +} + +func (e *IncompleteTransactionError) isTransactionValidationError() {} + +func (e *IncompleteTransactionError) Error() string { + return fmt.Sprintf("transaction is missing required fields: %s", e.MissingFields) +} + +// ExpiredTransactionError indicates that a transaction has expired. +type ExpiredTransactionError struct { + RefHeight, FinalHeight uint64 +} + +func (e *ExpiredTransactionError) isTransactionValidationError() {} + +func (e *ExpiredTransactionError) Error() string { + return fmt.Sprintf("transaction is expired: ref_height=%d final_height=%d", e.RefHeight, e.FinalHeight) +} + +// InvalidTransactionScriptError indicates that a transaction contains an invalid Cadence script. +type InvalidTransactionScriptError struct { + ParserErr error +} + +func (e *InvalidTransactionScriptError) isTransactionValidationError() {} + +func (e *InvalidTransactionScriptError) Error() string { + return fmt.Sprintf("failed to parse transaction Cadence script: %s", e.ParserErr) +} + +func (e *InvalidTransactionScriptError) Unwrap() error { + return e.ParserErr +} + +// InvalidTransactionGasLimitError indicates that a transaction specifies a gas limit that exceeds the maximum. +type InvalidTransactionGasLimitError struct { + Maximum uint64 + Actual uint64 +} + +func (e *InvalidTransactionGasLimitError) isTransactionValidationError() {} + +func (e *InvalidTransactionGasLimitError) Error() string { + return fmt.Sprintf("transaction gas limit (%d) exceeds the maximum gas limit (%d)", e.Actual, e.Maximum) +} + +// An InvalidStateVersionError indicates that a state version hash provided is invalid. +type InvalidStateVersionError struct { + Version crypto.Hash +} + +func (e *InvalidStateVersionError) Error() string { + return fmt.Sprintf("execution state with version hash %x is invalid", e.Version) +} + +// A PendingBlockCommitBeforeExecutionError indicates that the current pending block has not been executed (cannot commit). +type PendingBlockCommitBeforeExecutionError struct { + BlockID flowgo.Identifier +} + +func (e *PendingBlockCommitBeforeExecutionError) Error() string { + return fmt.Sprintf("pending block with ID %s cannot be committed before execution", e.BlockID) +} + +// A PendingBlockMidExecutionError indicates that the current pending block is mid-execution. +type PendingBlockMidExecutionError struct { + BlockID flowgo.Identifier +} + +func (e *PendingBlockMidExecutionError) Error() string { + return fmt.Sprintf("pending block with ID %s is currently being executed", e.BlockID) +} + +// A PendingBlockTransactionsExhaustedError indicates that the current pending block has finished executing (no more transactions to execute). +type PendingBlockTransactionsExhaustedError struct { + BlockID flowgo.Identifier +} + +func (e *PendingBlockTransactionsExhaustedError) Error() string { + return fmt.Sprintf("pending block with ID %s contains no more transactions to execute", e.BlockID) +} + +// A StorageError indicates that an error occurred in the storage provider. +type StorageError struct { + inner error +} + +func (e *StorageError) Error() string { + return fmt.Sprintf("storage failure: %v", e.inner) +} + +func (e *StorageError) Unwrap() error { + return e.inner +} + +// An ExecutionError occurs when a transaction fails to execute. +type ExecutionError struct { + Code int + Message string +} + +func (e *ExecutionError) Error() string { + return fmt.Sprintf("execution error code %d: %s", e.Code, e.Message) +} + +type FVMError struct { + FlowError fvmerrors.CodedError +} + +func (f *FVMError) Error() string { + return f.FlowError.Error() +} + +func (f *FVMError) Unwrap() error { + return f.FlowError +} + +func ConvertAccessError(err error) error { + switch typedErr := err.(type) { + case access.IncompleteTransactionError: + return &IncompleteTransactionError{MissingFields: typedErr.MissingFields} + case access.ExpiredTransactionError: + return &ExpiredTransactionError{RefHeight: typedErr.RefHeight, FinalHeight: typedErr.FinalHeight} + case access.InvalidGasLimitError: + return &InvalidTransactionGasLimitError{Maximum: typedErr.Maximum, Actual: typedErr.Actual} + case access.InvalidScriptError: + return &InvalidTransactionScriptError{ParserErr: typedErr.ParserErr} + } + + return err +} diff --git a/integration/internal/emulator/ledger.go b/integration/internal/emulator/ledger.go new file mode 100644 index 00000000000..f1bcdac7d79 --- /dev/null +++ b/integration/internal/emulator/ledger.go @@ -0,0 +1,143 @@ +package emulator + +import ( + "context" + "errors" + "fmt" + "math" + + "github.com/onflow/cadence" + + "github.com/onflow/flow-go/fvm" + "github.com/onflow/flow-go/fvm/environment" + "github.com/onflow/flow-go/fvm/meter" + "github.com/onflow/flow-go/fvm/storage/snapshot" + flowgo "github.com/onflow/flow-go/model/flow" +) + +func configureLedger( + conf config, + store EmulatorStorage, + vm *fvm.VirtualMachine, + ctx fvm.Context, +) ( + *flowgo.Block, + snapshot.StorageSnapshot, + error, +) { + + latestBlock, err := store.LatestBlock(context.Background()) + if err != nil && !errors.Is(err, ErrNotFound) { + return nil, nil, err + } + + if errors.Is(err, ErrNotFound) { + // bootstrap the ledger with the genesis block + ledger, err := store.LedgerByHeight(context.Background(), 0) + if err != nil { + return nil, nil, err + } + + genesisExecutionSnapshot, err := bootstrapLedger(vm, ctx, ledger, conf) + if err != nil { + return nil, nil, fmt.Errorf("failed to bootstrap execution state: %w", err) + } + + // commit the genesis block to storage + genesis := flowgo.Genesis(conf.GetChainID()) + latestBlock = *genesis + + err = store.CommitBlock( + context.Background(), + *genesis, + nil, + nil, + nil, + genesisExecutionSnapshot, + nil, + ) + if err != nil { + return nil, nil, err + } + } + + latestLedger, err := store.LedgerByHeight( + context.Background(), + latestBlock.Header.Height, + ) + + if err != nil { + return nil, nil, err + } + + return &latestBlock, latestLedger, nil +} + +func bootstrapLedger( + vm *fvm.VirtualMachine, + ctx fvm.Context, + ledger snapshot.StorageSnapshot, + conf config, +) ( + *snapshot.ExecutionSnapshot, + error, +) { + serviceKey := conf.GetServiceKey() + + ctx = fvm.NewContextFromParent( + ctx, + fvm.WithAccountStorageLimit(false), + ) + + flowAccountKey := flowgo.AccountPublicKey{ + PublicKey: serviceKey.PublicKey, + SignAlgo: serviceKey.SigAlgo, + HashAlgo: serviceKey.HashAlgo, + Weight: fvm.AccountKeyWeightThreshold, + } + + bootstrap := configureBootstrapProcedure(conf, flowAccountKey, conf.GenesisTokenSupply) + + executionSnapshot, output, err := vm.Run(ctx, bootstrap, ledger) + if err != nil { + return nil, err + } + + if output.Err != nil { + return nil, output.Err + } + + return executionSnapshot, nil +} + +func configureBootstrapProcedure(conf config, flowAccountKey flowgo.AccountPublicKey, supply cadence.UFix64) *fvm.BootstrapProcedure { + options := make([]fvm.BootstrapProcedureOption, 0) + options = append(options, + fvm.WithInitialTokenSupply(supply), + fvm.WithRestrictedAccountCreationEnabled(false), + // This enables variable transaction fees AND execution effort metering + // as described in Variable Transaction Fees: + // Execution Effort FLIP: https://github.com/onflow/flow/pull/753 + fvm.WithTransactionFee(fvm.DefaultTransactionFees), + fvm.WithExecutionMemoryLimit(math.MaxUint32), + fvm.WithExecutionMemoryWeights(meter.DefaultMemoryWeights), + fvm.WithExecutionEffortWeights(environment.MainnetExecutionEffortWeights), + ) + + if conf.ExecutionEffortWeights != nil { + options = append(options, + fvm.WithExecutionEffortWeights(conf.ExecutionEffortWeights), + ) + } + if conf.StorageLimitEnabled { + options = append(options, + fvm.WithAccountCreationFee(conf.MinimumStorageReservation), + fvm.WithMinimumStorageReservation(conf.MinimumStorageReservation), + fvm.WithStorageMBPerFLOW(conf.StorageMBPerFLOW), + ) + } + return fvm.Bootstrap( + flowAccountKey, + options..., + ) +} diff --git a/integration/internal/emulator/memstore.go b/integration/internal/emulator/memstore.go new file mode 100644 index 00000000000..191cd21f4d8 --- /dev/null +++ b/integration/internal/emulator/memstore.go @@ -0,0 +1,395 @@ +/* + * Flow Emulator + * + * Copyright Flow Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package emulator + +import ( + "context" + "errors" + "fmt" + "sync" + + "github.com/onflow/flow-go/access" + "github.com/onflow/flow-go/fvm/environment" + "github.com/onflow/flow-go/fvm/storage/snapshot" + flowgo "github.com/onflow/flow-go/model/flow" +) + +// Store implements the Store interface with an in-memory store. +type Store struct { + mu sync.RWMutex + // block ID to block height + blockIDToHeight map[flowgo.Identifier]uint64 + // blocks by height + blocks map[uint64]flowgo.Block + // collections by ID + collections map[flowgo.Identifier]flowgo.LightCollection + // transactions by ID + transactions map[flowgo.Identifier]flowgo.TransactionBody + // Transaction results by ID + transactionResults map[flowgo.Identifier]StorableTransactionResult + // Ledger states by block height + ledger map[uint64]snapshot.SnapshotTree + // events by block height + eventsByBlockHeight map[uint64][]flowgo.Event + // highest block height + blockHeight uint64 +} + +var _ environment.Blocks = &Store{} +var _ access.Blocks = &Store{} +var _ EmulatorStorage = &Store{} + +func (b *Store) HeaderByID(id flowgo.Identifier) (*flowgo.Header, error) { + block, err := b.BlockByID(context.Background(), id) + if err != nil { + if errors.Is(err, ErrNotFound) { + return nil, nil + } + return nil, err + } + return block.Header, nil +} + +func (b *Store) FinalizedHeader() (*flowgo.Header, error) { + block, err := b.LatestBlock(context.Background()) + if err != nil { + return nil, err + } + + return block.Header, nil +} + +func (b *Store) SealedHeader() (*flowgo.Header, error) { + block, err := b.LatestBlock(context.Background()) + if err != nil { + return nil, err + } + + return block.Header, nil +} + +func (b *Store) IndexedHeight() (uint64, error) { + block, err := b.LatestBlock(context.Background()) + if err != nil { + return 0, err + } + + return block.Header.Height, nil +} + +// ByHeightFrom We don't have to do anything complex here, as emulator does not fork the chain +func (b *Store) ByHeightFrom(height uint64, header *flowgo.Header) (*flowgo.Header, error) { + if height > header.Height { + return nil, ErrNotFound + } + block, err := b.BlockByHeight(context.Background(), height) + if err != nil { + return nil, err + } + + return block.Header, nil +} + +// New returns a new in-memory Store implementation. +func NewMemoryStore() *Store { + return &Store{ + mu: sync.RWMutex{}, + blockIDToHeight: make(map[flowgo.Identifier]uint64), + blocks: make(map[uint64]flowgo.Block), + collections: make(map[flowgo.Identifier]flowgo.LightCollection), + transactions: make(map[flowgo.Identifier]flowgo.TransactionBody), + transactionResults: make(map[flowgo.Identifier]StorableTransactionResult), + ledger: make(map[uint64]snapshot.SnapshotTree), + eventsByBlockHeight: make(map[uint64][]flowgo.Event), + } +} + +func (b *Store) Start() error { + return nil +} + +func (b *Store) Stop() { +} + +func (b *Store) LatestBlockHeight(ctx context.Context) (uint64, error) { + block, err := b.LatestBlock(ctx) + if err != nil { + return 0, err + } + + return block.Header.Height, nil +} + +func (b *Store) LatestBlock(_ context.Context) (flowgo.Block, error) { + b.mu.RLock() + defer b.mu.RUnlock() + + latestBlock, ok := b.blocks[b.blockHeight] + if !ok { + return flowgo.Block{}, ErrNotFound + } + return latestBlock, nil +} + +func (b *Store) StoreBlock(_ context.Context, block *flowgo.Block) error { + b.mu.Lock() + defer b.mu.Unlock() + + return b.storeBlock(block) +} + +func (b *Store) storeBlock(block *flowgo.Block) error { + b.blocks[block.Header.Height] = *block + b.blockIDToHeight[block.ID()] = block.Header.Height + + if block.Header.Height > b.blockHeight { + b.blockHeight = block.Header.Height + } + + return nil +} + +func (b *Store) BlockByID(_ context.Context, blockID flowgo.Identifier) (*flowgo.Block, error) { + b.mu.RLock() + defer b.mu.RUnlock() + + blockHeight, ok := b.blockIDToHeight[blockID] + if !ok { + return nil, ErrNotFound + } + + block, ok := b.blocks[blockHeight] + if !ok { + return nil, ErrNotFound + } + + return &block, nil + +} + +func (b *Store) BlockByHeight(_ context.Context, height uint64) (*flowgo.Block, error) { + b.mu.RLock() + defer b.mu.RUnlock() + + block, ok := b.blocks[height] + if !ok { + return nil, ErrNotFound + } + + return &block, nil +} + +func (b *Store) CommitBlock( + _ context.Context, + block flowgo.Block, + collections []*flowgo.LightCollection, + transactions map[flowgo.Identifier]*flowgo.TransactionBody, + transactionResults map[flowgo.Identifier]*StorableTransactionResult, + executionSnapshot *snapshot.ExecutionSnapshot, + events []flowgo.Event, +) error { + b.mu.Lock() + defer b.mu.Unlock() + + if len(transactions) != len(transactionResults) { + return fmt.Errorf( + "transactions count (%d) does not match result count (%d)", + len(transactions), + len(transactionResults), + ) + } + + err := b.storeBlock(&block) + if err != nil { + return err + } + + for _, col := range collections { + err := b.InsertCollection(*col) + if err != nil { + return err + } + } + + for _, tx := range transactions { + err := b.InsertTransaction(tx.ID(), *tx) + if err != nil { + return err + } + } + + for txID, result := range transactionResults { + err := b.InsertTransactionResult(txID, *result) + if err != nil { + return err + } + } + + err = b.InsertExecutionSnapshot( + block.Header.Height, + executionSnapshot) + if err != nil { + return err + } + + err = b.InsertEvents(block.Header.Height, events) + if err != nil { + return err + } + + return nil +} + +func (b *Store) CollectionByID( + _ context.Context, + collectionID flowgo.Identifier, +) (flowgo.LightCollection, error) { + b.mu.RLock() + defer b.mu.RUnlock() + + tx, ok := b.collections[collectionID] + if !ok { + return flowgo.LightCollection{}, ErrNotFound + } + return tx, nil +} + +func (b *Store) FullCollectionByID( + _ context.Context, + collectionID flowgo.Identifier, +) (flowgo.Collection, error) { + b.mu.RLock() + defer b.mu.RUnlock() + + light, ok := b.collections[collectionID] + if !ok { + return flowgo.Collection{}, ErrNotFound + } + + txs := make([]*flowgo.TransactionBody, len(light.Transactions)) + for i, txID := range light.Transactions { + tx, ok := b.transactions[txID] + if !ok { + return flowgo.Collection{}, ErrNotFound + } + txs[i] = &tx + } + + return flowgo.Collection{ + Transactions: txs, + }, nil +} + +func (b *Store) TransactionByID( + _ context.Context, + transactionID flowgo.Identifier, +) (flowgo.TransactionBody, error) { + b.mu.RLock() + defer b.mu.RUnlock() + + tx, ok := b.transactions[transactionID] + if !ok { + return flowgo.TransactionBody{}, ErrNotFound + } + return tx, nil + +} + +func (b *Store) TransactionResultByID( + _ context.Context, + transactionID flowgo.Identifier, +) (StorableTransactionResult, error) { + b.mu.RLock() + defer b.mu.RUnlock() + + result, ok := b.transactionResults[transactionID] + if !ok { + return StorableTransactionResult{}, ErrNotFound + } + return result, nil + +} + +func (b *Store) LedgerByHeight( + _ context.Context, + blockHeight uint64, +) (snapshot.StorageSnapshot, error) { + return b.ledger[blockHeight], nil +} + +func (b *Store) EventsByHeight( + _ context.Context, + blockHeight uint64, + eventType string, +) ([]flowgo.Event, error) { + b.mu.RLock() + defer b.mu.RUnlock() + + allEvents := b.eventsByBlockHeight[blockHeight] + + events := make([]flowgo.Event, 0) + + for _, event := range allEvents { + if eventType == "" { + events = append(events, event) + } else { + if string(event.Type) == eventType { + events = append(events, event) + } + } + } + + return events, nil +} + +func (b *Store) InsertCollection(col flowgo.LightCollection) error { + b.collections[col.ID()] = col + return nil +} + +func (b *Store) InsertTransaction(txID flowgo.Identifier, tx flowgo.TransactionBody) error { + b.transactions[txID] = tx + return nil +} + +func (b *Store) InsertTransactionResult(txID flowgo.Identifier, result StorableTransactionResult) error { + b.transactionResults[txID] = result + return nil +} + +func (b *Store) InsertExecutionSnapshot( + blockHeight uint64, + executionSnapshot *snapshot.ExecutionSnapshot, +) error { + oldLedger := b.ledger[blockHeight-1] + + b.ledger[blockHeight] = oldLedger.Append(executionSnapshot) + + return nil +} + +func (b *Store) InsertEvents(blockHeight uint64, events []flowgo.Event) error { + if b.eventsByBlockHeight[blockHeight] == nil { + b.eventsByBlockHeight[blockHeight] = events + } else { + b.eventsByBlockHeight[blockHeight] = append(b.eventsByBlockHeight[blockHeight], events...) + } + + return nil +} diff --git a/integration/internal/emulator/mocks/emulator.go b/integration/internal/emulator/mocks/emulator.go new file mode 100644 index 00000000000..1e81328bafe --- /dev/null +++ b/integration/internal/emulator/mocks/emulator.go @@ -0,0 +1,469 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/onflow/flow-go/integration/emulator (interfaces: Emulator) +// +// Generated by this command: +// +// mockgen -destination=emulator/mocks/emulator.go -package=mocks github.com/onflow/flow-go/integration/emulator Emulator +// + +// Package mocks is a generated GoMock package. +package mocks + +import ( + reflect "reflect" + + gomock "go.uber.org/mock/gomock" + + access "github.com/onflow/flow-go/access" + emulator "github.com/onflow/flow-go/integration/internal/emulator" + flow "github.com/onflow/flow-go/model/flow" +) + +// MockEmulator is a mock of Emulator interface. +type MockEmulator struct { + ctrl *gomock.Controller + recorder *MockEmulatorMockRecorder + isgomock struct{} +} + +// MockEmulatorMockRecorder is the mock recorder for MockEmulator. +type MockEmulatorMockRecorder struct { + mock *MockEmulator +} + +// NewMockEmulator creates a new mock instance. +func NewMockEmulator(ctrl *gomock.Controller) *MockEmulator { + mock := &MockEmulator{ctrl: ctrl} + mock.recorder = &MockEmulatorMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockEmulator) EXPECT() *MockEmulatorMockRecorder { + return m.recorder +} + +// AddTransaction mocks base method. +func (m *MockEmulator) AddTransaction(tx flow.TransactionBody) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AddTransaction", tx) + ret0, _ := ret[0].(error) + return ret0 +} + +// AddTransaction indicates an expected call of AddTransaction. +func (mr *MockEmulatorMockRecorder) AddTransaction(tx any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddTransaction", reflect.TypeOf((*MockEmulator)(nil).AddTransaction), tx) +} + +// CommitBlock mocks base method. +func (m *MockEmulator) CommitBlock() (*flow.Block, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CommitBlock") + ret0, _ := ret[0].(*flow.Block) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CommitBlock indicates an expected call of CommitBlock. +func (mr *MockEmulatorMockRecorder) CommitBlock() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CommitBlock", reflect.TypeOf((*MockEmulator)(nil).CommitBlock)) +} + +// DisableAutoMine mocks base method. +func (m *MockEmulator) DisableAutoMine() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "DisableAutoMine") +} + +// DisableAutoMine indicates an expected call of DisableAutoMine. +func (mr *MockEmulatorMockRecorder) DisableAutoMine() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DisableAutoMine", reflect.TypeOf((*MockEmulator)(nil).DisableAutoMine)) +} + +// EnableAutoMine mocks base method. +func (m *MockEmulator) EnableAutoMine() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "EnableAutoMine") +} + +// EnableAutoMine indicates an expected call of EnableAutoMine. +func (mr *MockEmulatorMockRecorder) EnableAutoMine() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnableAutoMine", reflect.TypeOf((*MockEmulator)(nil).EnableAutoMine)) +} + +// ExecuteAndCommitBlock mocks base method. +func (m *MockEmulator) ExecuteAndCommitBlock() (*flow.Block, []*emulator.TransactionResult, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ExecuteAndCommitBlock") + ret0, _ := ret[0].(*flow.Block) + ret1, _ := ret[1].([]*emulator.TransactionResult) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// ExecuteAndCommitBlock indicates an expected call of ExecuteAndCommitBlock. +func (mr *MockEmulatorMockRecorder) ExecuteAndCommitBlock() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExecuteAndCommitBlock", reflect.TypeOf((*MockEmulator)(nil).ExecuteAndCommitBlock)) +} + +// ExecuteBlock mocks base method. +func (m *MockEmulator) ExecuteBlock() ([]*emulator.TransactionResult, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ExecuteBlock") + ret0, _ := ret[0].([]*emulator.TransactionResult) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ExecuteBlock indicates an expected call of ExecuteBlock. +func (mr *MockEmulatorMockRecorder) ExecuteBlock() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExecuteBlock", reflect.TypeOf((*MockEmulator)(nil).ExecuteBlock)) +} + +// ExecuteNextTransaction mocks base method. +func (m *MockEmulator) ExecuteNextTransaction() (*emulator.TransactionResult, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ExecuteNextTransaction") + ret0, _ := ret[0].(*emulator.TransactionResult) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ExecuteNextTransaction indicates an expected call of ExecuteNextTransaction. +func (mr *MockEmulatorMockRecorder) ExecuteNextTransaction() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExecuteNextTransaction", reflect.TypeOf((*MockEmulator)(nil).ExecuteNextTransaction)) +} + +// ExecuteScript mocks base method. +func (m *MockEmulator) ExecuteScript(script []byte, arguments [][]byte) (*emulator.ScriptResult, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ExecuteScript", script, arguments) + ret0, _ := ret[0].(*emulator.ScriptResult) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ExecuteScript indicates an expected call of ExecuteScript. +func (mr *MockEmulatorMockRecorder) ExecuteScript(script, arguments any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExecuteScript", reflect.TypeOf((*MockEmulator)(nil).ExecuteScript), script, arguments) +} + +// ExecuteScriptAtBlockHeight mocks base method. +func (m *MockEmulator) ExecuteScriptAtBlockHeight(script []byte, arguments [][]byte, blockHeight uint64) (*emulator.ScriptResult, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ExecuteScriptAtBlockHeight", script, arguments, blockHeight) + ret0, _ := ret[0].(*emulator.ScriptResult) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ExecuteScriptAtBlockHeight indicates an expected call of ExecuteScriptAtBlockHeight. +func (mr *MockEmulatorMockRecorder) ExecuteScriptAtBlockHeight(script, arguments, blockHeight any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExecuteScriptAtBlockHeight", reflect.TypeOf((*MockEmulator)(nil).ExecuteScriptAtBlockHeight), script, arguments, blockHeight) +} + +// ExecuteScriptAtBlockID mocks base method. +func (m *MockEmulator) ExecuteScriptAtBlockID(script []byte, arguments [][]byte, id flow.Identifier) (*emulator.ScriptResult, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ExecuteScriptAtBlockID", script, arguments, id) + ret0, _ := ret[0].(*emulator.ScriptResult) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ExecuteScriptAtBlockID indicates an expected call of ExecuteScriptAtBlockID. +func (mr *MockEmulatorMockRecorder) ExecuteScriptAtBlockID(script, arguments, id any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExecuteScriptAtBlockID", reflect.TypeOf((*MockEmulator)(nil).ExecuteScriptAtBlockID), script, arguments, id) +} + +// GetAccount mocks base method. +func (m *MockEmulator) GetAccount(address flow.Address) (*flow.Account, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAccount", address) + ret0, _ := ret[0].(*flow.Account) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetAccount indicates an expected call of GetAccount. +func (mr *MockEmulatorMockRecorder) GetAccount(address any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccount", reflect.TypeOf((*MockEmulator)(nil).GetAccount), address) +} + +// GetAccountAtBlockHeight mocks base method. +func (m *MockEmulator) GetAccountAtBlockHeight(address flow.Address, blockHeight uint64) (*flow.Account, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAccountAtBlockHeight", address, blockHeight) + ret0, _ := ret[0].(*flow.Account) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetAccountAtBlockHeight indicates an expected call of GetAccountAtBlockHeight. +func (mr *MockEmulatorMockRecorder) GetAccountAtBlockHeight(address, blockHeight any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountAtBlockHeight", reflect.TypeOf((*MockEmulator)(nil).GetAccountAtBlockHeight), address, blockHeight) +} + +// GetAccountByIndex mocks base method. +func (m *MockEmulator) GetAccountByIndex(arg0 uint) (*flow.Account, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAccountByIndex", arg0) + ret0, _ := ret[0].(*flow.Account) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetAccountByIndex indicates an expected call of GetAccountByIndex. +func (mr *MockEmulatorMockRecorder) GetAccountByIndex(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountByIndex", reflect.TypeOf((*MockEmulator)(nil).GetAccountByIndex), arg0) +} + +// GetBlockByHeight mocks base method. +func (m *MockEmulator) GetBlockByHeight(height uint64) (*flow.Block, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetBlockByHeight", height) + ret0, _ := ret[0].(*flow.Block) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetBlockByHeight indicates an expected call of GetBlockByHeight. +func (mr *MockEmulatorMockRecorder) GetBlockByHeight(height any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBlockByHeight", reflect.TypeOf((*MockEmulator)(nil).GetBlockByHeight), height) +} + +// GetBlockByID mocks base method. +func (m *MockEmulator) GetBlockByID(id flow.Identifier) (*flow.Block, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetBlockByID", id) + ret0, _ := ret[0].(*flow.Block) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetBlockByID indicates an expected call of GetBlockByID. +func (mr *MockEmulatorMockRecorder) GetBlockByID(id any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBlockByID", reflect.TypeOf((*MockEmulator)(nil).GetBlockByID), id) +} + +// GetCollectionByID mocks base method. +func (m *MockEmulator) GetCollectionByID(colID flow.Identifier) (*flow.LightCollection, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetCollectionByID", colID) + ret0, _ := ret[0].(*flow.LightCollection) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetCollectionByID indicates an expected call of GetCollectionByID. +func (mr *MockEmulatorMockRecorder) GetCollectionByID(colID any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCollectionByID", reflect.TypeOf((*MockEmulator)(nil).GetCollectionByID), colID) +} + +// GetEventsByHeight mocks base method. +func (m *MockEmulator) GetEventsByHeight(blockHeight uint64, eventType string) ([]flow.Event, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetEventsByHeight", blockHeight, eventType) + ret0, _ := ret[0].([]flow.Event) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetEventsByHeight indicates an expected call of GetEventsByHeight. +func (mr *MockEmulatorMockRecorder) GetEventsByHeight(blockHeight, eventType any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetEventsByHeight", reflect.TypeOf((*MockEmulator)(nil).GetEventsByHeight), blockHeight, eventType) +} + +// GetEventsForBlockIDs mocks base method. +func (m *MockEmulator) GetEventsForBlockIDs(eventType string, blockIDs []flow.Identifier) ([]flow.BlockEvents, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetEventsForBlockIDs", eventType, blockIDs) + ret0, _ := ret[0].([]flow.BlockEvents) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetEventsForBlockIDs indicates an expected call of GetEventsForBlockIDs. +func (mr *MockEmulatorMockRecorder) GetEventsForBlockIDs(eventType, blockIDs any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetEventsForBlockIDs", reflect.TypeOf((*MockEmulator)(nil).GetEventsForBlockIDs), eventType, blockIDs) +} + +// GetEventsForHeightRange mocks base method. +func (m *MockEmulator) GetEventsForHeightRange(eventType string, startHeight, endHeight uint64) ([]flow.BlockEvents, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetEventsForHeightRange", eventType, startHeight, endHeight) + ret0, _ := ret[0].([]flow.BlockEvents) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetEventsForHeightRange indicates an expected call of GetEventsForHeightRange. +func (mr *MockEmulatorMockRecorder) GetEventsForHeightRange(eventType, startHeight, endHeight any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetEventsForHeightRange", reflect.TypeOf((*MockEmulator)(nil).GetEventsForHeightRange), eventType, startHeight, endHeight) +} + +// GetFullCollectionByID mocks base method. +func (m *MockEmulator) GetFullCollectionByID(colID flow.Identifier) (*flow.Collection, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetFullCollectionByID", colID) + ret0, _ := ret[0].(*flow.Collection) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetFullCollectionByID indicates an expected call of GetFullCollectionByID. +func (mr *MockEmulatorMockRecorder) GetFullCollectionByID(colID any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFullCollectionByID", reflect.TypeOf((*MockEmulator)(nil).GetFullCollectionByID), colID) +} + +// GetLatestBlock mocks base method. +func (m *MockEmulator) GetLatestBlock() (*flow.Block, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetLatestBlock") + ret0, _ := ret[0].(*flow.Block) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetLatestBlock indicates an expected call of GetLatestBlock. +func (mr *MockEmulatorMockRecorder) GetLatestBlock() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLatestBlock", reflect.TypeOf((*MockEmulator)(nil).GetLatestBlock)) +} + +// GetNetworkParameters mocks base method. +func (m *MockEmulator) GetNetworkParameters() access.NetworkParameters { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetNetworkParameters") + ret0, _ := ret[0].(access.NetworkParameters) + return ret0 +} + +// GetNetworkParameters indicates an expected call of GetNetworkParameters. +func (mr *MockEmulatorMockRecorder) GetNetworkParameters() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNetworkParameters", reflect.TypeOf((*MockEmulator)(nil).GetNetworkParameters)) +} + +// GetTransaction mocks base method. +func (m *MockEmulator) GetTransaction(txID flow.Identifier) (*flow.TransactionBody, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetTransaction", txID) + ret0, _ := ret[0].(*flow.TransactionBody) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetTransaction indicates an expected call of GetTransaction. +func (mr *MockEmulatorMockRecorder) GetTransaction(txID any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTransaction", reflect.TypeOf((*MockEmulator)(nil).GetTransaction), txID) +} + +// GetTransactionResult mocks base method. +func (m *MockEmulator) GetTransactionResult(txID flow.Identifier) (*access.TransactionResult, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetTransactionResult", txID) + ret0, _ := ret[0].(*access.TransactionResult) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetTransactionResult indicates an expected call of GetTransactionResult. +func (mr *MockEmulatorMockRecorder) GetTransactionResult(txID any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTransactionResult", reflect.TypeOf((*MockEmulator)(nil).GetTransactionResult), txID) +} + +// GetTransactionResultsByBlockID mocks base method. +func (m *MockEmulator) GetTransactionResultsByBlockID(blockID flow.Identifier) ([]*access.TransactionResult, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetTransactionResultsByBlockID", blockID) + ret0, _ := ret[0].([]*access.TransactionResult) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetTransactionResultsByBlockID indicates an expected call of GetTransactionResultsByBlockID. +func (mr *MockEmulatorMockRecorder) GetTransactionResultsByBlockID(blockID any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTransactionResultsByBlockID", reflect.TypeOf((*MockEmulator)(nil).GetTransactionResultsByBlockID), blockID) +} + +// GetTransactionsByBlockID mocks base method. +func (m *MockEmulator) GetTransactionsByBlockID(blockID flow.Identifier) ([]*flow.TransactionBody, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetTransactionsByBlockID", blockID) + ret0, _ := ret[0].([]*flow.TransactionBody) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetTransactionsByBlockID indicates an expected call of GetTransactionsByBlockID. +func (mr *MockEmulatorMockRecorder) GetTransactionsByBlockID(blockID any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTransactionsByBlockID", reflect.TypeOf((*MockEmulator)(nil).GetTransactionsByBlockID), blockID) +} + +// Ping mocks base method. +func (m *MockEmulator) Ping() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Ping") + ret0, _ := ret[0].(error) + return ret0 +} + +// Ping indicates an expected call of Ping. +func (mr *MockEmulatorMockRecorder) Ping() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Ping", reflect.TypeOf((*MockEmulator)(nil).Ping)) +} + +// SendTransaction mocks base method. +func (m *MockEmulator) SendTransaction(tx *flow.TransactionBody) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SendTransaction", tx) + ret0, _ := ret[0].(error) + return ret0 +} + +// SendTransaction indicates an expected call of SendTransaction. +func (mr *MockEmulatorMockRecorder) SendTransaction(tx any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendTransaction", reflect.TypeOf((*MockEmulator)(nil).SendTransaction), tx) +} + +// ServiceKey mocks base method. +func (m *MockEmulator) ServiceKey() emulator.ServiceKey { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ServiceKey") + ret0, _ := ret[0].(emulator.ServiceKey) + return ret0 +} + +// ServiceKey indicates an expected call of ServiceKey. +func (mr *MockEmulatorMockRecorder) ServiceKey() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ServiceKey", reflect.TypeOf((*MockEmulator)(nil).ServiceKey)) +} diff --git a/integration/internal/emulator/mocks/emulatorStorage.go b/integration/internal/emulator/mocks/emulatorStorage.go new file mode 100644 index 00000000000..32f9b4d6170 --- /dev/null +++ b/integration/internal/emulator/mocks/emulatorStorage.go @@ -0,0 +1,324 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/onflow/flow-emulator/emulator (interfaces: EmulatorStorage) +// +// Generated by this command: +// +// mockgen -destination=emulator/mocks/emulatorStorage.go -package=mocks github.com/onflow/flow-emulator/emulator EmulatorStorage +// + +// Package mocks is a generated GoMock package. +package mocks + +import ( + context "context" + reflect "reflect" + + gomock "go.uber.org/mock/gomock" + + snapshot "github.com/onflow/flow-go/fvm/storage/snapshot" + "github.com/onflow/flow-go/integration/internal/emulator" + flow "github.com/onflow/flow-go/model/flow" +) + +// MockEmulatorStorage is a mock of EmulatorStorage interface. +type MockEmulatorStorage struct { + ctrl *gomock.Controller + recorder *MockEmulatorStorageMockRecorder + isgomock struct{} +} + +// MockEmulatorStorageMockRecorder is the mock recorder for MockEmulatorStorage. +type MockEmulatorStorageMockRecorder struct { + mock *MockEmulatorStorage +} + +// NewMockEmulatorStorage creates a new mock instance. +func NewMockEmulatorStorage(ctrl *gomock.Controller) *MockEmulatorStorage { + mock := &MockEmulatorStorage{ctrl: ctrl} + mock.recorder = &MockEmulatorStorageMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockEmulatorStorage) EXPECT() *MockEmulatorStorageMockRecorder { + return m.recorder +} + +// BlockByHeight mocks base method. +func (m *MockEmulatorStorage) BlockByHeight(ctx context.Context, height uint64) (*flow.Block, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "BlockByHeight", ctx, height) + ret0, _ := ret[0].(*flow.Block) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// BlockByHeight indicates an expected call of BlockByHeight. +func (mr *MockEmulatorStorageMockRecorder) BlockByHeight(ctx, height any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BlockByHeight", reflect.TypeOf((*MockEmulatorStorage)(nil).BlockByHeight), ctx, height) +} + +// BlockByID mocks base method. +func (m *MockEmulatorStorage) BlockByID(ctx context.Context, blockID flow.Identifier) (*flow.Block, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "BlockByID", ctx, blockID) + ret0, _ := ret[0].(*flow.Block) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// BlockByID indicates an expected call of BlockByID. +func (mr *MockEmulatorStorageMockRecorder) BlockByID(ctx, blockID any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BlockByID", reflect.TypeOf((*MockEmulatorStorage)(nil).BlockByID), ctx, blockID) +} + +// ByHeightFrom mocks base method. +func (m *MockEmulatorStorage) ByHeightFrom(height uint64, header *flow.Header) (*flow.Header, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ByHeightFrom", height, header) + ret0, _ := ret[0].(*flow.Header) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ByHeightFrom indicates an expected call of ByHeightFrom. +func (mr *MockEmulatorStorageMockRecorder) ByHeightFrom(height, header any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ByHeightFrom", reflect.TypeOf((*MockEmulatorStorage)(nil).ByHeightFrom), height, header) +} + +// CollectionByID mocks base method. +func (m *MockEmulatorStorage) CollectionByID(ctx context.Context, collectionID flow.Identifier) (flow.LightCollection, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CollectionByID", ctx, collectionID) + ret0, _ := ret[0].(flow.LightCollection) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CollectionByID indicates an expected call of CollectionByID. +func (mr *MockEmulatorStorageMockRecorder) CollectionByID(ctx, collectionID any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CollectionByID", reflect.TypeOf((*MockEmulatorStorage)(nil).CollectionByID), ctx, collectionID) +} + +// CommitBlock mocks base method. +func (m *MockEmulatorStorage) CommitBlock(ctx context.Context, block flow.Block, collections []*flow.LightCollection, transactions map[flow.Identifier]*flow.TransactionBody, transactionResults map[flow.Identifier]*emulator.StorableTransactionResult, executionSnapshot *snapshot.ExecutionSnapshot, events []flow.Event) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CommitBlock", ctx, block, collections, transactions, transactionResults, executionSnapshot, events) + ret0, _ := ret[0].(error) + return ret0 +} + +// CommitBlock indicates an expected call of CommitBlock. +func (mr *MockEmulatorStorageMockRecorder) CommitBlock(ctx, block, collections, transactions, transactionResults, executionSnapshot, events any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CommitBlock", reflect.TypeOf((*MockEmulatorStorage)(nil).CommitBlock), ctx, block, collections, transactions, transactionResults, executionSnapshot, events) +} + +// EventsByHeight mocks base method. +func (m *MockEmulatorStorage) EventsByHeight(ctx context.Context, blockHeight uint64, eventType string) ([]flow.Event, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EventsByHeight", ctx, blockHeight, eventType) + ret0, _ := ret[0].([]flow.Event) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// EventsByHeight indicates an expected call of EventsByHeight. +func (mr *MockEmulatorStorageMockRecorder) EventsByHeight(ctx, blockHeight, eventType any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EventsByHeight", reflect.TypeOf((*MockEmulatorStorage)(nil).EventsByHeight), ctx, blockHeight, eventType) +} + +// FinalizedHeader mocks base method. +func (m *MockEmulatorStorage) FinalizedHeader() (*flow.Header, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FinalizedHeader") + ret0, _ := ret[0].(*flow.Header) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// FinalizedHeader indicates an expected call of FinalizedHeader. +func (mr *MockEmulatorStorageMockRecorder) FinalizedHeader() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FinalizedHeader", reflect.TypeOf((*MockEmulatorStorage)(nil).FinalizedHeader)) +} + +// FullCollectionByID mocks base method. +func (m *MockEmulatorStorage) FullCollectionByID(ctx context.Context, collectionID flow.Identifier) (flow.Collection, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FullCollectionByID", ctx, collectionID) + ret0, _ := ret[0].(flow.Collection) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// FullCollectionByID indicates an expected call of FullCollectionByID. +func (mr *MockEmulatorStorageMockRecorder) FullCollectionByID(ctx, collectionID any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FullCollectionByID", reflect.TypeOf((*MockEmulatorStorage)(nil).FullCollectionByID), ctx, collectionID) +} + +// HeaderByID mocks base method. +func (m *MockEmulatorStorage) HeaderByID(id flow.Identifier) (*flow.Header, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "HeaderByID", id) + ret0, _ := ret[0].(*flow.Header) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// HeaderByID indicates an expected call of HeaderByID. +func (mr *MockEmulatorStorageMockRecorder) HeaderByID(id any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HeaderByID", reflect.TypeOf((*MockEmulatorStorage)(nil).HeaderByID), id) +} + +// IndexedHeight mocks base method. +func (m *MockEmulatorStorage) IndexedHeight() (uint64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IndexedHeight") + ret0, _ := ret[0].(uint64) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// IndexedHeight indicates an expected call of IndexedHeight. +func (mr *MockEmulatorStorageMockRecorder) IndexedHeight() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IndexedHeight", reflect.TypeOf((*MockEmulatorStorage)(nil).IndexedHeight)) +} + +// LatestBlock mocks base method. +func (m *MockEmulatorStorage) LatestBlock(ctx context.Context) (flow.Block, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "LatestBlock", ctx) + ret0, _ := ret[0].(flow.Block) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// LatestBlock indicates an expected call of LatestBlock. +func (mr *MockEmulatorStorageMockRecorder) LatestBlock(ctx any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LatestBlock", reflect.TypeOf((*MockEmulatorStorage)(nil).LatestBlock), ctx) +} + +// LatestBlockHeight mocks base method. +func (m *MockEmulatorStorage) LatestBlockHeight(ctx context.Context) (uint64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "LatestBlockHeight", ctx) + ret0, _ := ret[0].(uint64) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// LatestBlockHeight indicates an expected call of LatestBlockHeight. +func (mr *MockEmulatorStorageMockRecorder) LatestBlockHeight(ctx any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LatestBlockHeight", reflect.TypeOf((*MockEmulatorStorage)(nil).LatestBlockHeight), ctx) +} + +// LedgerByHeight mocks base method. +func (m *MockEmulatorStorage) LedgerByHeight(ctx context.Context, blockHeight uint64) (snapshot.StorageSnapshot, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "LedgerByHeight", ctx, blockHeight) + ret0, _ := ret[0].(snapshot.StorageSnapshot) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// LedgerByHeight indicates an expected call of LedgerByHeight. +func (mr *MockEmulatorStorageMockRecorder) LedgerByHeight(ctx, blockHeight any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LedgerByHeight", reflect.TypeOf((*MockEmulatorStorage)(nil).LedgerByHeight), ctx, blockHeight) +} + +// SealedHeader mocks base method. +func (m *MockEmulatorStorage) SealedHeader() (*flow.Header, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SealedHeader") + ret0, _ := ret[0].(*flow.Header) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// SealedHeader indicates an expected call of SealedHeader. +func (mr *MockEmulatorStorageMockRecorder) SealedHeader() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SealedHeader", reflect.TypeOf((*MockEmulatorStorage)(nil).SealedHeader)) +} + +// Start mocks base method. +func (m *MockEmulatorStorage) Start() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Start") + ret0, _ := ret[0].(error) + return ret0 +} + +// Start indicates an expected call of Start. +func (mr *MockEmulatorStorageMockRecorder) Start() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockEmulatorStorage)(nil).Start)) +} + +// Stop mocks base method. +func (m *MockEmulatorStorage) Stop() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Stop") +} + +// Stop indicates an expected call of Stop. +func (mr *MockEmulatorStorageMockRecorder) Stop() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockEmulatorStorage)(nil).Stop)) +} + +// StoreBlock mocks base method. +func (m *MockEmulatorStorage) StoreBlock(ctx context.Context, block *flow.Block) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "StoreBlock", ctx, block) + ret0, _ := ret[0].(error) + return ret0 +} + +// StoreBlock indicates an expected call of StoreBlock. +func (mr *MockEmulatorStorageMockRecorder) StoreBlock(ctx, block any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StoreBlock", reflect.TypeOf((*MockEmulatorStorage)(nil).StoreBlock), ctx, block) +} + +// TransactionByID mocks base method. +func (m *MockEmulatorStorage) TransactionByID(ctx context.Context, transactionID flow.Identifier) (flow.TransactionBody, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "TransactionByID", ctx, transactionID) + ret0, _ := ret[0].(flow.TransactionBody) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// TransactionByID indicates an expected call of TransactionByID. +func (mr *MockEmulatorStorageMockRecorder) TransactionByID(ctx, transactionID any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TransactionByID", reflect.TypeOf((*MockEmulatorStorage)(nil).TransactionByID), ctx, transactionID) +} + +// TransactionResultByID mocks base method. +func (m *MockEmulatorStorage) TransactionResultByID(ctx context.Context, transactionID flow.Identifier) (emulator.StorableTransactionResult, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "TransactionResultByID", ctx, transactionID) + ret0, _ := ret[0].(emulator.StorableTransactionResult) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// TransactionResultByID indicates an expected call of TransactionResultByID. +func (mr *MockEmulatorStorageMockRecorder) TransactionResultByID(ctx, transactionID any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TransactionResultByID", reflect.TypeOf((*MockEmulatorStorage)(nil).TransactionResultByID), ctx, transactionID) +} diff --git a/integration/internal/emulator/pendingBlock.go b/integration/internal/emulator/pendingBlock.go new file mode 100644 index 00000000000..4d92e8a40a7 --- /dev/null +++ b/integration/internal/emulator/pendingBlock.go @@ -0,0 +1,236 @@ +/* + * Flow Emulator + * + * Copyright Flow Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package emulator + +import ( + "math/rand" + "time" + + "github.com/onflow/flow-go/fvm" + "github.com/onflow/flow-go/fvm/storage/snapshot" + "github.com/onflow/flow-go/fvm/storage/state" + flowgo "github.com/onflow/flow-go/model/flow" +) + +type IndexedTransactionResult struct { + fvm.ProcedureOutput + Index uint32 +} + +// MaxViewIncrease represents the largest difference in view number between +// two consecutive blocks. The minimum view increment is 1. +const MaxViewIncrease = 3 + +// A pendingBlock contains the pending state required to form a new block. +type pendingBlock struct { + height uint64 + view uint64 + parentID flowgo.Identifier + timestamp time.Time + // mapping from transaction ID to transaction + transactions map[flowgo.Identifier]*flowgo.TransactionBody + // list of transaction IDs in the block + transactionIDs []flowgo.Identifier + // mapping from transaction ID to transaction result + transactionResults map[flowgo.Identifier]IndexedTransactionResult + // current working ledger, updated after each transaction execution + ledgerState *state.ExecutionState + // events emitted during execution + events []flowgo.Event + // index of transaction execution + index uint32 +} + +// newPendingBlock creates a new pending block sequentially after a specified block. +func newPendingBlock( + prevBlock *flowgo.Block, + ledgerSnapshot snapshot.StorageSnapshot, + timestamp time.Time, +) *pendingBlock { + return &pendingBlock{ + height: prevBlock.Header.Height + 1, + // the view increments by between 1 and MaxViewIncrease to match + // behaviour on a real network, where views are not consecutive + view: prevBlock.Header.View + uint64(rand.Intn(MaxViewIncrease)+1), + parentID: prevBlock.ID(), + timestamp: timestamp, + transactions: make(map[flowgo.Identifier]*flowgo.TransactionBody), + transactionIDs: make([]flowgo.Identifier, 0), + transactionResults: make(map[flowgo.Identifier]IndexedTransactionResult), + ledgerState: state.NewExecutionState( + ledgerSnapshot, + state.DefaultParameters()), + events: make([]flowgo.Event, 0), + index: 0, + } +} + +// ID returns the ID of the pending block. +func (b *pendingBlock) ID() flowgo.Identifier { + return b.Block().ID() +} + +// Block returns the block information for the pending block. +func (b *pendingBlock) Block() *flowgo.Block { + collections := b.Collections() + + guarantees := make([]*flowgo.CollectionGuarantee, len(collections)) + for i, collection := range collections { + guarantees[i] = &flowgo.CollectionGuarantee{ + CollectionID: collection.ID(), + } + } + + return &flowgo.Block{ + Header: &flowgo.Header{ + Height: b.height, + View: b.view, + ParentID: b.parentID, + Timestamp: b.timestamp, + }, + Payload: &flowgo.Payload{ + Guarantees: guarantees, + }, + } +} + +func (b *pendingBlock) Collections() []*flowgo.LightCollection { + if len(b.transactionIDs) == 0 { + return []*flowgo.LightCollection{} + } + + transactionIDs := make([]flowgo.Identifier, len(b.transactionIDs)) + + // TODO: remove once SDK models are removed + copy(transactionIDs, b.transactionIDs) + + collection := flowgo.LightCollection{Transactions: transactionIDs} + + return []*flowgo.LightCollection{&collection} +} + +func (b *pendingBlock) Transactions() map[flowgo.Identifier]*flowgo.TransactionBody { + return b.transactions +} + +func (b *pendingBlock) TransactionResults() map[flowgo.Identifier]IndexedTransactionResult { + return b.transactionResults +} + +// Finalize returns the execution snapshot for the pending block. +func (b *pendingBlock) Finalize() *snapshot.ExecutionSnapshot { + return b.ledgerState.Finalize() +} + +// AddTransaction adds a transaction to the pending block. +func (b *pendingBlock) AddTransaction(tx flowgo.TransactionBody) { + b.transactionIDs = append(b.transactionIDs, tx.ID()) + b.transactions[tx.ID()] = &tx +} + +// ContainsTransaction checks if a transaction is included in the pending block. +func (b *pendingBlock) ContainsTransaction(txID flowgo.Identifier) bool { + _, exists := b.transactions[txID] + return exists +} + +// GetTransaction retrieves a transaction in the pending block by ID. +func (b *pendingBlock) GetTransaction(txID flowgo.Identifier) *flowgo.TransactionBody { + return b.transactions[txID] +} + +// NextTransaction returns the next indexed transaction. +func (b *pendingBlock) NextTransaction() *flowgo.TransactionBody { + if int(b.index) > len(b.transactionIDs) { + return nil + } + + txID := b.transactionIDs[b.index] + return b.GetTransaction(txID) +} + +// ExecuteNextTransaction executes the next transaction in the pending block. +// +// This function uses the provided execute function to perform the actual +// execution, then updates the pending block with the output. +func (b *pendingBlock) ExecuteNextTransaction( + vm *fvm.VirtualMachine, + ctx fvm.Context, +) ( + fvm.ProcedureOutput, + error, +) { + txnBody := b.NextTransaction() + txnIndex := b.index + + // increment transaction index even if transaction reverts + b.index++ + + executionSnapshot, output, err := vm.Run( + ctx, + fvm.Transaction(txnBody, txnIndex), + b.ledgerState) + if err != nil { + // fail fast if fatal error occurs + return fvm.ProcedureOutput{}, err + } + + b.events = append(b.events, output.Events...) + + err = b.ledgerState.Merge(executionSnapshot) + if err != nil { + // fail fast if fatal error occurs + return fvm.ProcedureOutput{}, err + } + + b.transactionResults[txnBody.ID()] = IndexedTransactionResult{ + ProcedureOutput: output, + Index: txnIndex, + } + + return output, nil +} + +// Events returns all events captured during the execution of the pending block. +func (b *pendingBlock) Events() []flowgo.Event { + return b.events +} + +// ExecutionStarted returns true if the pending block has started executing. +func (b *pendingBlock) ExecutionStarted() bool { + return b.index > 0 +} + +// ExecutionComplete returns true if the pending block is fully executed. +func (b *pendingBlock) ExecutionComplete() bool { + return b.index >= uint32(b.Size()) +} + +// Size returns the number of transactions in the pending block. +func (b *pendingBlock) Size() int { + return len(b.transactionIDs) +} + +// Empty returns true if the pending block is empty. +func (b *pendingBlock) Empty() bool { + return b.Size() == 0 +} + +func (b *pendingBlock) SetTimestamp(timestamp time.Time) { + b.timestamp = timestamp +} diff --git a/integration/internal/emulator/result.go b/integration/internal/emulator/result.go new file mode 100644 index 00000000000..a726a91d011 --- /dev/null +++ b/integration/internal/emulator/result.go @@ -0,0 +1,104 @@ +/* + * Flow Emulator + * + * Copyright Flow Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package emulator + +import ( + "fmt" + + "github.com/onflow/cadence" + + flowsdk "github.com/onflow/flow-go-sdk" + + flowgo "github.com/onflow/flow-go/model/flow" +) + +type StorableTransactionResult struct { + ErrorCode int + ErrorMessage string + Logs []string + Events []flowgo.Event + BlockID flowgo.Identifier + BlockHeight uint64 +} + +// A TransactionResult is the result of executing a transaction. +type TransactionResult struct { + TransactionID flowsdk.Identifier + ComputationUsed uint64 + MemoryEstimate uint64 + Error error + Logs []string + Events []flowsdk.Event + Debug *TransactionResultDebug +} + +// Succeeded returns true if the transaction executed without errors. +func (r TransactionResult) Succeeded() bool { + return r.Error == nil +} + +// Reverted returns true if the transaction executed with errors. +func (r TransactionResult) Reverted() bool { + return !r.Succeeded() +} + +// TransactionResultDebug provides details about unsuccessful transaction execution +type TransactionResultDebug struct { + Message string + Meta map[string]any +} + +// NewTransactionInvalidSignature creates more debug details for transactions with invalid signature +func NewTransactionInvalidSignature( + tx *flowgo.TransactionBody, +) *TransactionResultDebug { + return &TransactionResultDebug{ + Message: "", + Meta: map[string]any{ + "payer": tx.Payer.String(), + "proposer": tx.ProposalKey.Address.String(), + "proposerKeyIndex": fmt.Sprintf("%d", tx.ProposalKey.KeyIndex), + "authorizers": fmt.Sprintf("%v", tx.Authorizers), + "gasLimit": fmt.Sprintf("%d", tx.GasLimit), + }, + } +} + +// TODO - this class should be part of SDK for consistency + +// A ScriptResult is the result of executing a script. +type ScriptResult struct { + ScriptID flowgo.Identifier + Value cadence.Value + Error error + Logs []string + Events []flowgo.Event + ComputationUsed uint64 + MemoryEstimate uint64 +} + +// Succeeded returns true if the script executed without errors. +func (r ScriptResult) Succeeded() bool { + return r.Error == nil +} + +// Reverted returns true if the script executed with errors. +func (r ScriptResult) Reverted() bool { + return !r.Succeeded() +} diff --git a/integration/internal/emulator/sdk.go b/integration/internal/emulator/sdk.go new file mode 100644 index 00000000000..06b6085ec6a --- /dev/null +++ b/integration/internal/emulator/sdk.go @@ -0,0 +1,551 @@ +package emulator + +import ( + "context" + "fmt" + + "github.com/rs/zerolog" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "github.com/onflow/cadence" + jsoncdc "github.com/onflow/cadence/encoding/json" + "github.com/onflow/cadence/stdlib" + "github.com/onflow/flow/protobuf/go/flow/entities" + + sdk "github.com/onflow/flow-go-sdk" + "github.com/onflow/flow-go-sdk/templates" + + "github.com/onflow/flow-go/access" + flowgo "github.com/onflow/flow-go/model/flow" +) + +// SDKAdapter wraps an emulated emulator and implements the RPC handlers +// required by the Access API. +type SDKAdapter struct { + logger *zerolog.Logger + emulator Emulator +} + +func (b *SDKAdapter) EnableAutoMine() { + b.emulator.EnableAutoMine() +} +func (b *SDKAdapter) DisableAutoMine() { + b.emulator.DisableAutoMine() +} + +func (b *SDKAdapter) Emulator() Emulator { + return b.emulator +} + +// NewSDKAdapter returns a new SDKAdapter. +func NewSDKAdapter(logger *zerolog.Logger, emulator Emulator) *SDKAdapter { + return &SDKAdapter{ + logger: logger, + emulator: emulator, + } +} + +func (b *SDKAdapter) Ping(ctx context.Context) error { + return b.emulator.Ping() +} + +func (b *SDKAdapter) GetChainID(ctx context.Context) sdk.ChainID { + return sdk.ChainID(b.emulator.GetNetworkParameters().ChainID) +} + +// GetLatestBlockHeader gets the latest sealed block header. +func (b *SDKAdapter) GetLatestBlockHeader( + _ context.Context, + _ bool, +) ( + *sdk.BlockHeader, + sdk.BlockStatus, + error, +) { + block, err := b.emulator.GetLatestBlock() + if err != nil { + return nil, sdk.BlockStatusUnknown, status.Error(codes.Internal, err.Error()) + } + blockHeader := sdk.BlockHeader{ + ID: sdk.Identifier(block.ID()), + ParentID: sdk.Identifier(block.Header.ParentID), + Height: block.Header.Height, + Timestamp: block.Header.Timestamp, + } + return &blockHeader, sdk.BlockStatusSealed, nil +} + +// GetBlockHeaderByHeight gets a block header by height. +func (b *SDKAdapter) GetBlockHeaderByHeight( + _ context.Context, + height uint64, +) ( + *sdk.BlockHeader, + sdk.BlockStatus, + error, +) { + block, err := b.emulator.GetBlockByHeight(height) + if err != nil { + return nil, sdk.BlockStatusUnknown, status.Error(codes.Internal, err.Error()) + } + blockHeader := sdk.BlockHeader{ + ID: sdk.Identifier(block.ID()), + ParentID: sdk.Identifier(block.Header.ParentID), + Height: block.Header.Height, + Timestamp: block.Header.Timestamp, + } + return &blockHeader, sdk.BlockStatusSealed, nil +} + +// GetBlockHeaderByID gets a block header by ID. +func (b *SDKAdapter) GetBlockHeaderByID( + _ context.Context, + id sdk.Identifier, +) ( + *sdk.BlockHeader, + sdk.BlockStatus, + error, +) { + block, err := b.emulator.GetBlockByID(SDKIdentifierToFlow(id)) + if err != nil { + return nil, sdk.BlockStatusUnknown, err + } + blockHeader := sdk.BlockHeader{ + ID: sdk.Identifier(block.ID()), + ParentID: sdk.Identifier(block.Header.ParentID), + Height: block.Header.Height, + Timestamp: block.Header.Timestamp, + } + return &blockHeader, sdk.BlockStatusSealed, nil +} + +// GetLatestBlock gets the latest sealed block. +func (b *SDKAdapter) GetLatestBlock( + _ context.Context, + _ bool, +) ( + *sdk.Block, + sdk.BlockStatus, + error, +) { + flowBlock, err := b.emulator.GetLatestBlock() + if err != nil { + return nil, sdk.BlockStatusUnknown, status.Error(codes.Internal, err.Error()) + } + block := sdk.Block{ + BlockHeader: sdk.BlockHeader{ + ID: sdk.Identifier(flowBlock.ID()), + ParentID: sdk.Identifier(flowBlock.Header.ParentID), + Height: flowBlock.Header.Height, + Timestamp: flowBlock.Header.Timestamp, + }, + BlockPayload: convertBlockPayload(flowBlock.Payload), + } + return &block, sdk.BlockStatusSealed, nil +} + +// GetBlockByHeight gets a block by height. +func (b *SDKAdapter) GetBlockByHeight( + ctx context.Context, + height uint64, +) ( + *sdk.Block, + sdk.BlockStatus, + error, +) { + flowBlock, err := b.emulator.GetBlockByHeight(height) + if err != nil { + return nil, sdk.BlockStatusUnknown, status.Error(codes.Internal, err.Error()) + } + block := sdk.Block{ + BlockHeader: sdk.BlockHeader{ + ID: sdk.Identifier(flowBlock.ID()), + ParentID: sdk.Identifier(flowBlock.Header.ParentID), + Height: flowBlock.Header.Height, + Timestamp: flowBlock.Header.Timestamp, + }, + BlockPayload: convertBlockPayload(flowBlock.Payload), + } + return &block, sdk.BlockStatusSealed, nil +} + +// GetBlockByID gets a block by ID. +func (b *SDKAdapter) GetBlockByID( + _ context.Context, + id sdk.Identifier, +) ( + *sdk.Block, + sdk.BlockStatus, + error, +) { + flowBlock, err := b.emulator.GetBlockByID(SDKIdentifierToFlow(id)) + if err != nil { + return nil, sdk.BlockStatusUnknown, status.Error(codes.Internal, err.Error()) + } + block := sdk.Block{ + BlockHeader: sdk.BlockHeader{ + ID: sdk.Identifier(flowBlock.ID()), + ParentID: sdk.Identifier(flowBlock.Header.ParentID), + Height: flowBlock.Header.Height, + Timestamp: flowBlock.Header.Timestamp, + }, + BlockPayload: convertBlockPayload(flowBlock.Payload), + } + return &block, sdk.BlockStatusSealed, nil +} + +func convertBlockPayload(payload *flowgo.Payload) sdk.BlockPayload { + var seals []*sdk.BlockSeal + sealCount := len(payload.Seals) + if sealCount > 0 { + seals = make([]*sdk.BlockSeal, 0, sealCount) + for _, seal := range payload.Seals { + seals = append(seals, &sdk.BlockSeal{ + BlockID: sdk.Identifier(seal.BlockID), + ExecutionReceiptID: sdk.Identifier(seal.ResultID), + }) + } + } + + var collectionGuarantees []*sdk.CollectionGuarantee + guaranteesCount := len(payload.Guarantees) + if guaranteesCount > 0 { + collectionGuarantees = make([]*sdk.CollectionGuarantee, 0, guaranteesCount) + for _, guarantee := range payload.Guarantees { + collectionGuarantees = append(collectionGuarantees, &sdk.CollectionGuarantee{ + CollectionID: sdk.Identifier(guarantee.CollectionID), + }) + } + } + + return sdk.BlockPayload{ + Seals: seals, + CollectionGuarantees: collectionGuarantees, + } +} + +// GetCollectionByID gets a collection by ID. +func (b *SDKAdapter) GetCollectionByID( + _ context.Context, + id sdk.Identifier, +) (*sdk.Collection, error) { + flowCollection, err := b.emulator.GetCollectionByID(SDKIdentifierToFlow(id)) + if err != nil { + return nil, err + } + collection := FlowLightCollectionToSDK(*flowCollection) + return &collection, nil +} + +func (b *SDKAdapter) SendTransaction(ctx context.Context, tx sdk.Transaction) error { + flowTx := SDKTransactionToFlow(tx) + return b.emulator.SendTransaction(flowTx) +} + +// GetTransaction gets a transaction by ID. +func (b *SDKAdapter) GetTransaction( + ctx context.Context, + id sdk.Identifier, +) (*sdk.Transaction, error) { + tx, err := b.emulator.GetTransaction(SDKIdentifierToFlow(id)) + if err != nil { + return nil, err + } + sdkTx := FlowTransactionToSDK(*tx) + return &sdkTx, nil +} + +// GetTransactionResult gets a transaction by ID. +func (b *SDKAdapter) GetTransactionResult( + ctx context.Context, + id sdk.Identifier, +) (*sdk.TransactionResult, error) { + flowResult, err := b.emulator.GetTransactionResult(SDKIdentifierToFlow(id)) + if err != nil { + return nil, err + } + return FlowTransactionResultToSDK(flowResult) +} + +// GetAccount returns an account by address at the latest sealed block. +func (b *SDKAdapter) GetAccount( + ctx context.Context, + address sdk.Address, +) (*sdk.Account, error) { + account, err := b.getAccount(address) + if err != nil { + return nil, err + } + return account, nil +} + +// GetAccountAtLatestBlock returns an account by address at the latest sealed block. +func (b *SDKAdapter) GetAccountAtLatestBlock( + ctx context.Context, + address sdk.Address, +) (*sdk.Account, error) { + account, err := b.getAccount(address) + if err != nil { + return nil, err + } + return account, nil +} + +func (b *SDKAdapter) getAccount(address sdk.Address) (*sdk.Account, error) { + account, err := b.emulator.GetAccount(SDKAddressToFlow(address)) + if err != nil { + return nil, err + } + return FlowAccountToSDK(*account) +} + +func (b *SDKAdapter) GetAccountAtBlockHeight( + ctx context.Context, + address sdk.Address, + height uint64, +) (*sdk.Account, error) { + account, err := b.emulator.GetAccountAtBlockHeight(SDKAddressToFlow(address), height) + if err != nil { + return nil, err + } + return FlowAccountToSDK(*account) +} + +// ExecuteScriptAtLatestBlock executes a script at a the latest block +func (b *SDKAdapter) ExecuteScriptAtLatestBlock( + ctx context.Context, + script []byte, + arguments [][]byte, +) ([]byte, error) { + block, err := b.emulator.GetLatestBlock() + if err != nil { + return nil, err + } + return b.executeScriptAtBlock(script, arguments, block.Header.Height) +} + +// ExecuteScriptAtBlockHeight executes a script at a specific block height +func (b *SDKAdapter) ExecuteScriptAtBlockHeight( + ctx context.Context, + blockHeight uint64, + script []byte, + arguments [][]byte, +) ([]byte, error) { + return b.executeScriptAtBlock(script, arguments, blockHeight) +} + +// ExecuteScriptAtBlockID executes a script at a specific block ID +func (b *SDKAdapter) ExecuteScriptAtBlockID( + ctx context.Context, + blockID sdk.Identifier, + script []byte, + arguments [][]byte, +) ([]byte, error) { + block, err := b.emulator.GetBlockByID(SDKIdentifierToFlow(blockID)) + if err != nil { + return nil, err + } + return b.executeScriptAtBlock(script, arguments, block.Header.Height) +} + +// executeScriptAtBlock is a helper for executing a script at a specific block +func (b *SDKAdapter) executeScriptAtBlock(script []byte, arguments [][]byte, blockHeight uint64) ([]byte, error) { + result, err := b.emulator.ExecuteScriptAtBlockHeight(script, arguments, blockHeight) + if err != nil { + return nil, err + } + if !result.Succeeded() { + return nil, result.Error + } + valueBytes, err := jsoncdc.Encode(result.Value) + if err != nil { + return nil, err + } + return valueBytes, nil +} + +func (b *SDKAdapter) GetLatestProtocolStateSnapshot(_ context.Context) ([]byte, error) { + return nil, nil +} + +func (a *SDKAdapter) GetProtocolStateSnapshotByBlockID(_ context.Context, _ flowgo.Identifier) ([]byte, error) { + return nil, nil +} + +func (a *SDKAdapter) GetProtocolStateSnapshotByHeight(_ context.Context, _ uint64) ([]byte, error) { + return nil, nil +} + +func (b *SDKAdapter) GetExecutionResultForBlockID(_ context.Context, _ sdk.Identifier) (*sdk.ExecutionResult, error) { + return nil, nil +} + +func (b *SDKAdapter) GetSystemTransaction(_ context.Context, _ flowgo.Identifier) (*flowgo.TransactionBody, error) { + return nil, nil +} + +func (b *SDKAdapter) GetSystemTransactionResult(_ context.Context, _ flowgo.Identifier, _ entities.EventEncodingVersion) (*access.TransactionResult, error) { + return nil, nil +} + +func (b *SDKAdapter) GetTransactionsByBlockID(ctx context.Context, id sdk.Identifier) ([]*sdk.Transaction, error) { + result := []*sdk.Transaction{} + transactions, err := b.emulator.GetTransactionsByBlockID(SDKIdentifierToFlow(id)) + if err != nil { + return nil, err + } + for _, transaction := range transactions { + sdkTransaction := FlowTransactionToSDK(*transaction) + result = append(result, &sdkTransaction) + + } + return result, nil +} + +func (b *SDKAdapter) GetTransactionResultsByBlockID(ctx context.Context, id sdk.Identifier) ([]*sdk.TransactionResult, error) { + result := []*sdk.TransactionResult{} + transactionResults, err := b.emulator.GetTransactionResultsByBlockID(SDKIdentifierToFlow(id)) + if err != nil { + return nil, err + } + for _, transactionResult := range transactionResults { + sdkResult, err := FlowTransactionResultToSDK(transactionResult) + if err != nil { + return nil, err + } + result = append(result, sdkResult) + } + return result, nil +} + +func (b *SDKAdapter) GetEventsForBlockIDs(ctx context.Context, eventType string, blockIDs []sdk.Identifier) ([]*sdk.BlockEvents, error) { + result := []*sdk.BlockEvents{} + flowBlockEvents, err := b.emulator.GetEventsForBlockIDs(eventType, SDKIdentifiersToFlow(blockIDs)) + if err != nil { + return nil, err + } + + for _, flowBlockEvent := range flowBlockEvents { + sdkEvents, err := FlowEventsToSDK(flowBlockEvent.Events) + if err != nil { + return nil, err + } + + sdkBlockEvents := &sdk.BlockEvents{ + BlockID: sdk.Identifier(flowBlockEvent.BlockID), + Height: flowBlockEvent.BlockHeight, + BlockTimestamp: flowBlockEvent.BlockTimestamp, + Events: sdkEvents, + } + + result = append(result, sdkBlockEvents) + + } + + return result, nil +} + +func (b *SDKAdapter) GetEventsForHeightRange(ctx context.Context, eventType string, startHeight, endHeight uint64) ([]*sdk.BlockEvents, error) { + result := []*sdk.BlockEvents{} + + flowBlockEvents, err := b.emulator.GetEventsForHeightRange(eventType, startHeight, endHeight) + if err != nil { + return nil, err + } + + for _, flowBlockEvent := range flowBlockEvents { + sdkEvents, err := FlowEventsToSDK(flowBlockEvent.Events) + + if err != nil { + return nil, err + } + + sdkBlockEvents := &sdk.BlockEvents{ + BlockID: sdk.Identifier(flowBlockEvent.BlockID), + Height: flowBlockEvent.BlockHeight, + BlockTimestamp: flowBlockEvent.BlockTimestamp, + Events: sdkEvents, + } + + result = append(result, sdkBlockEvents) + + } + + return result, nil +} + +// CreateAccount submits a transaction to create a new account with the given +// account keys and contracts. The transaction is paid by the service account. +func (b *SDKAdapter) CreateAccount(ctx context.Context, publicKeys []*sdk.AccountKey, contracts []templates.Contract) (sdk.Address, error) { + + serviceKey := b.emulator.ServiceKey() + latestBlock, err := b.emulator.GetLatestBlock() + serviceAddress := FlowAddressToSDK(serviceKey.Address) + + if err != nil { + return sdk.Address{}, err + } + + if publicKeys == nil { + publicKeys = []*sdk.AccountKey{} + } + tx, err := templates.CreateAccount(publicKeys, contracts, serviceAddress) + if err != nil { + return sdk.Address{}, err + } + + tx.SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetReferenceBlockID(sdk.Identifier(latestBlock.ID())). + SetProposalKey(serviceAddress, serviceKey.Index, serviceKey.SequenceNumber). + SetPayer(serviceAddress) + + signer, err := serviceKey.Signer() + if err != nil { + return sdk.Address{}, err + } + + err = tx.SignEnvelope(serviceAddress, serviceKey.Index, signer) + if err != nil { + return sdk.Address{}, err + } + + err = b.SendTransaction(ctx, *tx) + if err != nil { + return sdk.Address{}, err + } + + _, results, err := b.emulator.ExecuteAndCommitBlock() + if err != nil { + return sdk.Address{}, err + } + lastResult := results[len(results)-1] + + _, err = b.emulator.CommitBlock() + if err != nil { + return sdk.Address{}, err + } + + if !lastResult.Succeeded() { + return sdk.Address{}, lastResult.Error + } + + var address sdk.Address + + for _, event := range lastResult.Events { + if event.Type == sdk.EventAccountCreated { + addressFieldValue := cadence.SearchFieldByName( + event.Value, + stdlib.AccountEventAddressParameter.Identifier, + ) + address = sdk.Address(addressFieldValue.(cadence.Address)) + break + } + } + + if address == (sdk.Address{}) { + return sdk.Address{}, fmt.Errorf("failed to find AccountCreated event") + } + + return address, nil +} diff --git a/integration/internal/emulator/store.go b/integration/internal/emulator/store.go new file mode 100644 index 00000000000..2035268a488 --- /dev/null +++ b/integration/internal/emulator/store.go @@ -0,0 +1,98 @@ +/* + * Flow Emulator + * + * Copyright Flow Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Package storage defines the interface and implementations for interacting with +// persistent chain state. +package emulator + +import ( + "context" + + "github.com/psiemens/graceland" + + "github.com/onflow/flow-go/access" + "github.com/onflow/flow-go/fvm/environment" + "github.com/onflow/flow-go/fvm/storage/snapshot" + flowgo "github.com/onflow/flow-go/model/flow" +) + +// EmulatorStorage defines the storage layer for persistent chain state. +// +// This includes finalized blocks and transactions, and the resultant register +// states and emitted events. It does not include pending state, such as pending +// transactions and register states. +// +// Implementations must distinguish between not found errors and errors with +// the underlying storage by returning an instance of store.ErrNotFound if a +// resource cannot be found. +// +// Implementations must be safe for use by multiple goroutines. + +type EmulatorStorage interface { + graceland.Routine + environment.Blocks + access.Blocks + LatestBlockHeight(ctx context.Context) (uint64, error) + + // LatestBlock returns the block with the highest block height. + LatestBlock(ctx context.Context) (flowgo.Block, error) + + // StoreBlock stores the block in storage. If the exactly same block is already in a storage, return successfully + StoreBlock(ctx context.Context, block *flowgo.Block) error + + // BlockByID returns the block with the given hash. It is available for + // finalized and ambiguous blocks. + BlockByID(ctx context.Context, blockID flowgo.Identifier) (*flowgo.Block, error) + + // BlockByHeight returns the block at the given height. It is only available + // for finalized blocks. + BlockByHeight(ctx context.Context, height uint64) (*flowgo.Block, error) + + // CommitBlock atomically saves the execution results for a block. + CommitBlock( + ctx context.Context, + block flowgo.Block, + collections []*flowgo.LightCollection, + transactions map[flowgo.Identifier]*flowgo.TransactionBody, + transactionResults map[flowgo.Identifier]*StorableTransactionResult, + executionSnapshot *snapshot.ExecutionSnapshot, + events []flowgo.Event, + ) error + + // CollectionByID gets the collection (transaction IDs only) with the given ID. + CollectionByID(ctx context.Context, collectionID flowgo.Identifier) (flowgo.LightCollection, error) + + // FullCollectionByID gets the full collection (including transaction bodies) with the given ID. + FullCollectionByID(ctx context.Context, collectionID flowgo.Identifier) (flowgo.Collection, error) + + // TransactionByID gets the transaction with the given ID. + TransactionByID(ctx context.Context, transactionID flowgo.Identifier) (flowgo.TransactionBody, error) + + // TransactionResultByID gets the transaction result with the given ID. + TransactionResultByID(ctx context.Context, transactionID flowgo.Identifier) (StorableTransactionResult, error) + + // LedgerByHeight returns a storage snapshot into the ledger state + // at a given block. + LedgerByHeight( + ctx context.Context, + blockHeight uint64, + ) (snapshot.StorageSnapshot, error) + + // EventsByHeight returns the events in the block at the given height, optionally filtered by type. + EventsByHeight(ctx context.Context, blockHeight uint64, eventType string) ([]flowgo.Event, error) +} diff --git a/integration/internal/emulator/templates/systemChunkTransactionTemplate.cdc b/integration/internal/emulator/templates/systemChunkTransactionTemplate.cdc new file mode 100644 index 00000000000..ef714ed20ba --- /dev/null +++ b/integration/internal/emulator/templates/systemChunkTransactionTemplate.cdc @@ -0,0 +1,16 @@ +import RandomBeaconHistory from "RandomBeaconHistory" +import EVM from "EVM" + +transaction { + prepare(serviceAccount: auth(BorrowValue) &Account) { + let randomBeaconHistoryHeartbeat = serviceAccount.storage + .borrow<&RandomBeaconHistory.Heartbeat>(from: RandomBeaconHistory.HeartbeatStoragePath) + ?? panic("Couldn't borrow RandomBeaconHistory.Heartbeat Resource") + randomBeaconHistoryHeartbeat.heartbeat(randomSourceHistory: randomSourceHistory()) + + let evmHeartbeat = serviceAccount.storage + .borrow<&EVM.Heartbeat>(from: /storage/EVMHeartbeat) + ?? panic("Couldn't borrow EVM.Heartbeat Resource") + evmHeartbeat.heartbeat() + } +} diff --git a/integration/internal/emulator/tests/accounts_test.go b/integration/internal/emulator/tests/accounts_test.go new file mode 100644 index 00000000000..8bf3680db48 --- /dev/null +++ b/integration/internal/emulator/tests/accounts_test.go @@ -0,0 +1,1218 @@ +/* + * Flow Emulator + * + * Copyright Flow Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tests + +import ( + "context" + "fmt" + "testing" + + "github.com/rs/zerolog" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + flowsdk "github.com/onflow/flow-go-sdk" + "github.com/onflow/flow-go-sdk/crypto" + "github.com/onflow/flow-go-sdk/templates" + "github.com/onflow/flow-go-sdk/test" + + fvmerrors "github.com/onflow/flow-go/fvm/errors" + emulator "github.com/onflow/flow-go/integration/internal/emulator" + flowgo "github.com/onflow/flow-go/model/flow" +) + +const testContract = "access(all) contract Test {}" + +func setupAccountTests(t *testing.T, opts ...emulator.Option) ( + *emulator.Blockchain, + *emulator.SDKAdapter, +) { + b, err := emulator.New( + opts..., + ) + require.NoError(t, err) + + logger := zerolog.Nop() + return b, emulator.NewSDKAdapter(&logger, b) +} + +func TestGetAccount(t *testing.T) { + + t.Parallel() + + t.Run("Get account at latest block height", func(t *testing.T) { + + t.Parallel() + b, adapter := setupAccountTests(t) + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + acc, err := adapter.GetAccount(context.Background(), serviceAccountAddress) + assert.NoError(t, err) + + assert.Equal(t, uint64(0), acc.Keys[0].SequenceNumber) + + }) + + t.Run("Get account at latest block by index", func(t *testing.T) { + + t.Parallel() + b, adapter := setupAccountTests(t) + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + acc, err := adapter.GetAccount(context.Background(), serviceAccountAddress) + assert.NoError(t, err) + + assert.Equal(t, uint64(0), acc.Keys[0].SequenceNumber) + + flowAccount, err := b.GetAccountByIndex(1) //service account + assert.NoError(t, err) + + assert.Equal(t, uint64(0), flowAccount.Keys[0].SeqNumber) + assert.Equal(t, acc.Address.String(), flowAccount.Address.String()) + + }) + + t.Run("Get account at latest block height", func(t *testing.T) { + + t.Parallel() + b, adapter := setupAccountTests(t) + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + acc, err := adapter.GetAccount(context.Background(), serviceAccountAddress) + assert.NoError(t, err) + + assert.Equal(t, uint64(0), acc.Keys[0].SequenceNumber) + + flowAccount, err := b.GetAccountByIndex(1) //service account + assert.NoError(t, err) + + assert.Equal(t, uint64(0), flowAccount.Keys[0].SeqNumber) + assert.Equal(t, acc.Address.String(), flowAccount.Address.String()) + + }) + + t.Run("Get account at specified block height", func(t *testing.T) { + + t.Parallel() + + b, adapter := setupAccountTests(t) + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + acc, err := adapter.GetAccount(context.Background(), serviceAccountAddress) + assert.NoError(t, err) + + assert.Equal(t, uint64(0), acc.Keys[0].SequenceNumber) + contract := templates.Contract{ + Name: "Test", + Source: testContract, + } + + tx := templates.AddAccountContract(serviceAccountAddress, contract) + + tx.SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress) + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + err = adapter.SendTransaction(context.Background(), *tx) + require.NoError(t, err) + + result, err := b.ExecuteNextTransaction() + assert.NoError(t, err) + AssertTransactionSucceeded(t, result) + + bl, err := b.CommitBlock() + assert.NoError(t, err) + + accNow, err := adapter.GetAccountAtBlockHeight(context.Background(), serviceAccountAddress, bl.Header.Height) + assert.NoError(t, err) + + accPrev, err := adapter.GetAccountAtBlockHeight(context.Background(), serviceAccountAddress, bl.Header.Height-uint64(1)) + assert.NoError(t, err) + + assert.Equal(t, accNow.Keys[0].SequenceNumber, uint64(1)) + assert.Equal(t, accPrev.Keys[0].SequenceNumber, uint64(0)) + }) +} + +func TestCreateAccount(t *testing.T) { + + t.Parallel() + + accountKeys := test.AccountKeyGenerator() + + t.Run("Simple addresses", func(t *testing.T) { + b, adapter := setupAccountTests( + t, + emulator.WithSimpleAddresses(), + ) + + accountKey := accountKeys.New() + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + tx, err := templates.CreateAccount( + []*flowsdk.AccountKey{accountKey}, + nil, + serviceAccountAddress, + ) + assert.NoError(t, err) + + tx.SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress) + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + err = adapter.SendTransaction(context.Background(), *tx) + require.NoError(t, err) + + result, err := b.ExecuteNextTransaction() + assert.NoError(t, err) + AssertTransactionSucceeded(t, result) + + _, err = b.CommitBlock() + assert.NoError(t, err) + + account, err := LastCreatedAccount(b, result) + require.NoError(t, err) + + assert.Equal(t, "0000000000000006", account.Address.Hex()) + assert.Equal(t, uint64(0x186a0), account.Balance) + require.Len(t, account.Keys, 1) + assert.Equal(t, accountKey.PublicKey.Encode(), account.Keys[0].PublicKey.Encode()) + assert.Empty(t, account.Contracts) + }) + + t.Run("Single public keys", func(t *testing.T) { + b, adapter := setupAccountTests(t) + + accountKey := accountKeys.New() + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + tx, err := templates.CreateAccount( + []*flowsdk.AccountKey{accountKey}, + nil, + serviceAccountAddress, + ) + assert.NoError(t, err) + + tx.SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress) + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + err = adapter.SendTransaction(context.Background(), *tx) + require.NoError(t, err) + + result, err := b.ExecuteNextTransaction() + assert.NoError(t, err) + AssertTransactionSucceeded(t, result) + + _, err = b.CommitBlock() + assert.NoError(t, err) + + account, err := LastCreatedAccount(b, result) + require.NoError(t, err) + + require.Len(t, account.Keys, 1) + assert.Equal(t, accountKey.PublicKey.Encode(), account.Keys[0].PublicKey.Encode()) + assert.Empty(t, account.Contracts) + }) + + t.Run("Multiple public keys", func(t *testing.T) { + b, adapter := setupAccountTests(t) + + accountKeyA := accountKeys.New() + accountKeyB := accountKeys.New() + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + tx, err := templates.CreateAccount( + []*flowsdk.AccountKey{accountKeyA, accountKeyB}, + nil, + serviceAccountAddress, + ) + assert.NoError(t, err) + + tx.SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress) + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + err = adapter.SendTransaction(context.Background(), *tx) + require.NoError(t, err) + + result, err := b.ExecuteNextTransaction() + require.NoError(t, err) + AssertTransactionSucceeded(t, result) + + _, err = b.CommitBlock() + assert.NoError(t, err) + + account, err := LastCreatedAccount(b, result) + require.NoError(t, err) + + require.Len(t, account.Keys, 2) + assert.Equal(t, accountKeyA.PublicKey.Encode(), account.Keys[0].PublicKey.Encode()) + assert.Equal(t, accountKeyB.PublicKey.Encode(), account.Keys[1].PublicKey.Encode()) + assert.Empty(t, account.Contracts) + }) + + t.Run("Public keys and contract", func(t *testing.T) { + b, adapter := setupAccountTests(t) + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + accountKeyA := accountKeys.New() + accountKeyB := accountKeys.New() + + contracts := []templates.Contract{ + { + Name: "Test", + Source: testContract, + }, + } + + tx, err := templates.CreateAccount( + []*flowsdk.AccountKey{accountKeyA, accountKeyB}, + contracts, + serviceAccountAddress, + ) + assert.NoError(t, err) + + tx.SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress) + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + err = adapter.SendTransaction(context.Background(), *tx) + require.NoError(t, err) + + result, err := b.ExecuteNextTransaction() + require.NoError(t, err) + AssertTransactionSucceeded(t, result) + + _, err = b.CommitBlock() + assert.NoError(t, err) + + account, err := LastCreatedAccount(b, result) + require.NoError(t, err) + + require.Len(t, account.Keys, 2) + assert.Equal(t, accountKeyA.PublicKey.Encode(), account.Keys[0].PublicKey.Encode()) + assert.Equal(t, accountKeyB.PublicKey.Encode(), account.Keys[1].PublicKey.Encode()) + assert.Equal(t, + map[string][]byte{ + "Test": []byte(testContract), + }, + account.Contracts, + ) + }) + + t.Run("Public keys and two contracts", func(t *testing.T) { + b, adapter := setupAccountTests(t) + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + codeA := ` + access(all) contract Test1 { + access(all) fun a(): Int { + return 1 + } + } + ` + codeB := ` + access(all) contract Test2 { + access(all) fun b(): Int { + return 2 + } + } + ` + + accountKey := accountKeys.New() + + contracts := []templates.Contract{ + { + Name: "Test1", + Source: codeA, + }, + { + Name: "Test2", + Source: codeB, + }, + } + + tx, err := templates.CreateAccount( + []*flowsdk.AccountKey{accountKey}, + contracts, + serviceAccountAddress, + ) + assert.NoError(t, err) + + tx.SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress) + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + err = adapter.SendTransaction(context.Background(), *tx) + require.NoError(t, err) + + result, err := b.ExecuteNextTransaction() + require.NoError(t, err) + AssertTransactionSucceeded(t, result) + + _, err = b.CommitBlock() + assert.NoError(t, err) + + account, err := LastCreatedAccount(b, result) + require.NoError(t, err) + + require.Len(t, account.Keys, 1) + assert.Equal(t, accountKey.PublicKey.Encode(), account.Keys[0].PublicKey.Encode()) + assert.Equal(t, + map[string][]byte{ + "Test1": []byte(codeA), + "Test2": []byte(codeB), + }, + account.Contracts, + ) + }) + + t.Run("Code and no keys", func(t *testing.T) { + b, adapter := setupAccountTests(t) + + contracts := []templates.Contract{ + { + Name: "Test", + Source: testContract, + }, + } + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + tx, err := templates.CreateAccount( + nil, + contracts, + serviceAccountAddress, + ) + assert.NoError(t, err) + + tx.SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress) + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + err = adapter.SendTransaction(context.Background(), *tx) + assert.NoError(t, err) + + result, err := b.ExecuteNextTransaction() + require.NoError(t, err) + AssertTransactionSucceeded(t, result) + + _, err = b.CommitBlock() + assert.NoError(t, err) + + account, err := LastCreatedAccount(b, result) + require.NoError(t, err) + + assert.Empty(t, account.Keys) + assert.Equal(t, + map[string][]byte{ + "Test": []byte(testContract), + }, + account.Contracts, + ) + }) + + t.Run("Event emitted", func(t *testing.T) { + b, adapter := setupAccountTests(t) + + accountKey := accountKeys.New() + + contracts := []templates.Contract{ + { + Name: "Test", + Source: testContract, + }, + } + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + tx, err := templates.CreateAccount( + []*flowsdk.AccountKey{accountKey}, + contracts, + serviceAccountAddress, + ) + assert.NoError(t, err) + + tx.SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress) + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + err = adapter.SendTransaction(context.Background(), *tx) + assert.NoError(t, err) + + result, err := b.ExecuteNextTransaction() + require.NoError(t, err) + AssertTransactionSucceeded(t, result) + + block, err := b.CommitBlock() + require.NoError(t, err) + + events, err := adapter.GetEventsForHeightRange(context.Background(), flowsdk.EventAccountCreated, block.Header.Height, block.Header.Height) + require.NoError(t, err) + require.Len(t, events, 1) + + accountEvent := flowsdk.AccountCreatedEvent(events[0].Events[0]) + + account, err := adapter.GetAccount(context.Background(), accountEvent.Address()) + assert.NoError(t, err) + + require.Len(t, account.Keys, 1) + assert.Equal(t, accountKey.PublicKey, account.Keys[0].PublicKey) + assert.Equal(t, + map[string][]byte{ + "Test": []byte(testContract), + }, + account.Contracts, + ) + }) + + t.Run("Invalid hash algorithm", func(t *testing.T) { + b, adapter := setupAccountTests(t) + + accountKey := accountKeys.New() + accountKey.SetHashAlgo(crypto.SHA3_384) // SHA3_384 is invalid for ECDSA_P256 + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + tx, err := templates.CreateAccount( + []*flowsdk.AccountKey{accountKey}, + nil, + serviceAccountAddress, + ) + assert.NoError(t, err) + + tx.SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress) + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + err = adapter.SendTransaction(context.Background(), *tx) + assert.NoError(t, err) + + result, err := b.ExecuteNextTransaction() + require.NoError(t, err) + + assert.True(t, result.Reverted()) + }) + + t.Run("Invalid code", func(t *testing.T) { + b, adapter := setupAccountTests(t) + + contracts := []templates.Contract{ + { + Name: "Test", + Source: "not a valid script", + }, + } + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + tx, err := templates.CreateAccount( + nil, + contracts, + serviceAccountAddress, + ) + assert.NoError(t, err) + + tx.SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress) + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + err = adapter.SendTransaction(context.Background(), *tx) + assert.NoError(t, err) + + result, err := b.ExecuteNextTransaction() + require.NoError(t, err) + + assert.True(t, result.Reverted()) + }) + + t.Run("Invalid contract name", func(t *testing.T) { + b, adapter := setupAccountTests(t) + + contracts := []templates.Contract{ + { + Name: "Test2", + Source: testContract, + }, + } + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + tx, err := templates.CreateAccount( + nil, + contracts, + serviceAccountAddress, + ) + assert.NoError(t, err) + + tx.SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress) + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + err = adapter.SendTransaction(context.Background(), *tx) + assert.NoError(t, err) + + result, err := b.ExecuteNextTransaction() + require.NoError(t, err) + + assert.True(t, result.Reverted()) + }) +} + +func TestAddAccountKey(t *testing.T) { + + t.Parallel() + + accountKeys := test.AccountKeyGenerator() + + t.Run("Valid key", func(t *testing.T) { + b, adapter := setupAccountTests(t) + + newAccountKey, newSigner := accountKeys.NewWithSigner() + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + tx1, err := templates.AddAccountKey(serviceAccountAddress, newAccountKey) + assert.NoError(t, err) + + tx1.SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress) + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx1.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + err = adapter.SendTransaction(context.Background(), *tx1) + assert.NoError(t, err) + + result, err := b.ExecuteNextTransaction() + assert.NoError(t, err) + AssertTransactionSucceeded(t, result) + + _, err = b.CommitBlock() + assert.NoError(t, err) + + script := []byte("transaction { execute {} }") + + var newKeyID = uint32(1) // new key will have ID 1 + var newKeySequenceNum uint64 = 0 + + tx2 := flowsdk.NewTransaction(). + SetScript(script). + SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, newKeyID, newKeySequenceNum). + SetPayer(serviceAccountAddress) + + err = tx2.SignEnvelope(serviceAccountAddress, newKeyID, newSigner) + assert.NoError(t, err) + + err = adapter.SendTransaction(context.Background(), *tx2) + require.NoError(t, err) + + result, err = b.ExecuteNextTransaction() + require.NoError(t, err) + AssertTransactionSucceeded(t, result) + + _, err = b.CommitBlock() + assert.NoError(t, err) + }) + + t.Run("Invalid hash algorithm", func(t *testing.T) { + b, adapter := setupAccountTests(t) + + accountKey := accountKeys.New() + accountKey.SetHashAlgo(crypto.SHA3_384) // SHA3_384 is invalid for ECDSA_P256 + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + tx, err := templates.AddAccountKey(serviceAccountAddress, accountKey) + assert.NoError(t, err) + + tx.SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress) + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + err = adapter.SendTransaction(context.Background(), *tx) + assert.NoError(t, err) + + result, err := b.ExecuteNextTransaction() + assert.NoError(t, err) + assert.True(t, result.Reverted()) + }) +} + +func TestRemoveAccountKey(t *testing.T) { + + t.Parallel() + + b, adapter := setupAccountTests(t) + + accountKeys := test.AccountKeyGenerator() + + newAccountKey, newSigner := accountKeys.NewWithSigner() + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + // create transaction that adds public key to account keys + tx1, err := templates.AddAccountKey(serviceAccountAddress, newAccountKey) + assert.NoError(t, err) + + // create transaction that adds public key to account keys + tx1.SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress) + + // sign with service key + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx1.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + // submit tx1 (should succeed) + err = adapter.SendTransaction(context.Background(), *tx1) + assert.NoError(t, err) + + result, err := b.ExecuteNextTransaction() + assert.NoError(t, err) + AssertTransactionSucceeded(t, result) + + _, err = b.CommitBlock() + assert.NoError(t, err) + + account, err := adapter.GetAccount(context.Background(), serviceAccountAddress) + assert.NoError(t, err) + + require.Len(t, account.Keys, 2) + assert.False(t, account.Keys[0].Revoked) + assert.False(t, account.Keys[1].Revoked) + + // create transaction that removes service key + tx2 := templates.RemoveAccountKey(serviceAccountAddress, 0) + + tx2.SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress) + + // sign with service key + signer, err = b.ServiceKey().Signer() + assert.NoError(t, err) + err = tx2.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + assert.NoError(t, err) + + // submit tx2 (should succeed) + err = adapter.SendTransaction(context.Background(), *tx2) + assert.NoError(t, err) + + result, err = b.ExecuteNextTransaction() + assert.NoError(t, err) + AssertTransactionSucceeded(t, result) + + _, err = b.CommitBlock() + assert.NoError(t, err) + + account, err = adapter.GetAccount(context.Background(), serviceAccountAddress) + assert.NoError(t, err) + + // key at index 0 should be revoked + require.Len(t, account.Keys, 2) + assert.True(t, account.Keys[0].Revoked) + assert.False(t, account.Keys[1].Revoked) + + // create transaction that removes remaining account key + tx3 := templates.RemoveAccountKey(serviceAccountAddress, 0) + + tx3.SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress) + + // sign with service key (that has been removed) + signer, err = b.ServiceKey().Signer() + assert.NoError(t, err) + err = tx3.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + assert.NoError(t, err) + + // submit tx3 (should fail) + err = adapter.SendTransaction(context.Background(), *tx3) + assert.NoError(t, err) + + result, err = b.ExecuteNextTransaction() + assert.NoError(t, err) + + var sigErr fvmerrors.CodedError + assert.ErrorAs(t, result.Error, &sigErr) + assert.True(t, fvmerrors.HasErrorCode(result.Error, fvmerrors.ErrCodeInvalidProposalSignatureError)) + + _, err = b.CommitBlock() + assert.NoError(t, err) + + account, err = adapter.GetAccount(context.Background(), serviceAccountAddress) + assert.NoError(t, err) + + // key at index 1 should not be revoked + require.Len(t, account.Keys, 2) + assert.True(t, account.Keys[0].Revoked) + assert.False(t, account.Keys[1].Revoked) + + // create transaction that removes remaining account key + tx4 := templates.RemoveAccountKey(serviceAccountAddress, 1) + + tx4.SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, account.Keys[1].Index, account.Keys[1].SequenceNumber). + SetPayer(serviceAccountAddress) + + // sign with remaining account key + err = tx4.SignEnvelope(serviceAccountAddress, account.Keys[1].Index, newSigner) + assert.NoError(t, err) + + // submit tx4 (should succeed) + err = adapter.SendTransaction(context.Background(), *tx4) + assert.NoError(t, err) + + result, err = b.ExecuteNextTransaction() + assert.NoError(t, err) + AssertTransactionSucceeded(t, result) + + _, err = b.CommitBlock() + assert.NoError(t, err) + + account, err = adapter.GetAccount(context.Background(), serviceAccountAddress) + assert.NoError(t, err) + + // all keys should be revoked + for _, key := range account.Keys { + assert.True(t, key.Revoked) + } +} + +func TestUpdateAccountCode(t *testing.T) { + + t.Parallel() + + const codeA = ` + access(all) contract Test { + access(all) fun a(): Int { + return 1 + } + } + ` + + const codeB = ` + access(all) contract Test { + access(all) fun b(): Int { + return 2 + } + } + ` + + accountKeys := test.AccountKeyGenerator() + + accountKeyB, signerB := accountKeys.NewWithSigner() + + t.Run("Valid signature", func(t *testing.T) { + b, adapter := setupAccountTests(t) + + contracts := []templates.Contract{ + { + Name: "Test", + Source: codeA, + }, + } + + accountAddressB, err := adapter.CreateAccount( + context.Background(), + []*flowsdk.AccountKey{accountKeyB}, + contracts, + ) + require.NoError(t, err) + + account, err := adapter.GetAccount(context.Background(), accountAddressB) + require.NoError(t, err) + + assert.Equal(t, + map[string][]byte{ + "Test": []byte(codeA), + }, + account.Contracts, + ) + + tx := templates.UpdateAccountContract( + accountAddressB, + templates.Contract{ + Name: "Test", + Source: codeB, + }, + ) + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + tx.SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress) + + err = tx.SignPayload(accountAddressB, 0, signerB) + assert.NoError(t, err) + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + err = adapter.SendTransaction(context.Background(), *tx) + require.NoError(t, err) + + result, err := b.ExecuteNextTransaction() + require.NoError(t, err) + AssertTransactionSucceeded(t, result) + + _, err = b.CommitBlock() + assert.NoError(t, err) + + account, err = adapter.GetAccount(context.Background(), accountAddressB) + assert.NoError(t, err) + + assert.Equal(t, codeB, string(account.Contracts["Test"])) + }) + + t.Run("Invalid signature", func(t *testing.T) { + b, adapter := setupAccountTests(t) + + contracts := []templates.Contract{ + { + Name: "Test", + Source: codeA, + }, + } + + accountAddressB, err := adapter.CreateAccount( + context.Background(), + []*flowsdk.AccountKey{accountKeyB}, + contracts, + ) + require.NoError(t, err) + + account, err := adapter.GetAccount(context.Background(), accountAddressB) + require.NoError(t, err) + + assert.Equal(t, codeA, string(account.Contracts["Test"])) + + tx := templates.UpdateAccountContract( + accountAddressB, + templates.Contract{ + Name: "Test", + Source: codeB, + }, + ) + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + tx.SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress) + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + err = adapter.SendTransaction(context.Background(), *tx) + assert.NoError(t, err) + + result, err := b.ExecuteNextTransaction() + assert.NoError(t, err) + + assert.True(t, fvmerrors.HasErrorCode(result.Error, fvmerrors.ErrCodeAccountAuthorizationError)) + + _, err = b.CommitBlock() + assert.NoError(t, err) + + account, err = adapter.GetAccount(context.Background(), accountAddressB) + assert.NoError(t, err) + + // code should not be updated + assert.Equal(t, codeA, string(account.Contracts["Test"])) + }) +} + +func TestImportAccountCode(t *testing.T) { + + t.Parallel() + + b, adapter := setupAccountTests(t) + + accountContracts := []templates.Contract{ + { + Name: "Computer", + Source: ` + access(all) contract Computer { + access(all) fun answer(): Int { + return 42 + } + } + `, + }, + } + + address, err := adapter.CreateAccount(context.Background(), nil, accountContracts) + assert.NoError(t, err) + + script := []byte(fmt.Sprintf(` + // address imports can omit leading zeros + import 0x%s + + transaction { + execute { + let answer = Computer.answer() + if answer != 42 { + panic("?!") + } + } + } + `, address)) + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + tx := flowsdk.NewTransaction(). + SetScript(script). + SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress) + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + err = adapter.SendTransaction(context.Background(), *tx) + assert.NoError(t, err) + + result, err := b.ExecuteNextTransaction() + assert.NoError(t, err) + AssertTransactionSucceeded(t, result) +} + +func TestAccountAccess(t *testing.T) { + + t.Parallel() + + b, adapter := setupAccountTests(t) + + // Create first account and deploy a contract A + // which has a field + // which only other code in the same should be allowed to access + + accountContracts := []templates.Contract{ + { + Name: "A", + Source: ` + access(all) contract A { + access(account) let a: Int + + init() { + self.a = 1 + } + } + `, + }, + } + + accountKeys := test.AccountKeyGenerator() + + accountKey1, signer1 := accountKeys.NewWithSigner() + + address1, err := adapter.CreateAccount( + context.Background(), + []*flowsdk.AccountKey{accountKey1}, + accountContracts, + ) + assert.NoError(t, err) + + // Deploy another contract B to the same account + // which accesses the field in contract A + // which allows access to code in the same account + + tx := templates.AddAccountContract( + address1, + templates.Contract{ + Name: "B", + Source: fmt.Sprintf(` + import A from 0x%s + + access(all) contract B { + access(all) fun use() { + let b = A.a + } + } + `, + address1.Hex(), + ), + }, + ) + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + tx.SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress) + + err = tx.SignPayload(address1, 0, signer1) + assert.NoError(t, err) + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + err = adapter.SendTransaction(context.Background(), *tx) + require.NoError(t, err) + + result, err := b.ExecuteNextTransaction() + require.NoError(t, err) + AssertTransactionSucceeded(t, result) + + _, err = b.CommitBlock() + require.NoError(t, err) + + // Create another account 2 + + accountKey2, signer2 := accountKeys.NewWithSigner() + + address2, err := adapter.CreateAccount( + context.Background(), + []*flowsdk.AccountKey{accountKey2}, + nil, + ) + assert.NoError(t, err) + + // Deploy a contract C to the second account + // which accesses the field in contract A of the first account + // which allows access to code in the same account + + tx = templates.AddAccountContract( + address2, + templates.Contract{ + Name: "C", + Source: fmt.Sprintf(` + import A from 0x%s + + access(all) contract C { + access(all) fun use() { + let b = A.a + } + } + `, + address1.Hex(), + ), + }, + ) + + tx.SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress) + + err = tx.SignPayload(address2, 0, signer2) + require.NoError(t, err) + + signer, err = b.ServiceKey().Signer() + assert.NoError(t, err) + err = tx.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + err = adapter.SendTransaction(context.Background(), *tx) + require.NoError(t, err) + + result, err = b.ExecuteNextTransaction() + require.NoError(t, err) + + require.False(t, result.Succeeded()) + require.Error(t, result.Error) + + require.Contains( + t, + result.Error.Error(), + "error: cannot access `a`: field requires `account` authorization", + ) +} diff --git a/integration/internal/emulator/tests/attachments_test.go b/integration/internal/emulator/tests/attachments_test.go new file mode 100644 index 00000000000..7d8f72dcdc5 --- /dev/null +++ b/integration/internal/emulator/tests/attachments_test.go @@ -0,0 +1,50 @@ +/* + * Flow Emulator + * + * Copyright Flow Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tests + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/onflow/flow-go/integration/internal/emulator" +) + +func TestAttachments(t *testing.T) { + + t.Parallel() + + b, err := emulator.New() + require.NoError(t, err) + + script := ` + access(all) resource R {} + + access(all) attachment A for R {} + + access(all) fun main() { + let r <- create R() + r[A] + destroy r + } + ` + + _, err = b.ExecuteScript([]byte(script), nil) + require.NoError(t, err) +} diff --git a/integration/internal/emulator/tests/block_info_test.go b/integration/internal/emulator/tests/block_info_test.go new file mode 100644 index 00000000000..06a937b8195 --- /dev/null +++ b/integration/internal/emulator/tests/block_info_test.go @@ -0,0 +1,113 @@ +/* + * Flow Emulator + * + * Copyright Flow Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tests + +import ( + "context" + "fmt" + "testing" + + "github.com/rs/zerolog" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + flowsdk "github.com/onflow/flow-go-sdk" + + emulator "github.com/onflow/flow-go/integration/internal/emulator" + flowgo "github.com/onflow/flow-go/model/flow" +) + +func TestBlockInfo(t *testing.T) { + + t.Parallel() + + b, err := emulator.New() + require.NoError(t, err) + + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + logger := zerolog.Nop() + adapter := emulator.NewSDKAdapter(&logger, b) + + block1, err := b.CommitBlock() + require.NoError(t, err) + + block2, err := b.CommitBlock() + require.NoError(t, err) + + t.Run("works as transaction", func(t *testing.T) { + tx := flowsdk.NewTransaction(). + SetScript([]byte(` + transaction { + execute { + let block = getCurrentBlock() + log(block) + + let lastBlock = getBlock(at: block.height - 1) + log(lastBlock) + } + } + `)). + SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress) + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + err = adapter.SendTransaction(context.Background(), *tx) + require.NoError(t, err) + + result, err := b.ExecuteNextTransaction() + assert.NoError(t, err) + AssertTransactionSucceeded(t, result) + + require.Len(t, result.Logs, 2) + assert.Equal(t, fmt.Sprintf("Block(height: %v, view: %v, id: 0x%x, timestamp: %.8f)", block2.Header.Height+1, + b.PendingBlockView(), b.PendingBlockID(), float64(b.PendingBlockTimestamp().Unix())), result.Logs[0]) + assert.Equal(t, fmt.Sprintf("Block(height: %v, view: %v, id: 0x%x, timestamp: %.8f)", block2.Header.Height, + block2.Header.View, block2.ID(), float64(block2.Header.Timestamp.Unix())), result.Logs[1]) + }) + + t.Run("works as script", func(t *testing.T) { + script := []byte(` + access(all) fun main() { + let block = getCurrentBlock() + log(block) + + let lastBlock = getBlock(at: block.height - 1) + log(lastBlock) + } + `) + + result, err := b.ExecuteScript(script, nil) + assert.NoError(t, err) + + assert.True(t, result.Succeeded()) + + require.Len(t, result.Logs, 2) + assert.Equal(t, fmt.Sprintf("Block(height: %v, view: %v, id: 0x%x, timestamp: %.8f)", block2.Header.Height, + block2.Header.View, block2.ID(), float64(block2.Header.Timestamp.Unix())), result.Logs[0]) + assert.Equal(t, fmt.Sprintf("Block(height: %v, view: %v, id: 0x%x, timestamp: %.8f)", block1.Header.Height, + block1.Header.View, block1.ID(), float64(block1.Header.Timestamp.Unix())), result.Logs[1]) + }) +} diff --git a/integration/internal/emulator/tests/block_test.go b/integration/internal/emulator/tests/block_test.go new file mode 100644 index 00000000000..b0bebb993f9 --- /dev/null +++ b/integration/internal/emulator/tests/block_test.go @@ -0,0 +1,178 @@ +/* + * Flow Emulator + * + * Copyright Flow Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tests + +import ( + "context" + "fmt" + "testing" + + "github.com/rs/zerolog" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + flowsdk "github.com/onflow/flow-go-sdk" + + emulator "github.com/onflow/flow-go/integration/internal/emulator" + flowgo "github.com/onflow/flow-go/model/flow" +) + +func TestCommitBlock(t *testing.T) { + + t.Parallel() + + b, err := emulator.New( + emulator.WithStorageLimitEnabled(false), + ) + + require.NoError(t, err) + + logger := zerolog.Nop() + adapter := emulator.NewSDKAdapter(&logger, b) + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + addTwoScript, _ := DeployAndGenerateAddTwoScript(t, adapter) + + tx1 := flowsdk.NewTransaction(). + SetScript([]byte(addTwoScript)). + SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress). + AddAuthorizer(serviceAccountAddress) + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx1.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + // Add tx1 to pending block + err = adapter.SendTransaction(context.Background(), *tx1) + assert.NoError(t, err) + + tx1Result, err := adapter.GetTransactionResult(context.Background(), tx1.ID()) + assert.NoError(t, err) + assert.Equal(t, flowsdk.TransactionStatusPending, tx1Result.Status) + + tx2 := flowsdk.NewTransaction(). + SetScript([]byte(`transaction { execute { panic("revert!") } }`)). + SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress). + AddAuthorizer(serviceAccountAddress) + + signer, err = b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx2.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + // Add tx2 to pending block + err = adapter.SendTransaction(context.Background(), *tx2) + require.NoError(t, err) + + tx2Result, err := adapter.GetTransactionResult(context.Background(), tx2.ID()) + assert.NoError(t, err) + assert.Equal(t, flowsdk.TransactionStatusPending, tx2Result.Status) + + // Execute tx1 + result, err := b.ExecuteNextTransaction() + assert.NoError(t, err) + assert.True(t, result.Succeeded()) + + // Execute tx2 + result, err = b.ExecuteNextTransaction() + assert.NoError(t, err) + assert.True(t, result.Reverted()) + + // Commit tx1 and tx2 into new block + _, err = b.CommitBlock() + assert.NoError(t, err) + + // tx1 status becomes TransactionStatusSealed + tx1Result, err = adapter.GetTransactionResult(context.Background(), tx1.ID()) + require.NoError(t, err) + assert.Equal(t, flowsdk.TransactionStatusSealed, tx1Result.Status) + + // tx2 status also becomes TransactionStatusSealed, even though it is reverted + tx2Result, err = adapter.GetTransactionResult(context.Background(), tx2.ID()) + require.NoError(t, err) + assert.Equal(t, flowsdk.TransactionStatusSealed, tx2Result.Status) + assert.Error(t, tx2Result.Error) +} + +func TestBlockView(t *testing.T) { + + t.Parallel() + + const nBlocks = 3 + + b, err := emulator.New() + require.NoError(t, err) + + logger := zerolog.Nop() + adapter := emulator.NewSDKAdapter(&logger, b) + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + t.Run("genesis should have 0 view", func(t *testing.T) { + block, err := b.GetBlockByHeight(0) + require.NoError(t, err) + assert.Equal(t, uint64(0), block.Header.Height) + assert.Equal(t, uint64(0), block.Header.View) + }) + + addTwoScript, _ := DeployAndGenerateAddTwoScript(t, adapter) + + // create a few blocks, each with one transaction + for i := 0; i < nBlocks; i++ { + + tx := flowsdk.NewTransaction(). + SetScript([]byte(addTwoScript)). + SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress). + AddAuthorizer(serviceAccountAddress) + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + // Add tx to pending block + err = adapter.SendTransaction(context.Background(), *tx) + assert.NoError(t, err) + + // execute and commit the block + _, _, err = b.ExecuteAndCommitBlock() + require.NoError(t, err) + } + const MaxViewIncrease = 3 + + for height := uint64(1); height <= nBlocks+1; height++ { + block, err := b.GetBlockByHeight(height) + require.NoError(t, err) + + maxView := height * MaxViewIncrease + t.Run(fmt.Sprintf("block %d should have view <%d", height, maxView), func(t *testing.T) { + assert.Equal(t, height, block.Header.Height) + assert.LessOrEqual(t, block.Header.View, maxView) + }) + } +} diff --git a/integration/internal/emulator/tests/blockchain_test.go b/integration/internal/emulator/tests/blockchain_test.go new file mode 100644 index 00000000000..c42f81fa940 --- /dev/null +++ b/integration/internal/emulator/tests/blockchain_test.go @@ -0,0 +1,153 @@ +/* + * Flow Emulator + * + * Copyright Flow Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tests + +import ( + "context" + "fmt" + "testing" + + "github.com/rs/zerolog" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/onflow/cadence" + "github.com/onflow/cadence/stdlib" + + flowsdk "github.com/onflow/flow-go-sdk" + "github.com/onflow/flow-go-sdk/templates" + + emulator "github.com/onflow/flow-go/integration/internal/emulator" +) + +const counterScript = ` + + access(all) contract Counting { + + access(all) event CountIncremented(count: Int) + + access(all) resource Counter { + access(all) var count: Int + + init() { + self.count = 0 + } + + access(all) fun add(_ count: Int) { + self.count = self.count + count + emit CountIncremented(count: self.count) + } + } + + access(all) fun createCounter(): @Counter { + return <-create Counter() + } + } +` + +// generateAddTwoToCounterScript generates a script that increments a counter. +// If no counter exists, it is created. +func GenerateAddTwoToCounterScript(counterAddress flowsdk.Address) string { + return fmt.Sprintf( + ` + import 0x%s + + transaction { + prepare(signer: auth(Storage, Capabilities) &Account) { + var counter = signer.storage.borrow<&Counting.Counter>(from: /storage/counter) + if counter == nil { + signer.storage.save(<-Counting.createCounter(), to: /storage/counter) + counter = signer.storage.borrow<&Counting.Counter>(from: /storage/counter) + + // Also publish this for others to borrow. + let cap = signer.capabilities.storage.issue<&Counting.Counter>(/storage/counter) + signer.capabilities.publish(cap, at: /public/counter) + } + counter?.add(2) + } + } + `, + counterAddress, + ) +} + +func DeployAndGenerateAddTwoScript(t *testing.T, adapter *emulator.SDKAdapter) (string, flowsdk.Address) { + + contracts := []templates.Contract{ + { + Name: "Counting", + Source: counterScript, + }, + } + + counterAddress, err := adapter.CreateAccount( + context.Background(), + nil, + contracts, + ) + require.NoError(t, err) + + return GenerateAddTwoToCounterScript(counterAddress), counterAddress +} + +func GenerateGetCounterCountScript(counterAddress flowsdk.Address, accountAddress flowsdk.Address) string { + return fmt.Sprintf( + ` + import 0x%s + + access(all) fun main(): Int { + return getAccount(0x%s).capabilities.borrow<&Counting.Counter>(/public/counter)?.count ?? 0 + } + `, + counterAddress, + accountAddress, + ) +} + +func AssertTransactionSucceeded(t *testing.T, result *emulator.TransactionResult) { + if !assert.True(t, result.Succeeded()) { + t.Error(result.Error) + } +} + +func LastCreatedAccount(b *emulator.Blockchain, result *emulator.TransactionResult) (*flowsdk.Account, error) { + logger := zerolog.Nop() + adapter := emulator.NewSDKAdapter(&logger, b) + + address, err := LastCreatedAccountAddress(result) + if err != nil { + return nil, err + } + + return adapter.GetAccount(context.Background(), address) +} + +func LastCreatedAccountAddress(result *emulator.TransactionResult) (flowsdk.Address, error) { + for _, event := range result.Events { + if event.Type == flowsdk.EventAccountCreated { + addressFieldValue := cadence.SearchFieldByName( + event.Value, + stdlib.AccountEventAddressParameter.Identifier, + ) + return flowsdk.Address(addressFieldValue.(cadence.Address)), nil + } + } + + return flowsdk.Address{}, fmt.Errorf("no account created in this result") +} diff --git a/integration/internal/emulator/tests/capcons_test.go b/integration/internal/emulator/tests/capcons_test.go new file mode 100644 index 00000000000..7fe37ecbd12 --- /dev/null +++ b/integration/internal/emulator/tests/capcons_test.go @@ -0,0 +1,44 @@ +/* + * Flow Emulator + * + * Copyright Flow Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tests + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/onflow/flow-go/integration/internal/emulator" +) + +func TestCapabilityControllers(t *testing.T) { + + t.Parallel() + + b, err := emulator.New() + require.NoError(t, err) + + script := ` + access(all) fun main() { + getAccount(0x1).capabilities.get + } + ` + + _, err = b.ExecuteScript([]byte(script), nil) + require.NoError(t, err) +} diff --git a/integration/internal/emulator/tests/collection_test.go b/integration/internal/emulator/tests/collection_test.go new file mode 100644 index 00000000000..0dae5c6dad6 --- /dev/null +++ b/integration/internal/emulator/tests/collection_test.go @@ -0,0 +1,117 @@ +/* + * Flow Emulator + * + * Copyright Flow Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package tests + +import ( + "context" + "testing" + + "github.com/rs/zerolog" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + flowsdk "github.com/onflow/flow-go-sdk" + + emulator "github.com/onflow/flow-go/integration/internal/emulator" + flowgo "github.com/onflow/flow-go/model/flow" +) + +func TestCollections(t *testing.T) { + + t.Parallel() + + t.Run("Empty block", func(t *testing.T) { + + t.Parallel() + + b, err := emulator.New() + require.NoError(t, err) + + block, err := b.CommitBlock() + require.NoError(t, err) + + // block should not contain any collections + assert.Empty(t, block.Payload.Guarantees) + }) + + t.Run("Non-empty block", func(t *testing.T) { + + t.Parallel() + + b, err := emulator.New( + emulator.WithStorageLimitEnabled(false), + ) + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + require.NoError(t, err) + + logger := zerolog.Nop() + adapter := emulator.NewSDKAdapter(&logger, b) + + addTwoScript, _ := DeployAndGenerateAddTwoScript(t, adapter) + + tx1 := flowsdk.NewTransaction(). + SetScript([]byte(addTwoScript)). + SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress). + AddAuthorizer(serviceAccountAddress) + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx1.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + tx2 := flowsdk.NewTransaction(). + SetScript([]byte(addTwoScript)). + SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress). + AddAuthorizer(serviceAccountAddress) + + err = tx2.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + // generate a list of transactions + transactions := []*flowsdk.Transaction{tx1, tx2} + + // add all transactions to block + for _, tx := range transactions { + err = adapter.SendTransaction(context.Background(), *tx) + require.NoError(t, err) + } + + block, _, err := b.ExecuteAndCommitBlock() + require.NoError(t, err) + + // block should contain at least one collection + assert.NotEmpty(t, block.Payload.Guarantees) + + i := 0 + for _, guarantee := range block.Payload.Guarantees { + collection, err := adapter.GetCollectionByID(context.Background(), emulator.FlowIdentifierToSDK(guarantee.ID())) + require.NoError(t, err) + + for _, txID := range collection.TransactionIDs { + assert.Equal(t, transactions[i].ID(), txID) + i++ + } + } + }) +} diff --git a/integration/internal/emulator/tests/events_test.go b/integration/internal/emulator/tests/events_test.go new file mode 100644 index 00000000000..9a561fd2c49 --- /dev/null +++ b/integration/internal/emulator/tests/events_test.go @@ -0,0 +1,202 @@ +/* + * Flow Emulator + * + * Copyright Flow Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tests + +import ( + "context" + "fmt" + "testing" + + "github.com/rs/zerolog" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/onflow/cadence" + "github.com/onflow/cadence/common" + + flowsdk "github.com/onflow/flow-go-sdk" + "github.com/onflow/flow-go-sdk/templates" + + emulator "github.com/onflow/flow-go/integration/internal/emulator" + flowgo "github.com/onflow/flow-go/model/flow" +) + +func TestEventEmitted(t *testing.T) { + + t.Parallel() + + t.Run("EmittedFromScript", func(t *testing.T) { + + t.Parallel() + + // Emitting events in scripts is not supported + + b, err := emulator.New() + require.NoError(t, err) + + script := []byte(` + access(all) event MyEvent(x: Int, y: Int) + + access(all) fun main() { + emit MyEvent(x: 1, y: 2) + } + `) + + result, err := b.ExecuteScript(script, nil) + assert.NoError(t, err) + require.NoError(t, result.Error) + require.Empty(t, result.Events) + }) + + t.Run("EmittedFromAccount", func(t *testing.T) { + + t.Parallel() + + b, err := emulator.New( + emulator.WithStorageLimitEnabled(false), + ) + require.NoError(t, err) + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + logger := zerolog.Nop() + adapter := emulator.NewSDKAdapter(&logger, b) + + accountContracts := []templates.Contract{ + { + Name: "Test", + Source: ` + access(all) contract Test { + access(all) event MyEvent(x: Int, y: Int) + + access(all) fun emitMyEvent(x: Int, y: Int) { + emit MyEvent(x: x, y: y) + } + } + `, + }, + } + + publicKey := b.ServiceKey() + accountKey := &flowsdk.AccountKey{ + Index: publicKey.Index, + PublicKey: publicKey.PublicKey, + SigAlgo: publicKey.SigAlgo, + HashAlgo: publicKey.HashAlgo, + Weight: publicKey.Weight, + SequenceNumber: publicKey.SequenceNumber, + } + + address, err := adapter.CreateAccount( + context.Background(), + []*flowsdk.AccountKey{accountKey}, + accountContracts, + ) + assert.NoError(t, err) + + script := []byte(fmt.Sprintf(` + import 0x%s + + transaction { + execute { + Test.emitMyEvent(x: 1, y: 2) + } + } + `, address.Hex())) + + tx := flowsdk.NewTransaction(). + SetScript(script). + SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress) + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + err = adapter.SendTransaction(context.Background(), *tx) + assert.NoError(t, err) + + result, err := b.ExecuteNextTransaction() + assert.NoError(t, err) + assert.True(t, result.Succeeded()) + + block, err := b.CommitBlock() + require.NoError(t, err) + + addr, _ := common.BytesToAddress(address.Bytes()) + location := common.AddressLocation{ + Address: addr, + Name: "Test", + } + expectedType := location.TypeID(nil, "Test.MyEvent") + + events, err := adapter.GetEventsForHeightRange(context.Background(), string(expectedType), block.Header.Height, block.Header.Height) + require.NoError(t, err) + require.Len(t, events, 1) + + actualEvent := events[0].Events[0] + decodedEvent := actualEvent.Value + decodedEventType := decodedEvent.Type().(*cadence.EventType) + expectedID := flowsdk.Event{TransactionID: tx.ID(), EventIndex: 0}.ID() + + assert.Equal(t, string(expectedType), actualEvent.Type) + assert.Equal(t, expectedID, actualEvent.ID()) + + fields := decodedEventType.FieldsMappedByName() + + assert.Contains(t, fields, "x") + assert.Contains(t, fields, "y") + + fieldValues := decodedEvent.FieldsMappedByName() + + assert.Equal(t, cadence.NewInt(1), fieldValues["x"]) + assert.Equal(t, cadence.NewInt(2), fieldValues["y"]) + + events, err = adapter.GetEventsForBlockIDs( + context.Background(), + string(expectedType), + []flowsdk.Identifier{ + flowsdk.Identifier(block.Header.ID()), + }, + ) + require.NoError(t, err) + require.Len(t, events, 1) + + actualEvent = events[0].Events[0] + decodedEvent = actualEvent.Value + decodedEventType = decodedEvent.Type().(*cadence.EventType) + expectedID = flowsdk.Event{TransactionID: tx.ID(), EventIndex: 0}.ID() + + assert.Equal(t, string(expectedType), actualEvent.Type) + assert.Equal(t, expectedID, actualEvent.ID()) + + fields = decodedEventType.FieldsMappedByName() + + assert.Contains(t, fields, "x") + assert.Contains(t, fields, "y") + + fieldValues = decodedEvent.FieldsMappedByName() + + assert.Equal(t, cadence.NewInt(1), fieldValues["x"]) + assert.Equal(t, cadence.NewInt(2), fieldValues["y"]) + + }) +} diff --git a/integration/internal/emulator/tests/logs_test.go b/integration/internal/emulator/tests/logs_test.go new file mode 100644 index 00000000000..af65ef14b89 --- /dev/null +++ b/integration/internal/emulator/tests/logs_test.go @@ -0,0 +1,46 @@ +/* + * Flow Emulator + * + * Copyright Flow Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tests + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/onflow/flow-go/integration/internal/emulator" +) + +func TestRuntimeLogs(t *testing.T) { + + t.Parallel() + + b, err := emulator.New() + require.NoError(t, err) + + script := []byte(` + access(all) fun main() { + log("elephant ears") + } + `) + + result, err := b.ExecuteScript(script, nil) + assert.NoError(t, err) + assert.Equal(t, []string{`"elephant ears"`}, result.Logs) +} diff --git a/integration/internal/emulator/tests/memstore_test.go b/integration/internal/emulator/tests/memstore_test.go new file mode 100644 index 00000000000..a28696d14be --- /dev/null +++ b/integration/internal/emulator/tests/memstore_test.go @@ -0,0 +1,118 @@ +/* + * Flow Emulator + * + * Copyright Flow Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tests + +import ( + "context" + "sync" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/onflow/flow-go/fvm/storage/snapshot" + "github.com/onflow/flow-go/integration/internal/emulator" + "github.com/onflow/flow-go/model/flow" + flowgo "github.com/onflow/flow-go/model/flow" +) + +func TestMemstore(t *testing.T) { + + t.Parallel() + + const blockHeight = 0 + key := flow.NewRegisterID(flowgo.EmptyAddress, "foo") + value := []byte("bar") + store := emulator.NewMemoryStore() + + err := store.InsertExecutionSnapshot( + blockHeight, + &snapshot.ExecutionSnapshot{ + WriteSet: map[flowgo.RegisterID]flowgo.RegisterValue{ + key: value, + }, + }, + ) + require.NoError(t, err) + + var wg sync.WaitGroup + + for i := 0; i < 100; i++ { + wg.Add(1) + go func() { + defer wg.Done() + + snapshot, err := store.LedgerByHeight( + context.Background(), + blockHeight) + require.NoError(t, err) + actualValue, err := snapshot.Get(key) + + require.NoError(t, err) + assert.Equal(t, value, actualValue) + }() + } + + wg.Wait() +} + +func TestMemstoreSetValueToNil(t *testing.T) { + + t.Parallel() + + store := emulator.NewMemoryStore() + key := flow.NewRegisterID(flowgo.EmptyAddress, "foo") + value := []byte("bar") + var nilByte []byte + nilValue := nilByte + + // set initial value + err := store.InsertExecutionSnapshot( + 0, + &snapshot.ExecutionSnapshot{ + WriteSet: map[flowgo.RegisterID]flowgo.RegisterValue{ + key: value, + }, + }) + require.NoError(t, err) + + // check initial value + ledger, err := store.LedgerByHeight(context.Background(), 0) + require.NoError(t, err) + register, err := ledger.Get(key) + require.NoError(t, err) + require.Equal(t, string(value), string(register)) + + // set value to nil + err = store.InsertExecutionSnapshot( + 1, + &snapshot.ExecutionSnapshot{ + WriteSet: map[flowgo.RegisterID]flowgo.RegisterValue{ + key: nilValue, + }, + }) + require.NoError(t, err) + + // check value is nil + ledger, err = store.LedgerByHeight(context.Background(), 1) + require.NoError(t, err) + register, err = ledger.Get(key) + require.NoError(t, err) + require.Equal(t, string(nilValue), string(register)) +} diff --git a/integration/internal/emulator/tests/pendingBlock_test.go b/integration/internal/emulator/tests/pendingBlock_test.go new file mode 100644 index 00000000000..be210742839 --- /dev/null +++ b/integration/internal/emulator/tests/pendingBlock_test.go @@ -0,0 +1,459 @@ +/* + * Flow Emulator + * + * Copyright Flow Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package tests + +import ( + "context" + "fmt" + "testing" + "time" + + "github.com/rs/zerolog" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + flowsdk "github.com/onflow/flow-go-sdk" + + emulator "github.com/onflow/flow-go/integration/internal/emulator" + flowgo "github.com/onflow/flow-go/model/flow" +) + +func setupPendingBlockTests(t *testing.T) ( + *emulator.Blockchain, + *emulator.SDKAdapter, + *flowsdk.Transaction, + *flowsdk.Transaction, + *flowsdk.Transaction, +) { + b, err := emulator.New( + emulator.WithStorageLimitEnabled(false), + ) + require.NoError(t, err) + logger := zerolog.Nop() + adapter := emulator.NewSDKAdapter(&logger, b) + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + addTwoScript, _ := DeployAndGenerateAddTwoScript(t, adapter) + + tx1 := flowsdk.NewTransaction(). + SetScript([]byte(addTwoScript)). + SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress). + AddAuthorizer(serviceAccountAddress) + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx1.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + tx2 := flowsdk.NewTransaction(). + SetScript([]byte(addTwoScript)). + SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber+1). + SetPayer(serviceAccountAddress). + AddAuthorizer(serviceAccountAddress) + + signer, err = b.ServiceKey().Signer() + assert.NoError(t, err) + err = tx2.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + invalid := flowsdk.NewTransaction(). + SetScript([]byte(`transaction { execute { panic("revert!") } }`)). + SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress). + AddAuthorizer(serviceAccountAddress) + + signer, err = b.ServiceKey().Signer() + assert.NoError(t, err) + err = invalid.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + return b, adapter, tx1, tx2, invalid +} + +func TestPendingBlockBeforeExecution(t *testing.T) { + + t.Parallel() + + t.Run("EmptyPendingBlock", func(t *testing.T) { + + t.Parallel() + + b, _, _, _, _ := setupPendingBlockTests(t) + + // Execute empty pending block + _, err := b.ExecuteBlock() + assert.NoError(t, err) + + // Commit empty pending block + _, err = b.CommitBlock() + assert.NoError(t, err) + + err = b.ResetPendingBlock() + assert.NoError(t, err) + }) + + t.Run("AddDuplicateTransaction", func(t *testing.T) { + + t.Parallel() + + b, adapter, tx, _, _ := setupPendingBlockTests(t) + + // Add tx1 to pending block + err := adapter.SendTransaction(context.Background(), *tx) + assert.NoError(t, err) + + // Add tx1 again + err = adapter.SendTransaction(context.Background(), *tx) + assert.IsType(t, &emulator.DuplicateTransactionError{}, err) + + err = b.ResetPendingBlock() + assert.NoError(t, err) + }) + + t.Run("CommitBeforeExecution", func(t *testing.T) { + + t.Parallel() + + b, adapter, tx, _, _ := setupPendingBlockTests(t) + + // Add tx1 to pending block + err := adapter.SendTransaction(context.Background(), *tx) + assert.NoError(t, err) + + // Attempt to commit block before execution begins + _, err = b.CommitBlock() + assert.IsType(t, &emulator.PendingBlockCommitBeforeExecutionError{}, err) + + err = b.ResetPendingBlock() + assert.NoError(t, err) + }) +} + +func TestPendingBlockDuringExecution(t *testing.T) { + + t.Parallel() + + t.Run("ExecuteNextTransaction", func(t *testing.T) { + + t.Parallel() + + b, adapter, tx1, _, invalid := setupPendingBlockTests(t) + + // Add tx1 to pending block + err := adapter.SendTransaction(context.Background(), *tx1) + require.NoError(t, err) + + // Add invalid script tx to pending block + err = adapter.SendTransaction(context.Background(), *invalid) + require.NoError(t, err) + + // Execute tx1 (succeeds) + result, err := b.ExecuteNextTransaction() + assert.NoError(t, err) + AssertTransactionSucceeded(t, result) + + // Execute invalid script tx (reverts) + result, err = b.ExecuteNextTransaction() + assert.NoError(t, err) + assert.True(t, result.Reverted()) + + err = b.ResetPendingBlock() + assert.NoError(t, err) + }) + + t.Run("ExecuteBlock", func(t *testing.T) { + + t.Parallel() + + b, adapter, tx1, _, invalid := setupPendingBlockTests(t) + + // Add tx1 to pending block + err := adapter.SendTransaction(context.Background(), *tx1) + require.NoError(t, err) + + // Add invalid script tx to pending block + err = adapter.SendTransaction(context.Background(), *invalid) + require.NoError(t, err) + + // Execute all tx in pending block (tx1, invalid) + results, err := b.ExecuteBlock() + assert.NoError(t, err) + + // tx1 result + assert.True(t, results[0].Succeeded()) + // invalid script tx result + assert.True(t, results[1].Reverted()) + + err = b.ResetPendingBlock() + assert.NoError(t, err) + }) + + t.Run("ExecuteNextThenBlock", func(t *testing.T) { + + t.Parallel() + + b, adapter, tx1, tx2, invalid := setupPendingBlockTests(t) + + // Add tx1 to pending block + err := adapter.SendTransaction(context.Background(), *tx1) + assert.NoError(t, err) + + // Add tx2 to pending block + err = adapter.SendTransaction(context.Background(), *tx2) + assert.NoError(t, err) + + // Add invalid script tx to pending block + err = adapter.SendTransaction(context.Background(), *invalid) + assert.NoError(t, err) + + // Execute tx1 first (succeeds) + result, err := b.ExecuteNextTransaction() + assert.NoError(t, err) + AssertTransactionSucceeded(t, result) + + // Execute rest of tx in pending block (tx2, invalid) + results, err := b.ExecuteBlock() + assert.NoError(t, err) + // tx2 result + assert.True(t, results[0].Succeeded()) + // invalid script tx result + assert.True(t, results[1].Reverted()) + + err = b.ResetPendingBlock() + assert.NoError(t, err) + }) + + t.Run("AddTransactionMidExecution", func(t *testing.T) { + + t.Parallel() + + b, adapter, tx1, tx2, invalid := setupPendingBlockTests(t) + + // Add tx1 to pending block + err := adapter.SendTransaction(context.Background(), *tx1) + assert.NoError(t, err) + + // Add invalid to pending block + err = adapter.SendTransaction(context.Background(), *invalid) + assert.NoError(t, err) + + // Execute tx1 first (succeeds) + result, err := b.ExecuteNextTransaction() + assert.NoError(t, err) + AssertTransactionSucceeded(t, result) + + // Attempt to add tx2 to pending block after execution begins + err = adapter.SendTransaction(context.Background(), *tx2) + assert.IsType(t, &emulator.PendingBlockMidExecutionError{}, err) + + err = b.ResetPendingBlock() + assert.NoError(t, err) + }) + + t.Run("CommitMidExecution", func(t *testing.T) { + + t.Parallel() + + b, adapter, tx1, _, invalid := setupPendingBlockTests(t) + + // Add tx1 to pending block + err := adapter.SendTransaction(context.Background(), *tx1) + assert.NoError(t, err) + + // Add invalid to pending block + err = adapter.SendTransaction(context.Background(), *invalid) + assert.NoError(t, err) + + // Execute tx1 first (succeeds) + result, err := b.ExecuteNextTransaction() + assert.NoError(t, err) + AssertTransactionSucceeded(t, result) + + // Attempt to commit block before execution finishes + _, err = b.CommitBlock() + assert.IsType(t, &emulator.PendingBlockMidExecutionError{}, err) + + err = b.ResetPendingBlock() + assert.NoError(t, err) + }) + + t.Run("TransactionsExhaustedDuringExecution", func(t *testing.T) { + + t.Parallel() + + b, adapter, tx1, _, _ := setupPendingBlockTests(t) + + // Add tx1 to pending block + err := adapter.SendTransaction(context.Background(), *tx1) + assert.NoError(t, err) + + // Execute tx1 (succeeds) + result, err := b.ExecuteNextTransaction() + assert.NoError(t, err) + AssertTransactionSucceeded(t, result) + + // Attempt to execute nonexistent next tx (fails) + _, err = b.ExecuteNextTransaction() + assert.IsType(t, &emulator.PendingBlockTransactionsExhaustedError{}, err) + + // Attempt to execute rest of block tx (fails) + _, err = b.ExecuteBlock() + assert.IsType(t, &emulator.PendingBlockTransactionsExhaustedError{}, err) + + err = b.ResetPendingBlock() + assert.NoError(t, err) + }) +} + +func TestPendingBlockCommit(t *testing.T) { + + t.Parallel() + + b, err := emulator.New( + emulator.WithStorageLimitEnabled(false), + ) + require.NoError(t, err) + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + logger := zerolog.Nop() + adapter := emulator.NewSDKAdapter(&logger, b) + + addTwoScript, _ := DeployAndGenerateAddTwoScript(t, adapter) + + t.Run("CommitBlock", func(t *testing.T) { + tx1 := flowsdk.NewTransaction(). + SetScript([]byte(addTwoScript)). + SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress). + AddAuthorizer(serviceAccountAddress) + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx1.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + // Add tx1 to pending block + err = adapter.SendTransaction(context.Background(), *tx1) + require.NoError(t, err) + + // Enter execution mode (block hash should not change after this point) + blockID := b.PendingBlockID() + + // Execute tx1 (succeeds) + result, err := b.ExecuteNextTransaction() + assert.NoError(t, err) + AssertTransactionSucceeded(t, result) + + // Commit pending block + block, err := b.CommitBlock() + assert.NoError(t, err) + assert.Equal(t, blockID, block.ID()) + }) + + t.Run("ExecuteAndCommitBlock", func(t *testing.T) { + tx1 := flowsdk.NewTransaction(). + SetScript([]byte(addTwoScript)). + SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress). + AddAuthorizer(serviceAccountAddress) + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx1.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + // Add tx1 to pending block + err = adapter.SendTransaction(context.Background(), *tx1) + assert.NoError(t, err) + + // Enter execution mode (block hash should not change after this point) + blockID := b.PendingBlockID() + + // Execute and commit pending block + block, results, err := b.ExecuteAndCommitBlock() + assert.NoError(t, err) + assert.Equal(t, blockID, block.ID()) + assert.Len(t, results, 1) + }) +} + +type testClock struct { + Time time.Time +} + +func (tc testClock) Now() time.Time { + return tc.Time.UTC() +} + +func TestPendingBlockSetTimestamp(t *testing.T) { + + t.Parallel() + + b, adapter, _, _, _ := setupPendingBlockTests(t) + clock := testClock{ + Time: time.Now().UTC(), + } + b.SetClock(clock.Now) + _, _ = b.CommitBlock() + + script := []byte(` + access(all) fun main(): UFix64 { + return getCurrentBlock().timestamp + } + `) + scriptResult, err := adapter.ExecuteScriptAtLatestBlock( + context.Background(), + script, + [][]byte{}, + ) + require.NoError(t, err) + + expected := fmt.Sprintf( + "{\"value\":\"%d.00000000\",\"type\":\"UFix64\"}\n", + clock.Time.Unix(), + ) + assert.Equal(t, expected, string(scriptResult)) + + clock = testClock{ + Time: time.Now().Add(time.Hour * 24 * 7).UTC(), + } + b.SetClock(clock.Now) + _, _ = b.CommitBlock() + + _, err = adapter.ExecuteScriptAtLatestBlock( + context.Background(), + script, + [][]byte{}, + ) + require.NoError(t, err) + + /*expected = fmt.Sprintf( + "{\"value\":\"%d.00000000\",\"type\":\"UFix64\"}\n", + clock.Time.Unix(), + )*/ + //assert.Equal(t, expected, string(scriptResult)) +} diff --git a/integration/internal/emulator/tests/result_test.go b/integration/internal/emulator/tests/result_test.go new file mode 100644 index 00000000000..c5679a509b3 --- /dev/null +++ b/integration/internal/emulator/tests/result_test.go @@ -0,0 +1,87 @@ +/* + * Flow Emulator + * + * Copyright Flow Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tests + +import ( + "errors" + "testing" + + "github.com/onflow/cadence" + "github.com/stretchr/testify/assert" + + flowsdk "github.com/onflow/flow-go-sdk" + "github.com/onflow/flow-go-sdk/test" + + emulator "github.com/onflow/flow-go/integration/internal/emulator" + "github.com/onflow/flow-go/model/flow" +) + +func TestResult(t *testing.T) { + + t.Parallel() + + t.Run("should return correct boolean", func(t *testing.T) { + + t.Parallel() + + idGenerator := test.IdentifierGenerator() + + trSucceed := &emulator.TransactionResult{ + TransactionID: idGenerator.New(), + ComputationUsed: 20, + MemoryEstimate: 2048, + Error: nil, + Logs: []string{}, + Events: []flowsdk.Event{}, + } + assert.True(t, trSucceed.Succeeded()) + assert.False(t, trSucceed.Reverted()) + + trReverted := &emulator.TransactionResult{ + TransactionID: idGenerator.New(), + ComputationUsed: 20, + MemoryEstimate: 2048, + Error: errors.New("transaction execution error"), + Logs: []string{}, + Events: []flowsdk.Event{}, + } + assert.True(t, trReverted.Reverted()) + assert.False(t, trReverted.Succeeded()) + + srSucceed := &emulator.ScriptResult{ + ScriptID: emulator.SDKIdentifierToFlow(idGenerator.New()), + Value: cadence.Value(cadence.NewInt(1)), + Error: nil, + Logs: []string{}, + Events: []flow.Event{}, + } + assert.True(t, srSucceed.Succeeded()) + assert.False(t, srSucceed.Reverted()) + + srReverted := &emulator.ScriptResult{ + ScriptID: emulator.SDKIdentifierToFlow(idGenerator.New()), + Value: cadence.Value(cadence.NewInt(1)), + Error: errors.New("transaction execution error"), + Logs: []string{}, + Events: []flow.Event{}, + } + assert.True(t, srReverted.Reverted()) + assert.False(t, srReverted.Succeeded()) + }) +} diff --git a/integration/internal/emulator/tests/script_test.go b/integration/internal/emulator/tests/script_test.go new file mode 100644 index 00000000000..ad7e4782512 --- /dev/null +++ b/integration/internal/emulator/tests/script_test.go @@ -0,0 +1,316 @@ +/* + * Flow Emulator + * + * Copyright Flow Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tests + +import ( + "context" + "fmt" + "testing" + + "github.com/onflow/cadence" + jsoncdc "github.com/onflow/cadence/encoding/json" + "github.com/rs/zerolog" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + flowsdk "github.com/onflow/flow-go-sdk" + + fvmerrors "github.com/onflow/flow-go/fvm/errors" + "github.com/onflow/flow-go/fvm/evm/stdlib" + emulator "github.com/onflow/flow-go/integration/internal/emulator" + flowgo "github.com/onflow/flow-go/model/flow" +) + +func TestExecuteScript(t *testing.T) { + + t.Parallel() + + b, err := emulator.New( + emulator.WithStorageLimitEnabled(false), + ) + require.NoError(t, err) + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + logger := zerolog.Nop() + adapter := emulator.NewSDKAdapter(&logger, b) + + addTwoScript, counterAddress := DeployAndGenerateAddTwoScript(t, adapter) + + tx := flowsdk.NewTransaction(). + SetScript([]byte(addTwoScript)). + SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress). + AddAuthorizer(serviceAccountAddress) + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + callScript := GenerateGetCounterCountScript(counterAddress, serviceAccountAddress) + + // Sample call (value is 0) + scriptResult, err := b.ExecuteScript([]byte(callScript), nil) + require.NoError(t, err) + assert.Equal(t, cadence.NewInt(0), scriptResult.Value) + + // Submit tx (script adds 2) + err = adapter.SendTransaction(context.Background(), *tx) + assert.NoError(t, err) + + txResult, err := b.ExecuteNextTransaction() + assert.NoError(t, err) + AssertTransactionSucceeded(t, txResult) + + t.Run("BeforeCommit", func(t *testing.T) { + t.Skip("TODO: fix stored ledger") + + // Sample call (value is still 0) + result, err := b.ExecuteScript([]byte(callScript), nil) + require.NoError(t, err) + assert.Equal(t, cadence.NewInt(0), result.Value) + }) + + _, err = b.CommitBlock() + assert.NoError(t, err) + + t.Run("AfterCommit", func(t *testing.T) { + // Sample call (value is 2) + result, err := b.ExecuteScript([]byte(callScript), nil) + require.NoError(t, err) + assert.Equal(t, cadence.NewInt(2), result.Value) + }) +} + +func TestExecuteScript_WithArguments(t *testing.T) { + + t.Parallel() + + t.Run("Int", func(t *testing.T) { + + t.Parallel() + + b, err := emulator.New() + require.NoError(t, err) + + scriptWithArgs := ` + access(all) fun main(n: Int): Int { + return n + } + ` + + arg, err := jsoncdc.Encode(cadence.NewInt(10)) + require.NoError(t, err) + + scriptResult, err := b.ExecuteScript([]byte(scriptWithArgs), [][]byte{arg}) + require.NoError(t, err) + + assert.Equal(t, cadence.NewInt(10), scriptResult.Value) + }) + + t.Run("String", func(t *testing.T) { + + t.Parallel() + + b, err := emulator.New() + require.NoError(t, err) + + scriptWithArgs := ` + access(all) fun main(n: String): Int { + log(n) + return 0 + } + ` + + arg, err := jsoncdc.Encode(cadence.String("Hello, World")) + require.NoError(t, err) + scriptResult, err := b.ExecuteScript([]byte(scriptWithArgs), [][]byte{arg}) + require.NoError(t, err) + assert.Contains(t, scriptResult.Logs, "\"Hello, World\"") + }) +} + +func TestExecuteScript_FlowServiceAccountBalance(t *testing.T) { + + t.Parallel() + + b, err := emulator.New() + require.NoError(t, err) + + code := fmt.Sprintf( + ` + import FlowServiceAccount from %[1]s + + access(all) + fun main(): UFix64 { + let acct = getAccount(%[1]s) + return FlowServiceAccount.defaultTokenBalance(acct) + } + `, + b.GetChain().ServiceAddress().HexWithPrefix(), + ) + + res, err := b.ExecuteScript([]byte(code), nil) + require.NoError(t, err) + require.NoError(t, res.Error) + + require.Positive(t, res.Value) +} + +func TestInfiniteScript(t *testing.T) { + + t.Parallel() + + const limit = 18 + b, err := emulator.New( + emulator.WithScriptGasLimit(limit), + ) + require.NoError(t, err) + + const code = ` + access(all) fun main() { + main() + } + ` + result, err := b.ExecuteScript([]byte(code), nil) + require.NoError(t, err) + + require.True(t, fvmerrors.IsComputationLimitExceededError(result.Error)) +} + +func TestScriptExecutionLimit(t *testing.T) { + + t.Parallel() + + const code = ` + access(all) fun main() { + var s: Int256 = 1024102410241024 + var i: Int256 = 0 + var a: Int256 = 7 + var b: Int256 = 5 + var c: Int256 = 2 + + while i < 150000 { + s = s * a + s = s / b + s = s / c + i = i + 1 + } + } + ` + + t.Run("ExceedingLimit", func(t *testing.T) { + + t.Parallel() + + const limit = 2000 + b, err := emulator.New( + emulator.WithScriptGasLimit(limit), + ) + require.NoError(t, err) + + result, err := b.ExecuteScript([]byte(code), nil) + require.NoError(t, err) + + require.True(t, fvmerrors.IsComputationLimitExceededError(result.Error)) + }) + + t.Run("SufficientLimit", func(t *testing.T) { + + t.Parallel() + + const limit = 19000 + b, err := emulator.New( + emulator.WithScriptGasLimit(limit), + ) + require.NoError(t, err) + + result, err := b.ExecuteScript([]byte(code), nil) + require.NoError(t, err) + require.NoError(t, result.Error) + }) +} + +// TestScriptWithCadenceRandom checks Cadence's random function works +// within a script +func TestScriptWithCadenceRandom(t *testing.T) { + + //language=cadence + code := ` + access(all) + fun main() { + assert(revertibleRandom() >= 0) + } + ` + + const limit = 200 + b, err := emulator.New( + emulator.WithScriptGasLimit(limit), + ) + require.NoError(t, err) + + result, err := b.ExecuteScript([]byte(code), nil) + require.NoError(t, err) + require.NoError(t, result.Error) +} + +// TestEVM checks evm functionality +func TestEVM(t *testing.T) { + serviceAddr := flowgo.Emulator.Chain().ServiceAddress() + code := []byte(fmt.Sprintf( + ` + import EVM from 0x%s + + access(all) + fun main(bytes: [UInt8; 20]) { + log(EVM.EVMAddress(bytes: bytes)) + } + `, + serviceAddr, + )) + + gasLimit := uint64(100_000) + + b, err := emulator.New( + emulator.WithScriptGasLimit(gasLimit), + ) + require.NoError(t, err) + + addressBytesArray := cadence.NewArray([]cadence.Value{ + cadence.UInt8(1), cadence.UInt8(1), + cadence.UInt8(2), cadence.UInt8(2), + cadence.UInt8(3), cadence.UInt8(3), + cadence.UInt8(4), cadence.UInt8(4), + cadence.UInt8(5), cadence.UInt8(5), + cadence.UInt8(6), cadence.UInt8(6), + cadence.UInt8(7), cadence.UInt8(7), + cadence.UInt8(8), cadence.UInt8(8), + cadence.UInt8(9), cadence.UInt8(9), + cadence.UInt8(10), cadence.UInt8(10), + }).WithType(stdlib.EVMAddressBytesCadenceType) + + result, err := b.ExecuteScript(code, [][]byte{jsoncdc.MustEncode(addressBytesArray)}) + require.NoError(t, err) + require.NoError(t, result.Error) + require.Len(t, result.Logs, 1) + require.Equal(t, result.Logs[0], fmt.Sprintf("A.%s.EVM.EVMAddress(bytes: %s)", serviceAddr, addressBytesArray.String())) + +} diff --git a/integration/internal/emulator/tests/store_test.go b/integration/internal/emulator/tests/store_test.go new file mode 100644 index 00000000000..1a4db36057f --- /dev/null +++ b/integration/internal/emulator/tests/store_test.go @@ -0,0 +1,482 @@ +/* + * Flow Emulator + * + * Copyright Flow Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tests_test + +import ( + "context" + "fmt" + "testing" + + "github.com/onflow/flow/protobuf/go/flow/entities" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/onflow/flow-go-sdk/test" + + "github.com/onflow/flow-go/fvm/storage/snapshot" + emulator "github.com/onflow/flow-go/integration/internal/emulator" + "github.com/onflow/flow-go/integration/internal/emulator/utils/unittest" + "github.com/onflow/flow-go/model/flow" + flowgo "github.com/onflow/flow-go/model/flow" +) + +func TestBlocks(t *testing.T) { + + t.Parallel() + + store := setupStore(t) + + block1 := &flowgo.Block{ + Header: &flowgo.Header{ + Height: 1, + }, + } + block2 := &flowgo.Block{ + Header: &flowgo.Header{ + Height: 2, + }, + } + + t.Run("should return error for not found", func(t *testing.T) { + t.Run("BlockByID", func(t *testing.T) { + freshId := test.IdentifierGenerator().New() + _, err := store.BlockByID(context.Background(), flowgo.Identifier(freshId)) + if assert.Error(t, err) { + assert.Equal(t, emulator.ErrNotFound, err) + } + }) + + t.Run("BlockByHeight", func(t *testing.T) { + _, err := store.BlockByHeight(context.Background(), block1.Header.Height) + if assert.Error(t, err) { + assert.Equal(t, emulator.ErrNotFound, err) + } + }) + + t.Run("LatestBlock", func(t *testing.T) { + _, err := store.LatestBlock(context.Background()) + if assert.Error(t, err) { + assert.Equal(t, emulator.ErrNotFound, err) + } + }) + }) + + t.Run("should be able to insert block", func(t *testing.T) { + err := store.StoreBlock(context.Background(), block1) + assert.NoError(t, err) + }) + + // insert block 1 + err := store.StoreBlock(context.Background(), block1) + assert.NoError(t, err) + + t.Run("should be able to get inserted block", func(t *testing.T) { + t.Run("BlockByHeight", func(t *testing.T) { + block, err := store.BlockByHeight(context.Background(), block1.Header.Height) + assert.NoError(t, err) + assert.Equal(t, block1, block) + }) + + t.Run("BlockByID", func(t *testing.T) { + block, err := store.BlockByID(context.Background(), block1.ID()) + assert.NoError(t, err) + assert.Equal(t, block1, block) + }) + + t.Run("LatestBlock", func(t *testing.T) { + block, err := store.LatestBlock(context.Background()) + assert.NoError(t, err) + assert.Equal(t, *block1, block) + }) + }) + + // insert block 2 + err = store.StoreBlock(context.Background(), block2) + assert.NoError(t, err) + + t.Run("Latest block should update", func(t *testing.T) { + block, err := store.LatestBlock(context.Background()) + assert.NoError(t, err) + assert.Equal(t, *block2, block) + }) +} + +func TestCollections(t *testing.T) { + + t.Parallel() + + store := setupStore(t) + + // collection with 3 transactions + col := unittest.FullCollectionFixture(3) + + t.Run("should return error for not found", func(t *testing.T) { + _, err := store.CollectionByID(context.Background(), col.ID()) + if assert.Error(t, err) { + assert.Equal(t, emulator.ErrNotFound, err) + } + }) + + t.Run("should be able to insert collection", func(t *testing.T) { + err := store.InsertCollection(col.Light()) + assert.NoError(t, err) + + t.Run("should be able to get inserted collection", func(t *testing.T) { + storedCol, err := store.CollectionByID(context.Background(), col.ID()) + require.NoError(t, err) + assert.Equal(t, col.Light(), storedCol) + }) + }) +} + +func TestTransactions(t *testing.T) { + + t.Parallel() + + store := setupStore(t) + + tx := unittest.TransactionFixture() + + t.Run("should return error for not found", func(t *testing.T) { + _, err := store.TransactionByID(context.Background(), tx.ID()) + if assert.Error(t, err) { + assert.Equal(t, emulator.ErrNotFound, err) + } + }) + + t.Run("should be able to insert tx", func(t *testing.T) { + err := store.InsertTransaction(tx.ID(), tx) + assert.NoError(t, err) + + t.Run("should be able to get inserted tx", func(t *testing.T) { + storedTx, err := store.TransactionByID(context.Background(), tx.ID()) + require.NoError(t, err) + assert.Equal(t, tx.ID(), storedTx.ID()) + }) + }) +} + +func TestFullCollection(t *testing.T) { + t.Parallel() + store := setupStore(t) + + col := unittest.FullCollectionFixture(3) + + t.Run("should be able to insert full collection", func(t *testing.T) { + _, err := store.CollectionByID(context.Background(), col.ID()) + require.Error(t, emulator.ErrNotFound, err) + + _, err = store.FullCollectionByID(context.Background(), col.ID()) + require.Error(t, emulator.ErrNotFound, err) + + err = store.InsertCollection(col.Light()) + require.NoError(t, err) + + for _, tx := range col.Transactions { + err = store.InsertTransaction(tx.ID(), *tx) + require.NoError(t, err) + } + + c, err := store.FullCollectionByID(context.Background(), col.ID()) + require.NoError(t, err) + require.Equal(t, col, c) + }) + +} + +func TestTransactionResults(t *testing.T) { + + t.Parallel() + + test := func(eventEncodingVersion entities.EventEncodingVersion) { + + t.Run(eventEncodingVersion.String(), func(t *testing.T) { + t.Parallel() + + store := setupStore(t) + + ids := test.IdentifierGenerator() + + result := unittest.StorableTransactionResultFixture(eventEncodingVersion) + + t.Run("should return error for not found", func(t *testing.T) { + txID := flowgo.Identifier(ids.New()) + + _, err := store.TransactionResultByID(context.Background(), txID) + if assert.Error(t, err) { + assert.Equal(t, emulator.ErrNotFound, err) + } + }) + + t.Run("should be able to insert result", func(t *testing.T) { + txID := flowgo.Identifier(ids.New()) + + err := store.InsertTransactionResult(txID, result) + assert.NoError(t, err) + + t.Run("should be able to get inserted result", func(t *testing.T) { + storedResult, err := store.TransactionResultByID(context.Background(), txID) + require.NoError(t, err) + assert.Equal(t, result, storedResult) + }) + }) + }) + } + + test(entities.EventEncodingVersion_CCF_V0) + test(entities.EventEncodingVersion_JSON_CDC_V0) +} + +func TestLedger(t *testing.T) { + + t.Parallel() + + t.Run("get/set", func(t *testing.T) { + + t.Parallel() + + store := setupStore(t) + + var blockHeight uint64 = 1 + + owner := flow.HexToAddress("0x01") + const key = "foo" + expected := []byte("bar") + + executionSnapshot := &snapshot.ExecutionSnapshot{ + WriteSet: map[flow.RegisterID]flow.RegisterValue{ + flow.NewRegisterID(owner, key): expected, + }, + } + + t.Run("should get able to set ledger", func(t *testing.T) { + err := store.InsertExecutionSnapshot( + blockHeight, + executionSnapshot) + assert.NoError(t, err) + }) + + t.Run("should be to get set ledger", func(t *testing.T) { + gotLedger, err := store.LedgerByHeight(context.Background(), blockHeight) + assert.NoError(t, err) + actual, err := gotLedger.Get(flow.NewRegisterID(owner, key)) + assert.NoError(t, err) + assert.Equal(t, expected, actual) + }) + }) + + t.Run("versioning", func(t *testing.T) { + + t.Parallel() + store := setupStore(t) + + owner := flow.HexToAddress("0x01") + + // Create a list of ledgers, where the ledger at index i has + // keys (i+2)-1->(i+2)+1 set to value i-1. + totalBlocks := 10 + var snapshots []*snapshot.ExecutionSnapshot + for i := 2; i < totalBlocks+2; i++ { + writeSet := map[flow.RegisterID]flow.RegisterValue{} + for j := i - 1; j <= i+1; j++ { + key := fmt.Sprintf("%d", j) + writeSet[flow.NewRegisterID(owner, key)] = []byte{byte(i - 1)} + } + snapshots = append( + snapshots, + &snapshot.ExecutionSnapshot{WriteSet: writeSet}) + } + require.Equal(t, totalBlocks, len(snapshots)) + + // Insert all the ledgers, starting with block 1. + // This will result in a ledger state that looks like this: + // Block 1: {1: 1, 2: 1, 3: 1} + // Block 2: {2: 2, 3: 2, 4: 2} + // ... + // The combined state at block N looks like: + // {1: 1, 2: 2, 3: 3, ..., N+1: N, N+2: N} + for i, snapshot := range snapshots { + err := store.InsertExecutionSnapshot( + uint64(i+1), + snapshot) + require.NoError(t, err) + } + + // View at block 1 should have keys 1, 2, 3 + t.Run("should version the first written block", func(t *testing.T) { + gotLedger, err := store.LedgerByHeight(context.Background(), 1) + assert.NoError(t, err) + for i := 1; i <= 3; i++ { + val, err := gotLedger.Get(flow.NewRegisterID(owner, fmt.Sprintf("%d", i))) + assert.NoError(t, err) + assert.Equal(t, []byte{byte(1)}, val) + } + }) + + // View at block N should have values 1->N+2 + t.Run("should version all blocks", func(t *testing.T) { + for block := 2; block < totalBlocks; block++ { + gotLedger, err := store.LedgerByHeight(context.Background(), uint64(block)) + assert.NoError(t, err) + // The keys 1->N-1 are defined in previous blocks + for i := 1; i < block; i++ { + val, err := gotLedger.Get(flow.NewRegisterID(owner, fmt.Sprintf("%d", i))) + assert.NoError(t, err) + assert.Equal(t, []byte{byte(i)}, val) + } + // The keys N->N+2 are defined in the queried block + for i := block; i <= block+2; i++ { + val, err := gotLedger.Get(flow.NewRegisterID(owner, fmt.Sprintf("%d", i))) + assert.NoError(t, err) + assert.Equal(t, []byte{byte(block)}, val) + } + } + }) + }) +} + +func TestInsertEvents(t *testing.T) { + + t.Parallel() + + test := func(eventEncodingVersion entities.EventEncodingVersion) { + + t.Run(eventEncodingVersion.String(), func(t *testing.T) { + t.Parallel() + + store := setupStore(t) + + events := test.EventGenerator(eventEncodingVersion) + + t.Run("should be able to insert events", func(t *testing.T) { + event, _ := emulator.SDKEventToFlow(events.New()) + events := []flowgo.Event{event} + + var blockHeight uint64 = 1 + + err := store.InsertEvents(blockHeight, events) + assert.NoError(t, err) + + t.Run("should be able to get inserted events", func(t *testing.T) { + gotEvents, err := store.EventsByHeight(context.Background(), blockHeight, "") + assert.NoError(t, err) + assert.Equal(t, events, gotEvents) + }) + }) + }) + } + + test(entities.EventEncodingVersion_CCF_V0) + test(entities.EventEncodingVersion_JSON_CDC_V0) +} + +func TestEventsByHeight(t *testing.T) { + + t.Parallel() + test := func(eventEncodingVersion entities.EventEncodingVersion) { + + t.Run(eventEncodingVersion.String(), func(t *testing.T) { + t.Parallel() + + store := setupStore(t) + + events := test.EventGenerator(eventEncodingVersion) + + var ( + nonEmptyBlockHeight uint64 = 1 + emptyBlockHeight uint64 = 2 + nonExistentBlockHeight uint64 = 3 + + allEvents = make([]flowgo.Event, 10) + eventsA = make([]flowgo.Event, 0, 5) + eventsB = make([]flowgo.Event, 0, 5) + ) + + for i := range allEvents { + event, _ := emulator.SDKEventToFlow(events.New()) + + event.TransactionIndex = uint32(i) + event.EventIndex = uint32(i * 2) + + // interleave events of both types + if i%2 == 0 { + event.Type = "A" + eventsA = append(eventsA, event) + } else { + event.Type = "B" + eventsB = append(eventsB, event) + } + + allEvents[i] = event + } + + err := store.InsertEvents(nonEmptyBlockHeight, allEvents) + assert.NoError(t, err) + + err = store.InsertEvents(emptyBlockHeight, nil) + assert.NoError(t, err) + + t.Run("should be able to query by block", func(t *testing.T) { + t.Run("non-empty block", func(t *testing.T) { + events, err := store.EventsByHeight(context.Background(), nonEmptyBlockHeight, "") + assert.NoError(t, err) + assert.Equal(t, allEvents, events) + }) + + t.Run("empty block", func(t *testing.T) { + events, err := store.EventsByHeight(context.Background(), emptyBlockHeight, "") + assert.NoError(t, err) + assert.Empty(t, events) + }) + + t.Run("non-existent block", func(t *testing.T) { + events, err := store.EventsByHeight(context.Background(), nonExistentBlockHeight, "") + assert.NoError(t, err) + assert.Empty(t, events) + }) + }) + + t.Run("should be able to query by event type", func(t *testing.T) { + t.Run("type=A, block=1", func(t *testing.T) { + // should be one event type=1 in block 1 + events, err := store.EventsByHeight(context.Background(), nonEmptyBlockHeight, "A") + assert.NoError(t, err) + assert.Equal(t, eventsA, events) + }) + + t.Run("type=B, block=1", func(t *testing.T) { + // should be 0 type=2 events here + events, err := store.EventsByHeight(context.Background(), nonEmptyBlockHeight, "B") + assert.NoError(t, err) + assert.Equal(t, eventsB, events) + }) + }) + }) + } + + test(entities.EventEncodingVersion_CCF_V0) + test(entities.EventEncodingVersion_JSON_CDC_V0) +} + +// setupStore creates a temporary file for the Sqlite and creates a +// sqlite.Store instance. The caller is responsible for closing the store +// and deleting the temporary directory. +func setupStore(t *testing.T) *emulator.Store { + return emulator.NewMemoryStore() +} diff --git a/integration/internal/emulator/tests/temp_dep_test.go b/integration/internal/emulator/tests/temp_dep_test.go new file mode 100644 index 00000000000..6bd6219f1b6 --- /dev/null +++ b/integration/internal/emulator/tests/temp_dep_test.go @@ -0,0 +1,25 @@ +/* + * Flow Emulator + * + * Copyright Flow Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package tests + +import "github.com/btcsuite/btcd/chaincfg/chainhash" + +// this is added to resolve the issue with chainhash ambiguous import, +// the code is not used, but it's needed to force go.mod specify and retain chainhash version +// workaround for issue: https://github.com/golang/go/issues/27899 +var _ = chainhash.Hash{} diff --git a/integration/internal/emulator/tests/transaction_test.go b/integration/internal/emulator/tests/transaction_test.go new file mode 100644 index 00000000000..cc7202ff2f3 --- /dev/null +++ b/integration/internal/emulator/tests/transaction_test.go @@ -0,0 +1,2145 @@ +/* + * Flow Emulator + * + * Copyright Flow Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tests + +import ( + "bufio" + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "io" + "strings" + "testing" + + "github.com/rs/zerolog" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/onflow/cadence" + "github.com/onflow/cadence/common" + "github.com/onflow/cadence/interpreter" + + "github.com/onflow/flow-go-sdk" + flowsdk "github.com/onflow/flow-go-sdk" + "github.com/onflow/flow-go-sdk/crypto" + "github.com/onflow/flow-go-sdk/templates" + "github.com/onflow/flow-go-sdk/test" + + fvmerrors "github.com/onflow/flow-go/fvm/errors" + "github.com/onflow/flow-go/fvm/evm/stdlib" + emulator "github.com/onflow/flow-go/integration/internal/emulator" + flowgo "github.com/onflow/flow-go/model/flow" +) + +func setupTransactionTests(t *testing.T, opts ...emulator.Option) ( + *emulator.Blockchain, + *emulator.SDKAdapter, +) { + b, err := emulator.New(opts...) + require.NoError(t, err) + + logger := zerolog.Nop() + return b, emulator.NewSDKAdapter(&logger, b) +} + +func TestSubmitTransaction(t *testing.T) { + + t.Parallel() + + b, adapter := setupTransactionTests(t) + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + addTwoScript, _ := DeployAndGenerateAddTwoScript(t, adapter) + + tx1 := flowsdk.NewTransaction(). + SetScript([]byte(addTwoScript)). + SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress). + AddAuthorizer(serviceAccountAddress) + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx1.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + // Submit tx1 + err = adapter.SendTransaction(context.Background(), *tx1) + assert.NoError(t, err) + + // Execute tx1 + result, err := b.ExecuteNextTransaction() + assert.NoError(t, err) + AssertTransactionSucceeded(t, result) + + _, err = b.CommitBlock() + assert.NoError(t, err) + + // tx1 status becomes TransactionStatusSealed + tx1Result, err := adapter.GetTransactionResult(context.Background(), tx1.ID()) + assert.NoError(t, err) + assert.Equal(t, flowsdk.TransactionStatusSealed, tx1Result.Status) +} + +// TODO: Add test case for missing ReferenceBlockID +// TODO: Add test case for missing ProposalKey +func TestSubmitTransaction_Invalid(t *testing.T) { + + t.Parallel() + + t.Run("Empty transaction", func(t *testing.T) { + + t.Parallel() + + b, adapter := setupTransactionTests(t) + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + // Create empty transaction (no required fields) + tx := flowsdk.NewTransaction() + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + // Submit tx + err = adapter.SendTransaction(context.Background(), *tx) + assert.IsType(t, err, &emulator.IncompleteTransactionError{}) + }) + + t.Run("Missing script", func(t *testing.T) { + + t.Parallel() + + b, adapter := setupTransactionTests(t) + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + // Create transaction with no Script field + tx := flowsdk.NewTransaction(). + SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress) + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + err = adapter.SendTransaction(context.Background(), *tx) + assert.IsType(t, err, &emulator.IncompleteTransactionError{}) + }) + + t.Run("Invalid script", func(t *testing.T) { + + t.Parallel() + + b, adapter := setupTransactionTests(t) + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + // Create transaction with invalid Script field + tx := flowsdk.NewTransaction(). + SetScript([]byte("this script cannot be parsed")). + SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress) + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + // Submit tx + err = adapter.SendTransaction(context.Background(), *tx) + assert.IsType(t, &emulator.InvalidTransactionScriptError{}, err) + }) + + t.Run("Missing gas limit", func(t *testing.T) { + + t.Parallel() + + t.Skip("TODO: transaction validation") + + b, adapter := setupTransactionTests( + t, + emulator.WithStorageLimitEnabled(false), + ) + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + addTwoScript, _ := DeployAndGenerateAddTwoScript(t, adapter) + + // Create transaction with no GasLimit field + tx := flowsdk.NewTransaction(). + SetScript([]byte(addTwoScript)). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress) + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + // Submit tx + err = adapter.SendTransaction(context.Background(), *tx) + assert.IsType(t, &emulator.IncompleteTransactionError{}, err) + }) + + t.Run("Missing payer account", func(t *testing.T) { + + t.Parallel() + + b, adapter := setupTransactionTests( + t, + emulator.WithStorageLimitEnabled(false), + ) + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + addTwoScript, _ := DeployAndGenerateAddTwoScript(t, adapter) + + // Create transaction with no PayerAccount field + tx := flowsdk.NewTransaction(). + SetScript([]byte(addTwoScript)). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit) + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + // Submit tx + err = adapter.SendTransaction(context.Background(), *tx) + assert.IsType(t, err, &emulator.IncompleteTransactionError{}) + }) + + t.Run("Missing proposal key", func(t *testing.T) { + + t.Parallel() + + b, adapter := setupTransactionTests( + t, + emulator.WithStorageLimitEnabled(false), + ) + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + addTwoScript, _ := DeployAndGenerateAddTwoScript(t, adapter) + + // Create transaction with no PayerAccount field + tx := flowsdk.NewTransaction(). + SetScript([]byte(addTwoScript)). + SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit) + + tx.ProposalKey = flowsdk.ProposalKey{} + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + // Submit tx + err = adapter.SendTransaction(context.Background(), *tx) + assert.IsType(t, &emulator.IncompleteTransactionError{}, err) + }) + + t.Run("Invalid sequence number", func(t *testing.T) { + + t.Parallel() + + b, adapter := setupTransactionTests( + t, + emulator.WithStorageLimitEnabled(false), + ) + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + addTwoScript, _ := DeployAndGenerateAddTwoScript(t, adapter) + + invalidSequenceNumber := b.ServiceKey().SequenceNumber + 2137 + tx := flowsdk.NewTransaction(). + SetScript([]byte(addTwoScript)). + SetPayer(serviceAccountAddress). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, invalidSequenceNumber). + SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + AddAuthorizer(serviceAccountAddress) + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + // Submit tx + err = adapter.SendTransaction(context.Background(), *tx) + require.NoError(t, err) + + result, err := b.ExecuteNextTransaction() + require.NoError(t, err) + + require.Error(t, result.Error) + + assert.IsType(t, &emulator.FVMError{}, result.Error) + seqErr := fvmerrors.InvalidProposalSeqNumberError{} + ok := errors.As(result.Error, &seqErr) + assert.True(t, ok) + assert.Equal(t, invalidSequenceNumber, seqErr.ProvidedSeqNumber()) + }) + + const expiry = 10 + + t.Run("Missing reference block ID", func(t *testing.T) { + + t.Parallel() + + b, adapter := setupTransactionTests( + t, + emulator.WithTransactionExpiry(expiry), + emulator.WithStorageLimitEnabled(false), + ) + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + addTwoScript, _ := DeployAndGenerateAddTwoScript(t, adapter) + + tx := flowsdk.NewTransaction(). + SetScript([]byte(addTwoScript)). + SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress) + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + err = adapter.SendTransaction(context.Background(), *tx) + assert.IsType(t, &emulator.IncompleteTransactionError{}, err) + }) + + t.Run("Expired transaction", func(t *testing.T) { + + t.Parallel() + + b, adapter := setupTransactionTests( + t, + emulator.WithTransactionExpiry(expiry), + emulator.WithStorageLimitEnabled(false), + ) + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + addTwoScript, _ := DeployAndGenerateAddTwoScript(t, adapter) + + expiredBlock, err := b.GetLatestBlock() + require.NoError(t, err) + + // commit blocks until expiry window is exceeded + for i := 0; i < expiry+1; i++ { + _, _, err := b.ExecuteAndCommitBlock() + require.NoError(t, err) + } + + tx := flowsdk.NewTransaction(). + SetScript([]byte(addTwoScript)). + SetReferenceBlockID(flowsdk.Identifier(expiredBlock.ID())). + SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress) + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + err = adapter.SendTransaction(context.Background(), *tx) + assert.IsType(t, &emulator.ExpiredTransactionError{}, err) + }) + + t.Run("Invalid signature for provided data", func(t *testing.T) { + + t.Parallel() + + b, adapter := setupTransactionTests( + t, + emulator.WithStorageLimitEnabled(false), + ) + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + addTwoScript, _ := DeployAndGenerateAddTwoScript(t, adapter) + + tx := flowsdk.NewTransaction(). + SetScript([]byte(addTwoScript)). + SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress). + AddAuthorizer(serviceAccountAddress) + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + tx.SetComputeLimit(100) // change data after signing + + err = adapter.SendTransaction(context.Background(), *tx) + assert.NoError(t, err) + + result, err := b.ExecuteNextTransaction() + assert.NoError(t, err) + + debug := emulator.NewTransactionInvalidSignature(&flowgo.TransactionBody{ + ReferenceBlockID: flowgo.Identifier{}, + Script: nil, + Arguments: nil, + GasLimit: flowgo.DefaultMaxTransactionGasLimit, + ProposalKey: flowgo.ProposalKey{ + Address: emulator.SDKAddressToFlow(serviceAccountAddress), + KeyIndex: b.ServiceKey().Index, + SequenceNumber: b.ServiceKey().SequenceNumber, + }, + Payer: emulator.SDKAddressToFlow(serviceAccountAddress), + Authorizers: emulator.SDKAddressesToFlow([]flowsdk.Address{serviceAccountAddress}), + PayloadSignatures: nil, + EnvelopeSignatures: nil, + }) + + assert.NotNil(t, result.Error) + assert.IsType(t, result.Debug, debug) + }) +} + +func TestSubmitTransaction_Duplicate(t *testing.T) { + + t.Parallel() + + b, adapter := setupTransactionTests( + t, + emulator.WithStorageLimitEnabled(false), + ) + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + addTwoScript, _ := DeployAndGenerateAddTwoScript(t, adapter) + + tx := flowsdk.NewTransaction(). + SetScript([]byte(addTwoScript)). + SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress). + AddAuthorizer(serviceAccountAddress) + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + // Submit tx + err = adapter.SendTransaction(context.Background(), *tx) + assert.NoError(t, err) + + result, err := b.ExecuteNextTransaction() + assert.NoError(t, err) + AssertTransactionSucceeded(t, result) + + _, err = b.CommitBlock() + assert.NoError(t, err) + + // Submit same tx again (errors) + err = adapter.SendTransaction(context.Background(), *tx) + assert.IsType(t, err, &emulator.DuplicateTransactionError{}) +} + +func TestSubmitTransaction_Reverted(t *testing.T) { + + t.Parallel() + + b, adapter := setupTransactionTests(t) + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + tx := flowsdk.NewTransaction(). + SetScript([]byte(`transaction { execute { panic("revert!") } }`)). + SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress). + AddAuthorizer(serviceAccountAddress) + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + // Submit invalid tx1 + err = adapter.SendTransaction(context.Background(), *tx) + assert.NoError(t, err) + + result, err := b.ExecuteNextTransaction() + assert.NoError(t, err) + assert.True(t, result.Reverted()) + + _, err = b.CommitBlock() + assert.NoError(t, err) + + // tx1 status becomes TransactionStatusSealed + tx1Result, err := adapter.GetTransactionResult(context.Background(), tx.ID()) + assert.NoError(t, err) + assert.Equal(t, flowsdk.TransactionStatusSealed, tx1Result.Status) + assert.Error(t, tx1Result.Error) +} + +func TestSubmitTransaction_Authorizers(t *testing.T) { + + t.Parallel() + + b, adapter := setupTransactionTests( + t, + emulator.WithStorageLimitEnabled(false), + ) + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + accountKeys := test.AccountKeyGenerator() + + accountKeyB, signerB := accountKeys.NewWithSigner() + accountKeyB.SetWeight(flowsdk.AccountKeyWeightThreshold) + + accountAddressB, err := adapter.CreateAccount(context.Background(), []*flowsdk.AccountKey{accountKeyB}, nil) + assert.NoError(t, err) + + t.Run("Extra authorizers", func(t *testing.T) { + // script only supports one account + script := []byte(` + transaction { + prepare(signer: &Account) {} + } + `) + + // create transaction with two authorizing accounts + tx := flowsdk.NewTransaction(). + SetScript(script). + SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress). + AddAuthorizer(serviceAccountAddress). + AddAuthorizer(accountAddressB) + + err = tx.SignPayload(accountAddressB, 0, signerB) + assert.NoError(t, err) + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + err = adapter.SendTransaction(context.Background(), *tx) + require.NoError(t, err) + + result, err := b.ExecuteNextTransaction() + require.NoError(t, err) + assert.True(t, result.Reverted()) + + _, err = b.CommitBlock() + assert.NoError(t, err) + }) + + t.Run("Insufficient authorizers", func(t *testing.T) { + // script requires two accounts + script := []byte(` + transaction { + prepare(signerA: &Account, signerB: &Account) {} + } + `) + + // create transaction with two accounts + tx := flowsdk.NewTransaction(). + SetScript(script). + SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress). + AddAuthorizer(serviceAccountAddress) + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + err = adapter.SendTransaction(context.Background(), *tx) + assert.NoError(t, err) + + result, err := b.ExecuteNextTransaction() + assert.NoError(t, err) + assert.True(t, result.Reverted()) + + _, err = b.CommitBlock() + assert.NoError(t, err) + }) +} + +func TestSubmitTransaction_EnvelopeSignature(t *testing.T) { + + t.Parallel() + + t.Run("Missing envelope signature", func(t *testing.T) { + + t.Parallel() + + b, adapter := setupTransactionTests( + t, + emulator.WithStorageLimitEnabled(false), + ) + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + addTwoScript, _ := DeployAndGenerateAddTwoScript(t, adapter) + + tx := flowsdk.NewTransaction(). + SetScript([]byte(addTwoScript)). + SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress). + AddAuthorizer(serviceAccountAddress) + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx.SignPayload(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + err = adapter.SendTransaction(context.Background(), *tx) + assert.NoError(t, err) + + result, err := b.ExecuteNextTransaction() + assert.NoError(t, err) + + assert.True(t, fvmerrors.HasErrorCode(result.Error, fvmerrors.ErrCodeAccountAuthorizationError)) + }) + + t.Run("Invalid account", func(t *testing.T) { + + t.Parallel() + + b, adapter := setupTransactionTests(t) + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + addresses := flowsdk.NewAddressGenerator(flowsdk.Emulator) + for { + _, err := adapter.GetAccount(context.Background(), addresses.NextAddress()) + if err != nil { + break + } + } + + nonExistentAccountAddress := addresses.Address() + + script := []byte(` + transaction { + prepare(signer: &Account) {} + } + `) + + tx := flowsdk.NewTransaction(). + SetScript(script). + SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress). + AddAuthorizer(nonExistentAccountAddress) + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx.SignPayload(nonExistentAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + err = tx.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + err = adapter.SendTransaction(context.Background(), *tx) + assert.NoError(t, err) + + result, err := b.ExecuteNextTransaction() + assert.NoError(t, err) + + assert.Error(t, result.Error) + assert.True(t, fvmerrors.IsAccountPublicKeyNotFoundError(result.Error)) + }) + + t.Run("Mismatched authorizer count", func(t *testing.T) { + + t.Parallel() + + b, adapter := setupTransactionTests( + t, + emulator.WithTransactionValidationEnabled(false), + ) + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + addresses := flowsdk.NewAddressGenerator(flowsdk.Emulator) + for { + _, err := adapter.GetAccount(context.Background(), addresses.NextAddress()) + if err != nil { + break + } + } + + nonExistentAccountAddress := addresses.Address() + + script := []byte(` + transaction { + prepare() {} + } + `) + + tx := flowsdk.NewTransaction(). + SetScript(script). + SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress). + AddAuthorizer(nonExistentAccountAddress) + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx.SignPayload(nonExistentAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + err = tx.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + err = adapter.SendTransaction(context.Background(), *tx) + assert.NoError(t, err) + + result, err := b.ExecuteNextTransaction() + assert.NoError(t, err) + + assert.ErrorContains(t, result.Error, "authorizer count mismatch") + }) + + t.Run("Invalid key", func(t *testing.T) { + + t.Parallel() + + b, adapter := setupTransactionTests( + t, + emulator.WithStorageLimitEnabled(false), + ) + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + addTwoScript, _ := DeployAndGenerateAddTwoScript(t, adapter) + + // use key that does not exist on service account + invalidKey, _ := crypto.GeneratePrivateKey(crypto.ECDSA_P256, + []byte("invalid key invalid key invalid key invalid key invalid key invalid key")) + invalidSigner, err := crypto.NewNaiveSigner(invalidKey, crypto.SHA3_256) + require.NoError(t, err) + + tx := flowsdk.NewTransaction(). + SetScript([]byte(addTwoScript)). + SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress). + AddAuthorizer(serviceAccountAddress) + + err = tx.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, invalidSigner) + require.NoError(t, err) + + err = adapter.SendTransaction(context.Background(), *tx) + assert.NoError(t, err) + + result, err := b.ExecuteNextTransaction() + assert.NoError(t, err) + + assert.True(t, fvmerrors.HasErrorCode(result.Error, fvmerrors.ErrCodeInvalidProposalSignatureError)) + }) + + t.Run("Key weights", func(t *testing.T) { + + t.Parallel() + + b, adapter := setupTransactionTests( + t, + emulator.WithStorageLimitEnabled(false), + ) + + accountKeys := test.AccountKeyGenerator() + + accountKeyA, signerA := accountKeys.NewWithSigner() + accountKeyA.SetWeight(flowsdk.AccountKeyWeightThreshold / 2) + + accountKeyB, signerB := accountKeys.NewWithSigner() + accountKeyB.SetWeight(flowsdk.AccountKeyWeightThreshold / 2) + + accountAddressA, err := adapter.CreateAccount(context.Background(), []*flowsdk.AccountKey{accountKeyA, accountKeyB}, nil) + assert.NoError(t, err) + + script := []byte(` + transaction { + prepare(signer: &Account) {} + } + `) + + tx := flowsdk.NewTransaction(). + SetScript(script). + SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(accountAddressA, 1, 0). + SetPayer(accountAddressA). + AddAuthorizer(accountAddressA) + + // Insufficient keys + err = tx.SignEnvelope(accountAddressA, 1, signerB) + assert.NoError(t, err) + + err = adapter.SendTransaction(context.Background(), *tx) + assert.NoError(t, err) + + // Add key so we have sufficient keys + err = tx.SignEnvelope(accountAddressA, 0, signerA) + assert.NoError(t, err) + + err = adapter.SendTransaction(context.Background(), *tx) + assert.NoError(t, err) + + t.Run("Insufficient key weight", func(t *testing.T) { + result, err := b.ExecuteNextTransaction() + assert.NoError(t, err) + + assert.True(t, fvmerrors.HasErrorCode(result.Error, fvmerrors.ErrCodeAccountAuthorizationError)) + }) + + t.Run("Sufficient key weight", func(t *testing.T) { + result, err := b.ExecuteNextTransaction() + assert.NoError(t, err) + + AssertTransactionSucceeded(t, result) + }) + }) +} + +func TestSubmitTransaction_PayloadSignatures(t *testing.T) { + + t.Parallel() + + t.Run("Missing payload signature", func(t *testing.T) { + + t.Parallel() + + b, adapter := setupTransactionTests( + t, + emulator.WithStorageLimitEnabled(false), + ) + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + addTwoScript, _ := DeployAndGenerateAddTwoScript(t, adapter) + + // create a new account, + // authorizer must be different from payer + + accountKeys := test.AccountKeyGenerator() + + accountKeyB, _ := accountKeys.NewWithSigner() + accountKeyB.SetWeight(flowsdk.AccountKeyWeightThreshold) + + accountAddressB, err := adapter.CreateAccount(context.Background(), []*flowsdk.AccountKey{accountKeyB}, nil) + assert.NoError(t, err) + + tx := flowsdk.NewTransaction(). + SetScript([]byte(addTwoScript)). + SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress). + AddAuthorizer(accountAddressB) + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + err = adapter.SendTransaction(context.Background(), *tx) + assert.NoError(t, err) + + result, err := b.ExecuteNextTransaction() + assert.NoError(t, err) + + assert.True(t, fvmerrors.HasErrorCode(result.Error, fvmerrors.ErrCodeAccountAuthorizationError)) + }) + + t.Run("Multiple payload signers", func(t *testing.T) { + + t.Parallel() + + b, adapter := setupTransactionTests( + t, + emulator.WithStorageLimitEnabled(false), + ) + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + accountKeys := test.AccountKeyGenerator() + + accountKeyB, signerB := accountKeys.NewWithSigner() + accountKeyB.SetWeight(flowsdk.AccountKeyWeightThreshold) + + accountAddressB, err := adapter.CreateAccount(context.Background(), []*flowsdk.AccountKey{accountKeyB}, nil) + assert.NoError(t, err) + + multipleAccountScript := []byte(` + transaction { + prepare(signerA: &Account, signerB: &Account) { + log(signerA.address) + log(signerB.address) + } + } + `) + + tx := flowsdk.NewTransaction(). + SetScript(multipleAccountScript). + SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress). + AddAuthorizer(serviceAccountAddress). + AddAuthorizer(accountAddressB) + + err = tx.SignPayload(accountAddressB, 0, signerB) + assert.NoError(t, err) + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + err = adapter.SendTransaction(context.Background(), *tx) + require.NoError(t, err) + + result, err := b.ExecuteNextTransaction() + require.NoError(t, err) + AssertTransactionSucceeded(t, result) + + assert.Contains(t, + result.Logs, + interpreter.NewUnmeteredAddressValueFromBytes(serviceAccountAddress.Bytes()).String(), + ) + + assert.Contains(t, + result.Logs, + interpreter.NewUnmeteredAddressValueFromBytes(accountAddressB.Bytes()).String(), + ) + }) +} + +func TestSubmitTransaction_Arguments(t *testing.T) { + + t.Parallel() + + addresses := test.AddressGenerator() + + fix64Value, _ := cadence.NewFix64("123456.00000") + uFix64Value, _ := cadence.NewUFix64("123456.00000") + + var tests = []struct { + argType cadence.Type + arg cadence.Value + }{ + { + cadence.BoolType, + cadence.NewBool(true), + }, + { + cadence.StringType, + cadence.String("foo"), + }, + { + cadence.AddressType, + cadence.NewAddress(addresses.New()), + }, + { + cadence.IntType, + cadence.NewInt(42), + }, + { + cadence.Int8Type, + cadence.NewInt8(42), + }, + { + cadence.Int16Type, + cadence.NewInt16(42), + }, + { + cadence.Int32Type, + cadence.NewInt32(42), + }, + { + cadence.Int64Type, + cadence.NewInt64(42), + }, + { + cadence.Int128Type, + cadence.NewInt128(42), + }, + { + cadence.Int256Type, + cadence.NewInt256(42), + }, + { + cadence.UIntType, + cadence.NewUInt(42), + }, + { + cadence.UInt8Type, + cadence.NewUInt8(42), + }, + { + cadence.UInt16Type, + cadence.NewUInt16(42), + }, + { + cadence.UInt32Type, + cadence.NewUInt32(42), + }, + { + cadence.UInt64Type, + cadence.NewUInt64(42), + }, + { + cadence.UInt128Type, + cadence.NewUInt128(42), + }, + { + cadence.UInt256Type, + cadence.NewUInt256(42), + }, + { + cadence.Word8Type, + cadence.NewWord8(42), + }, + { + cadence.Word16Type, + cadence.NewWord16(42), + }, + { + cadence.Word32Type, + cadence.NewWord32(42), + }, + { + cadence.Word64Type, + cadence.NewWord64(42), + }, + { + cadence.Fix64Type, + fix64Value, + }, + { + cadence.UFix64Type, + uFix64Value, + }, + { + &cadence.ConstantSizedArrayType{ + Size: 3, + ElementType: cadence.IntType, + }, + cadence.NewArray([]cadence.Value{ + cadence.NewInt(1), + cadence.NewInt(2), + cadence.NewInt(3), + }), + }, + { + &cadence.DictionaryType{ + KeyType: cadence.StringType, + ElementType: cadence.IntType, + }, + cadence.NewDictionary([]cadence.KeyValuePair{ + { + Key: cadence.String("a"), + Value: cadence.NewInt(1), + }, + { + Key: cadence.String("b"), + Value: cadence.NewInt(2), + }, + { + Key: cadence.String("c"), + Value: cadence.NewInt(3), + }, + }), + }, + } + + var script = func(argType cadence.Type) []byte { + return []byte(fmt.Sprintf(` + transaction(x: %s) { + execute { + log(x) + } + } + `, argType.ID())) + } + + for _, tt := range tests { + t.Run(tt.argType.ID(), func(t *testing.T) { + + b, adapter := setupTransactionTests(t) + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + tx := flowsdk.NewTransaction(). + SetScript(script(tt.argType)). + SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress) + + err := tx.AddArgument(tt.arg) + assert.NoError(t, err) + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + err = adapter.SendTransaction(context.Background(), *tx) + assert.NoError(t, err) + + result, err := b.ExecuteNextTransaction() + require.NoError(t, err) + AssertTransactionSucceeded(t, result) + + assert.Len(t, result.Logs, 1) + }) + } + + t.Run("Log", func(t *testing.T) { + b, adapter := setupTransactionTests(t) + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + script := []byte(` + transaction(x: Int) { + execute { + log(x * 6) + } + } + `) + + x := 7 + + tx := flowsdk.NewTransaction(). + SetScript(script). + SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress) + + err := tx.AddArgument(cadence.NewInt(x)) + assert.NoError(t, err) + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + err = adapter.SendTransaction(context.Background(), *tx) + assert.NoError(t, err) + + result, err := b.ExecuteNextTransaction() + require.NoError(t, err) + AssertTransactionSucceeded(t, result) + + require.Len(t, result.Logs, 1) + assert.Equal(t, "42", result.Logs[0]) + }) +} + +func TestSubmitTransaction_ProposerSequence(t *testing.T) { + + t.Parallel() + + t.Run("Valid transaction increases sequence number", func(t *testing.T) { + + t.Parallel() + + b, adapter := setupTransactionTests( + t, + emulator.WithStorageLimitEnabled(false), + ) + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + script := []byte(` + transaction { + prepare(signer: &Account) {} + } + `) + prevSeq := b.ServiceKey().SequenceNumber + + tx := flowsdk.NewTransaction(). + SetScript(script). + SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress). + AddAuthorizer(serviceAccountAddress) + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + err = adapter.SendTransaction(context.Background(), *tx) + assert.NoError(t, err) + + result, err := b.ExecuteNextTransaction() + assert.NoError(t, err) + AssertTransactionSucceeded(t, result) + + _, err = b.CommitBlock() + assert.NoError(t, err) + + tx1Result, err := adapter.GetTransactionResult(context.Background(), tx.ID()) + assert.NoError(t, err) + assert.Equal(t, flowsdk.TransactionStatusSealed, tx1Result.Status) + + assert.Equal(t, prevSeq+1, b.ServiceKey().SequenceNumber) + }) + + t.Run("Reverted transaction increases sequence number", func(t *testing.T) { + + t.Parallel() + + b, adapter := setupTransactionTests( + t, + emulator.WithStorageLimitEnabled(false), + ) + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + prevSeq := b.ServiceKey().SequenceNumber + script := []byte(` + transaction { + prepare(signer: &Account) {} + execute { panic("revert!") } + } + `) + + tx := flowsdk.NewTransaction(). + SetScript(script). + SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress). + AddAuthorizer(serviceAccountAddress) + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + err = adapter.SendTransaction(context.Background(), *tx) + assert.NoError(t, err) + + _, err = b.ExecuteNextTransaction() + assert.NoError(t, err) + + _, err = b.CommitBlock() + assert.NoError(t, err) + + tx1Result, err := adapter.GetTransactionResult(context.Background(), tx.ID()) + assert.NoError(t, err) + assert.Equal(t, prevSeq+1, b.ServiceKey().SequenceNumber) + assert.Equal(t, flowsdk.TransactionStatusSealed, tx1Result.Status) + assert.Len(t, tx1Result.Events, 0) + assert.IsType(t, &emulator.ExecutionError{}, tx1Result.Error) + }) +} + +func TestGetTransaction(t *testing.T) { + + t.Parallel() + + b, adapter := setupTransactionTests( + t, + emulator.WithStorageLimitEnabled(false), + ) + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + addTwoScript, _ := DeployAndGenerateAddTwoScript(t, adapter) + + tx1 := flowsdk.NewTransaction(). + SetScript([]byte(addTwoScript)). + SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress). + AddAuthorizer(serviceAccountAddress) + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx1.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + err = adapter.SendTransaction(context.Background(), *tx1) + assert.NoError(t, err) + + result, err := b.ExecuteNextTransaction() + assert.NoError(t, err) + AssertTransactionSucceeded(t, result) + + t.Run("Nonexistent", func(t *testing.T) { + _, err := adapter.GetTransaction(context.Background(), flowsdk.EmptyID) + if assert.Error(t, err) { + assert.IsType(t, &emulator.TransactionNotFoundError{}, err) + } + }) + + t.Run("Existent", func(t *testing.T) { + tx2, err := adapter.GetTransaction(context.Background(), tx1.ID()) + require.NoError(t, err) + + assert.Equal(t, tx1.ID(), tx2.ID()) + }) +} + +func TestGetTransactionResult(t *testing.T) { + + t.Parallel() + + b, adapter := setupTransactionTests( + t, + emulator.WithStorageLimitEnabled(false), + ) + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + addTwoScript, counterAddress := DeployAndGenerateAddTwoScript(t, adapter) + + tx := flowsdk.NewTransaction(). + SetScript([]byte(addTwoScript)). + SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress). + AddAuthorizer(serviceAccountAddress) + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + result, err := adapter.GetTransactionResult(context.Background(), tx.ID()) + assert.NoError(t, err) + assert.Equal(t, flowsdk.TransactionStatusUnknown, result.Status) + require.Empty(t, result.Events) + + err = adapter.SendTransaction(context.Background(), *tx) + assert.NoError(t, err) + + result, err = adapter.GetTransactionResult(context.Background(), tx.ID()) + assert.NoError(t, err) + assert.Equal(t, flowsdk.TransactionStatusPending, result.Status) + require.Empty(t, result.Events) + + _, err = b.ExecuteNextTransaction() + assert.NoError(t, err) + + result, err = adapter.GetTransactionResult(context.Background(), tx.ID()) + assert.NoError(t, err) + assert.Equal(t, flowsdk.TransactionStatusPending, result.Status) + require.Empty(t, result.Events) + + _, err = b.CommitBlock() + assert.NoError(t, err) + + result, err = adapter.GetTransactionResult(context.Background(), tx.ID()) + assert.NoError(t, err) + assert.Equal(t, flowsdk.TransactionStatusSealed, result.Status) + + require.Len(t, result.Events, 3) + + event1 := result.Events[0] + assert.Equal(t, tx.ID(), event1.TransactionID) + assert.Equal(t, "flow.StorageCapabilityControllerIssued", event1.Type) + assert.Equal(t, 0, event1.EventIndex) + + event2 := result.Events[1] + assert.Equal(t, tx.ID(), event2.TransactionID) + assert.Equal(t, "flow.CapabilityPublished", event2.Type) + assert.Equal(t, 1, event2.EventIndex) + + event3 := result.Events[2] + addr, _ := common.BytesToAddress(counterAddress.Bytes()) + location := common.AddressLocation{ + Address: addr, + Name: "Counting", + } + assert.Equal(t, tx.ID(), event3.TransactionID) + assert.Equal(t, + string(location.TypeID(nil, "Counting.CountIncremented")), + event3.Type, + ) + assert.Equal(t, 2, event3.EventIndex) + fields := cadence.FieldsMappedByName(event3.Value) + assert.Len(t, fields, 1) + assert.Equal(t, + cadence.NewInt(2), + fields["count"], + ) +} + +// TestGetTxByBlockIDMethods tests the GetTransactionByBlockID and GetTransactionResultByBlockID +// methods return the correct transaction and transaction result for a given block ID. +func TestGetTxByBlockIDMethods(t *testing.T) { + + t.Parallel() + + b, adapter := setupTransactionTests( + t, + emulator.WithStorageLimitEnabled(false), + ) + + const code = ` + transaction { + execute { + log("Hello, World!") + } + } + ` + + serviceKey := b.ServiceKey() + serviceAccountAddress := flowsdk.Address(serviceKey.Address) + + signer, err := serviceKey.Signer() + require.NoError(t, err) + + submittedTx := make([]*flowsdk.Transaction, 0) + + // submit 5 tx to be executed in a single block + for i := uint64(0); i < 5; i++ { + tx := flowsdk.NewTransaction(). + SetScript([]byte(code)). + SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, serviceKey.Index, serviceKey.SequenceNumber). + SetPayer(serviceAccountAddress). + AddAuthorizer(serviceAccountAddress) + + err = tx.SignEnvelope(serviceAccountAddress, serviceKey.Index, signer) + require.NoError(t, err) + + err = adapter.SendTransaction(context.Background(), *tx) + assert.NoError(t, err) + + // added to fix tx matching (nil vs empty slice) + tx.PayloadSignatures = []flow.TransactionSignature{} + + submittedTx = append(submittedTx, tx) + + // tx will be executed in the order they were submitted + serviceKey.SequenceNumber++ + } + + // execute the batch of transactions + block, expectedResults, err := b.ExecuteAndCommitBlock() + assert.NoError(t, err) + assert.Len(t, expectedResults, len(submittedTx)) + + results, err := adapter.GetTransactionResultsByBlockID(context.Background(), flowsdk.Identifier(block.ID())) + require.NoError(t, err) + assert.Len(t, results, len(submittedTx)) + + transactions, err := adapter.GetTransactionsByBlockID(context.Background(), flowsdk.Identifier(block.ID())) + require.NoError(t, err) + assert.Len(t, transactions, len(submittedTx)) + + // make sure the results and transactions returned match the transactions submitted, and are in + // the same order + for i, tx := range submittedTx { + assert.Equal(t, tx.ID(), transactions[i].ID()) + assert.Equal(t, submittedTx[i], transactions[i]) + + assert.Equal(t, tx.ID(), results[i].TransactionID) + assert.Equal(t, tx.ID(), expectedResults[i].TransactionID) + // note: expectedResults from ExecuteAndCommitBlock and results from GetTransactionResultsByBlockID + // use different representations. results is missing some data included in the flow.TransactionResult + // struct, so we can't compare them directly. + } +} + +const helloWorldContract = ` + access(all) contract HelloWorld { + + access(all) fun hello(): String { + return "Hello, World!" + } + } +` + +const callHelloTxTemplate = ` + import HelloWorld from 0x%s + transaction { + prepare() { + assert(HelloWorld.hello() == "Hello, World!") + } + } +` + +func TestHelloWorld_NewAccount(t *testing.T) { + + t.Parallel() + + accountKeys := test.AccountKeyGenerator() + + b, adapter := setupTransactionTests( + t, + emulator.WithStorageLimitEnabled(false), + ) + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + accountKey, accountSigner := accountKeys.NewWithSigner() + + contracts := []templates.Contract{ + { + Name: "HelloWorld", + Source: helloWorldContract, + }, + } + + createAccountTx, err := templates.CreateAccount( + []*flowsdk.AccountKey{accountKey}, + contracts, + serviceAccountAddress, + ) + require.NoError(t, err) + + createAccountTx.SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress) + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = createAccountTx.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + err = adapter.SendTransaction(context.Background(), *createAccountTx) + assert.NoError(t, err) + + result, err := b.ExecuteNextTransaction() + assert.NoError(t, err) + AssertTransactionSucceeded(t, result) + + _, err = b.CommitBlock() + assert.NoError(t, err) + + // createAccountTx status becomes TransactionStatusSealed + createAccountTxResult, err := adapter.GetTransactionResult(context.Background(), createAccountTx.ID()) + assert.NoError(t, err) + assert.Equal(t, flowsdk.TransactionStatusSealed, createAccountTxResult.Status) + + var newAccountAddress flowsdk.Address + for _, event := range createAccountTxResult.Events { + if event.Type != flowsdk.EventAccountCreated { + continue + } + accountCreatedEvent := flowsdk.AccountCreatedEvent(event) + newAccountAddress = accountCreatedEvent.Address() + break + } + + if newAccountAddress == flowsdk.EmptyAddress { + assert.Fail(t, "missing account created event") + } + + t.Logf("new account address: 0x%s", newAccountAddress.Hex()) + + account, err := adapter.GetAccount(context.Background(), newAccountAddress) + assert.NoError(t, err) + + assert.Equal(t, newAccountAddress, account.Address) + + // call hello world code + + accountKey = account.Keys[0] + + callHelloCode := []byte(fmt.Sprintf(callHelloTxTemplate, newAccountAddress.Hex())) + callHelloTx := flowsdk.NewTransaction(). + SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetScript(callHelloCode). + SetProposalKey(newAccountAddress, accountKey.Index, accountKey.SequenceNumber). + SetPayer(newAccountAddress) + + err = callHelloTx.SignEnvelope(newAccountAddress, accountKey.Index, accountSigner) + assert.NoError(t, err) + + err = adapter.SendTransaction(context.Background(), *callHelloTx) + assert.NoError(t, err) + + result, err = b.ExecuteNextTransaction() + assert.NoError(t, err) + AssertTransactionSucceeded(t, result) + + _, err = b.CommitBlock() + assert.NoError(t, err) +} + +func TestHelloWorld_UpdateAccount(t *testing.T) { + + t.Parallel() + + accountKeys := test.AccountKeyGenerator() + + b, adapter := setupTransactionTests( + t, + emulator.WithStorageLimitEnabled(false), + ) + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + accountKey, accountSigner := accountKeys.NewWithSigner() + _ = accountSigner + + contracts := []templates.Contract{ + { + Name: "HelloWorld", + Source: `access(all) contract HelloWorld {}`, + }, + } + + createAccountTx, err := templates.CreateAccount( + []*flowsdk.AccountKey{accountKey}, + contracts, + serviceAccountAddress, + ) + assert.NoError(t, err) + + createAccountTx. + SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress) + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = createAccountTx.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + err = adapter.SendTransaction(context.Background(), *createAccountTx) + assert.NoError(t, err) + + result, err := b.ExecuteNextTransaction() + assert.NoError(t, err) + AssertTransactionSucceeded(t, result) + + _, err = b.CommitBlock() + assert.NoError(t, err) + + // createAccountTx status becomes TransactionStatusSealed + createAccountTxResult, err := adapter.GetTransactionResult(context.Background(), createAccountTx.ID()) + assert.NoError(t, err) + assert.Equal(t, flowsdk.TransactionStatusSealed, createAccountTxResult.Status) + + var newAccountAddress flowsdk.Address + for _, event := range createAccountTxResult.Events { + if event.Type != flowsdk.EventAccountCreated { + continue + } + accountCreatedEvent := flowsdk.AccountCreatedEvent(event) + newAccountAddress = accountCreatedEvent.Address() + break + } + + if newAccountAddress == flowsdk.EmptyAddress { + assert.Fail(t, "missing account created event") + } + + t.Logf("new account address: 0x%s", newAccountAddress.Hex()) + + account, err := adapter.GetAccount(context.Background(), newAccountAddress) + assert.NoError(t, err) + + accountKey = account.Keys[0] + + updateAccountCodeTx := templates.UpdateAccountContract( + newAccountAddress, + templates.Contract{ + Name: "HelloWorld", + Source: helloWorldContract, + }, + ) + updateAccountCodeTx. + SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(newAccountAddress, accountKey.Index, accountKey.SequenceNumber). + SetPayer(newAccountAddress) + + err = updateAccountCodeTx.SignEnvelope(newAccountAddress, accountKey.Index, accountSigner) + assert.NoError(t, err) + + err = adapter.SendTransaction(context.Background(), *updateAccountCodeTx) + assert.NoError(t, err) + + result, err = b.ExecuteNextTransaction() + assert.NoError(t, err) + AssertTransactionSucceeded(t, result) + + _, err = b.CommitBlock() + assert.NoError(t, err) + + // call hello world code + + accountKey.SequenceNumber++ + + callHelloCode := []byte(fmt.Sprintf(callHelloTxTemplate, newAccountAddress.Hex())) + callHelloTx := flowsdk.NewTransaction(). + SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetScript(callHelloCode). + SetProposalKey(newAccountAddress, accountKey.Index, accountKey.SequenceNumber). + SetPayer(newAccountAddress) + + err = callHelloTx.SignEnvelope(newAccountAddress, accountKey.Index, accountSigner) + assert.NoError(t, err) + + err = adapter.SendTransaction(context.Background(), *callHelloTx) + assert.NoError(t, err) + + result, err = b.ExecuteNextTransaction() + assert.NoError(t, err) + AssertTransactionSucceeded(t, result) + + _, err = b.CommitBlock() + assert.NoError(t, err) +} + +func TestInfiniteTransaction(t *testing.T) { + + t.Parallel() + + const limit = 18 + + b, adapter := setupTransactionTests( + t, + emulator.WithStorageLimitEnabled(false), + emulator.WithTransactionMaxGasLimit(limit), + ) + + const code = ` + access(all) fun test() { + test() + } + + transaction { + execute { + test() + } + } + ` + + // Create a new account + + accountKeys := test.AccountKeyGenerator() + accountKey, signer := accountKeys.NewWithSigner() + accountAddress, err := adapter.CreateAccount(context.Background(), []*flowsdk.AccountKey{accountKey}, nil) + assert.NoError(t, err) + + // Sign the transaction using the new account. + // Do not test using the service account, + // as the computation limit is disabled for it + + tx := flowsdk.NewTransaction(). + SetScript([]byte(code)). + SetComputeLimit(limit). + SetProposalKey(accountAddress, 0, 0). + SetPayer(accountAddress) + + err = tx.SignEnvelope(accountAddress, 0, signer) + assert.NoError(t, err) + + // Submit tx + err = adapter.SendTransaction(context.Background(), *tx) + assert.NoError(t, err) + + // Execute tx + result, err := b.ExecuteNextTransaction() + assert.NoError(t, err) + + require.True(t, fvmerrors.IsComputationLimitExceededError(result.Error)) +} + +func TestTransactionExecutionLimit(t *testing.T) { + + t.Parallel() + + const code = ` + transaction { + execute { + var s: Int256 = 1024102410241024 + var i: Int256 = 0 + var a: Int256 = 7 + var b: Int256 = 5 + var c: Int256 = 2 + + while i < 150000 { + s = s * a + s = s / b + s = s / c + i = i + 1 + } + } + } + ` + + t.Run("ExceedingLimit", func(t *testing.T) { + + t.Parallel() + + const limit = 2000 + + b, adapter := setupTransactionTests( + t, + emulator.WithStorageLimitEnabled(false), + emulator.WithTransactionMaxGasLimit(limit), + ) + + // Create a new account + + accountKeys := test.AccountKeyGenerator() + accountKey, signer := accountKeys.NewWithSigner() + accountAddress, err := adapter.CreateAccount(context.Background(), []*flowsdk.AccountKey{accountKey}, nil) + assert.NoError(t, err) + + // Sign the transaction using the new account. + // Do not test using the service account, + // as the computation limit is disabled for it + + tx := flowsdk.NewTransaction(). + SetScript([]byte(code)). + SetComputeLimit(limit). + SetProposalKey(accountAddress, 0, 0). + SetPayer(accountAddress) + + err = tx.SignEnvelope(accountAddress, 0, signer) + assert.NoError(t, err) + + // Submit tx + err = adapter.SendTransaction(context.Background(), *tx) + assert.NoError(t, err) + + // Execute tx + result, err := b.ExecuteNextTransaction() + assert.NoError(t, err) + + require.True(t, fvmerrors.IsComputationLimitExceededError(result.Error)) + }) + + t.Run("SufficientLimit", func(t *testing.T) { + + t.Parallel() + + const limit = 19000 + + b, adapter := setupTransactionTests( + t, + emulator.WithStorageLimitEnabled(false), + emulator.WithTransactionMaxGasLimit(limit), + ) + + // Create a new account + + accountKeys := test.AccountKeyGenerator() + accountKey, signer := accountKeys.NewWithSigner() + accountAddress, err := adapter.CreateAccount(context.Background(), []*flowsdk.AccountKey{accountKey}, nil) + assert.NoError(t, err) + + // Sign the transaction using the new account. + // Do not test using the service account, + // as the computation limit is disabled for it + + tx := flowsdk.NewTransaction(). + SetScript([]byte(code)). + SetComputeLimit(limit). + SetProposalKey(accountAddress, 0, 0). + SetPayer(accountAddress) + + err = tx.SignEnvelope(accountAddress, 0, signer) + assert.NoError(t, err) + + // Submit tx + err = adapter.SendTransaction(context.Background(), *tx) + assert.NoError(t, err) + + // Execute tx + result, err := b.ExecuteNextTransaction() + assert.NoError(t, err) + assert.NoError(t, result.Error) + }) +} + +func TestSubmitTransactionWithCustomLogger(t *testing.T) { + + t.Parallel() + + var memlog bytes.Buffer + memlogWrite := io.Writer(&memlog) + logger := zerolog.New(memlogWrite).Level(zerolog.DebugLevel) + + b, adapter := setupTransactionTests( + t, + emulator.WithStorageLimitEnabled(false), + emulator.WithLogger(logger), + emulator.WithTransactionFeesEnabled(true), + ) + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + addTwoScript, _ := DeployAndGenerateAddTwoScript(t, adapter) + + tx1 := flowsdk.NewTransaction(). + SetScript([]byte(addTwoScript)). + SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress). + AddAuthorizer(serviceAccountAddress) + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx1.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + // Submit tx1 + err = adapter.SendTransaction(context.Background(), *tx1) + assert.NoError(t, err) + + // Execute tx1 + result, err := b.ExecuteNextTransaction() + assert.NoError(t, err) + AssertTransactionSucceeded(t, result) + + _, err = b.CommitBlock() + assert.NoError(t, err) + + // tx1 status becomes TransactionStatusSealed + tx1Result, err := adapter.GetTransactionResult(context.Background(), tx1.ID()) + assert.NoError(t, err) + assert.Equal(t, flowsdk.TransactionStatusSealed, tx1Result.Status) + + var meter Meter + scanner := bufio.NewScanner(&memlog) + for scanner.Scan() { + txt := scanner.Text() + if strings.Contains(txt, "transaction execution data") { + err = json.Unmarshal([]byte(txt), &meter) + } + } + + assert.NoError(t, err) + assert.Greater(t, meter.LedgerInteractionUsed, 0) + assert.Greater(t, meter.ComputationUsed, 0) + assert.Greater(t, meter.MemoryEstimate, 0) + assert.Greater(t, len(meter.ComputationIntensities), 0) + assert.Greater(t, len(meter.MemoryIntensities), 0) + +} + +type Meter struct { + LedgerInteractionUsed int `json:"ledgerInteractionUsed"` + ComputationUsed int `json:"computationUsed"` + MemoryEstimate int `json:"memoryEstimate"` + ComputationIntensities MeteredComputationIntensities `json:"computationIntensities"` + MemoryIntensities MeteredMemoryIntensities `json:"memoryIntensities"` +} + +type MeteredComputationIntensities map[common.ComputationKind]uint + +type MeteredMemoryIntensities map[common.MemoryKind]uint + +func IncrementHelper( + t *testing.T, + b emulator.Emulator, + adapter *emulator.SDKAdapter, + counterAddress flowsdk.Address, + addTwoScript string, + expected int, + expectSetup bool, +) { + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + tx := flowsdk.NewTransaction(). + SetScript([]byte(addTwoScript)). + SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress). + AddAuthorizer(serviceAccountAddress) + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + result, err := adapter.GetTransactionResult(context.Background(), tx.ID()) + assert.NoError(t, err) + assert.Equal(t, flowsdk.TransactionStatusUnknown, result.Status) + require.Empty(t, result.Events) + + err = adapter.SendTransaction(context.Background(), *tx) + assert.NoError(t, err) + + result, err = adapter.GetTransactionResult(context.Background(), tx.ID()) + assert.NoError(t, err) + assert.Equal(t, flowsdk.TransactionStatusPending, result.Status) + require.Empty(t, result.Events) + + _, err = b.ExecuteNextTransaction() + assert.NoError(t, err) + + result, err = adapter.GetTransactionResult(context.Background(), tx.ID()) + assert.NoError(t, err) + assert.Equal(t, flowsdk.TransactionStatusPending, result.Status) + require.Empty(t, result.Events) + + _, err = b.CommitBlock() + assert.NoError(t, err) + + result, err = adapter.GetTransactionResult(context.Background(), tx.ID()) + assert.NoError(t, err) + assert.Equal(t, flowsdk.TransactionStatusSealed, result.Status) + + var expectedEventIndex int + if expectSetup { + require.Len(t, result.Events, 3) + + event1 := result.Events[0] + assert.Equal(t, tx.ID(), event1.TransactionID) + assert.Equal(t, "flow.StorageCapabilityControllerIssued", event1.Type) + assert.Equal(t, 0, event1.EventIndex) + + event2 := result.Events[1] + assert.Equal(t, tx.ID(), event2.TransactionID) + assert.Equal(t, "flow.CapabilityPublished", event2.Type) + assert.Equal(t, 1, event2.EventIndex) + + expectedEventIndex = 2 + } else { + require.Len(t, result.Events, 1) + expectedEventIndex = 0 + } + incrementedEvent := result.Events[expectedEventIndex] + + addr, _ := common.BytesToAddress(counterAddress.Bytes()) + location := common.AddressLocation{ + Address: addr, + Name: "Counting", + } + assert.Equal(t, tx.ID(), incrementedEvent.TransactionID) + assert.Equal(t, + string(location.TypeID(nil, "Counting.CountIncremented")), + incrementedEvent.Type, + ) + assert.Equal(t, expectedEventIndex, incrementedEvent.EventIndex) + fields := cadence.FieldsMappedByName(incrementedEvent.Value) + assert.Len(t, fields, 1) + assert.Equal(t, + cadence.NewInt(expected), + fields["count"], + ) +} + +// TestTransactionWithCadenceRandom checks Cadence's random function works +// within a transaction +func TestTransactionWithCadenceRandom(t *testing.T) { + b, adapter := setupTransactionTests(t) + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + code := ` + transaction { + prepare() { + assert(revertibleRandom() >= 0) + } + } + ` + callRandomTx := flowsdk.NewTransaction(). + SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetScript([]byte(code)). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress) + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = callRandomTx.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + err = adapter.SendTransaction(context.Background(), *callRandomTx) + assert.NoError(t, err) + + result, err := b.ExecuteNextTransaction() + assert.NoError(t, err) + AssertTransactionSucceeded(t, result) + + _, err = b.CommitBlock() + assert.NoError(t, err) +} + +func TestEVMTransaction(t *testing.T) { + serviceAddr := flowgo.Emulator.Chain().ServiceAddress() + code := []byte(fmt.Sprintf( + ` + import EVM from %s + + transaction(bytes: [UInt8; 20]) { + execute { + let addr = EVM.EVMAddress(bytes: bytes) + log(addr) + } + } + `, + serviceAddr.HexWithPrefix(), + )) + + b, adapter := setupTransactionTests(t) + serviceAccountAddress := flowsdk.Address(b.ServiceKey().Address) + + // generate random address + genArr := make([]cadence.Value, 20) + for i := range genArr { + genArr[i] = cadence.UInt8(i) + } + addressBytesArray := cadence.NewArray(genArr).WithType(stdlib.EVMAddressBytesCadenceType) + + tx := flowsdk.NewTransaction(). + SetScript(code). + SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit). + SetProposalKey(serviceAccountAddress, b.ServiceKey().Index, b.ServiceKey().SequenceNumber). + SetPayer(serviceAccountAddress) + + err := tx.AddArgument(addressBytesArray) + assert.NoError(t, err) + + signer, err := b.ServiceKey().Signer() + require.NoError(t, err) + + err = tx.SignEnvelope(serviceAccountAddress, b.ServiceKey().Index, signer) + require.NoError(t, err) + + err = adapter.SendTransaction(context.Background(), *tx) + assert.NoError(t, err) + + result, err := b.ExecuteNextTransaction() + require.NoError(t, err) + AssertTransactionSucceeded(t, result) + + require.Len(t, result.Logs, 1) + require.Equal(t, result.Logs[0], fmt.Sprintf("A.%s.EVM.EVMAddress(bytes: %s)", serviceAddr, addressBytesArray.String())) +} diff --git a/integration/internal/emulator/tests/vm_test.go b/integration/internal/emulator/tests/vm_test.go new file mode 100644 index 00000000000..5e378589ca0 --- /dev/null +++ b/integration/internal/emulator/tests/vm_test.go @@ -0,0 +1,82 @@ +/* + * Flow Emulator + * + * Copyright Flow Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tests_test + +import ( + "testing" + + "github.com/onflow/flow/protobuf/go/flow/entities" + "github.com/stretchr/testify/assert" + + "github.com/onflow/flow-go-sdk/test" + + "github.com/onflow/flow-go/fvm" + "github.com/onflow/flow-go/integration/internal/emulator" + flowgo "github.com/onflow/flow-go/model/flow" +) + +func TestVm(t *testing.T) { + + t.Parallel() + + test := func(eventEncodingVersion entities.EventEncodingVersion) { + t.Run(eventEncodingVersion.String(), func(t *testing.T) { + t.Parallel() + t.Run("should be able to convert", func(t *testing.T) { + + t.Parallel() + + idGenerator := test.IdentifierGenerator() + + eventGenerator := test.EventGenerator(eventEncodingVersion) + event1, err := emulator.SDKEventToFlow(eventGenerator.New()) + assert.NoError(t, err) + + event2, err := emulator.SDKEventToFlow(eventGenerator.New()) + assert.NoError(t, err) + + txnId := flowgo.Identifier(idGenerator.New()) + output := fvm.ProcedureOutput{ + Logs: []string{"TestLog1", "TestLog2"}, + Events: []flowgo.Event{event1, event2}, + ComputationUsed: 5, + MemoryEstimate: 1211, + Err: nil, + } + + tr, err := emulator.VMTransactionResultToEmulator(txnId, output) + assert.NoError(t, err) + + assert.Equal(t, txnId, flowgo.Identifier(tr.TransactionID)) + assert.Equal(t, output.Logs, tr.Logs) + + flowEvents, err := emulator.FlowEventsToSDK(output.Events) + assert.NoError(t, err) + assert.Equal(t, flowEvents, tr.Events) + + assert.Equal(t, output.ComputationUsed, tr.ComputationUsed) + assert.Equal(t, output.MemoryEstimate, tr.MemoryEstimate) + assert.Equal(t, output.Err, tr.Error) + }) + }) + } + + test(entities.EventEncodingVersion_JSON_CDC_V0) + test(entities.EventEncodingVersion_CCF_V0) +} diff --git a/integration/internal/emulator/utils/unittest/fixtures.go b/integration/internal/emulator/utils/unittest/fixtures.go new file mode 100644 index 00000000000..70efad250ca --- /dev/null +++ b/integration/internal/emulator/utils/unittest/fixtures.go @@ -0,0 +1,61 @@ +/* + * Flow Emulator + * + * Copyright Flow Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package unittest + +import ( + "github.com/onflow/flow/protobuf/go/flow/entities" + + "github.com/onflow/flow-go-sdk/test" + + emulator "github.com/onflow/flow-go/integration/internal/emulator" + flowgo "github.com/onflow/flow-go/model/flow" +) + +func TransactionFixture() flowgo.TransactionBody { + return *emulator.SDKTransactionToFlow(*test.TransactionGenerator().New()) +} + +func StorableTransactionResultFixture(eventEncodingVersion entities.EventEncodingVersion) emulator.StorableTransactionResult { + events := test.EventGenerator(eventEncodingVersion) + + eventA, _ := emulator.SDKEventToFlow(events.New()) + eventB, _ := emulator.SDKEventToFlow(events.New()) + + return emulator.StorableTransactionResult{ + ErrorCode: 42, + ErrorMessage: "foo", + Logs: []string{"a", "b", "c"}, + Events: []flowgo.Event{ + eventA, + eventB, + }, + } +} + +func FullCollectionFixture(n int) flowgo.Collection { + transactions := make([]*flowgo.TransactionBody, n) + for i := 0; i < n; i++ { + tx := TransactionFixture() + transactions[i] = &tx + } + + return flowgo.Collection{ + Transactions: transactions, + } +} diff --git a/integration/localnet/builder/bootstrap.go b/integration/localnet/builder/bootstrap.go index 42155923c54..27749a39f99 100644 --- a/integration/localnet/builder/bootstrap.go +++ b/integration/localnet/builder/bootstrap.go @@ -395,14 +395,11 @@ func prepareExecutionService(container testnet.ContainerConfig, i int, n int) Se panic(err) } - enableNewIngestionEngine := true - service.Command = append(service.Command, "--triedir=/trie", fmt.Sprintf("--rpc-addr=%s:%s", container.ContainerName, testnet.GRPCPort), fmt.Sprintf("--cadence-tracing=%t", cadenceTracing), fmt.Sprintf("--extensive-tracing=%t", extesiveTracing), - fmt.Sprintf("--enable-new-ingestion-engine=%v", enableNewIngestionEngine), "--execution-data-dir=/data/execution-data", "--chunk-data-pack-dir=/data/chunk-data-pack", ) @@ -459,8 +456,8 @@ func prepareObserverService(i int, observerName string, agPublicKey string) Serv service := defaultService(observerName, DefaultObserverRole, dataDir, profilerDir, i) service.Command = append(service.Command, - fmt.Sprintf("--bootstrap-node-addresses=%s:%s", testnet.PrimaryAN, testnet.PublicNetworkPort), - fmt.Sprintf("--bootstrap-node-public-keys=%s", agPublicKey), + fmt.Sprintf("--observer-mode-bootstrap-node-addresses=%s:%s", testnet.PrimaryAN, testnet.PublicNetworkPort), + fmt.Sprintf("--observer-mode-bootstrap-node-public-keys=%s", agPublicKey), fmt.Sprintf("--upstream-node-addresses=%s:%s", testnet.PrimaryAN, testnet.GRPCSecurePort), fmt.Sprintf("--upstream-node-public-keys=%s", agPublicKey), fmt.Sprintf("--observer-networking-key-path=/bootstrap/private-root-information/%s_key", observerName), diff --git a/integration/localnet/conf/tempo-local.yaml b/integration/localnet/conf/tempo-local.yaml index d2f4089bbf8..fd453459942 100644 --- a/integration/localnet/conf/tempo-local.yaml +++ b/integration/localnet/conf/tempo-local.yaml @@ -41,7 +41,7 @@ storage: index_downsample_bytes: 1000 # number of bytes per index record encoding: zstd # block encoding/compression. options: none, gzip, lz4-64k, lz4-256k, lz4-1M, lz4, snappy, zstd, s2 wal: - path: /tmp/tempo/wal # where to store the the wal locally + path: /tmp/tempo/wal # where to store the wal locally encoding: snappy # wal encoding/compression. options: none, gzip, lz4-64k, lz4-256k, lz4-1M, lz4, snappy, zstd, s2 local: path: /tmp/tempo/blocks @@ -50,4 +50,4 @@ storage: queue_depth: 10000 overrides: - metrics_generator_processors: [service-graphs, span-metrics] \ No newline at end of file + metrics_generator_processors: [service-graphs, span-metrics] diff --git a/integration/testnet/network.go b/integration/testnet/network.go index 40c88295073..a84a3c529b0 100644 --- a/integration/testnet/network.go +++ b/integration/testnet/network.go @@ -31,6 +31,7 @@ import ( "github.com/onflow/cadence" "github.com/onflow/flow-go-sdk/crypto" + "github.com/onflow/flow-go/cmd/bootstrap/dkg" "github.com/onflow/flow-go/cmd/bootstrap/run" "github.com/onflow/flow-go/cmd/bootstrap/utils" @@ -765,8 +766,8 @@ func (net *FlowNetwork) AddObserver(t *testing.T, conf ObserverConfig) *Containe fmt.Sprintf("--secretsdir=%s", DefaultFlowSecretsDBDir), fmt.Sprintf("--profiler-dir=%s", DefaultProfilerDir), fmt.Sprintf("--loglevel=%s", conf.LogLevel.String()), - fmt.Sprintf("--bootstrap-node-addresses=%s", accessNode.ContainerAddr(PublicNetworkPort)), - fmt.Sprintf("--bootstrap-node-public-keys=%s", accessPublicKey), + fmt.Sprintf("--observer-mode-bootstrap-node-addresses=%s", accessNode.ContainerAddr(PublicNetworkPort)), + fmt.Sprintf("--observer-mode-bootstrap-node-public-keys=%s", accessPublicKey), fmt.Sprintf("--upstream-node-addresses=%s", accessNode.ContainerAddr(GRPCSecurePort)), fmt.Sprintf("--upstream-node-public-keys=%s", accessPublicKey), fmt.Sprintf("--observer-networking-key-path=%s/private-root-information/%s_key", DefaultBootstrapDir, conf.ContainerName), diff --git a/integration/tests/access/cohort1/access_api_test.go b/integration/tests/access/cohort1/access_api_test.go index f070c1a4140..6470ced35b2 100644 --- a/integration/tests/access/cohort1/access_api_test.go +++ b/integration/tests/access/cohort1/access_api_test.go @@ -319,7 +319,7 @@ func (s *AccessAPISuite) TestSendAndSubscribeTransactionStatuses() { }) s.Require().NoError(err) - expectedCounter := uint64(1) + expectedCounter := uint64(0) lastReportedTxStatus := entities.TransactionStatus_UNKNOWN var txID sdk.Identifier diff --git a/integration/tests/access/cohort2/observer_indexer_enabled_test.go b/integration/tests/access/cohort2/observer_indexer_enabled_test.go index 43f784669bc..cc2709f9780 100644 --- a/integration/tests/access/cohort2/observer_indexer_enabled_test.go +++ b/integration/tests/access/cohort2/observer_indexer_enabled_test.go @@ -17,6 +17,7 @@ import ( sdk "github.com/onflow/flow-go-sdk" sdkcrypto "github.com/onflow/flow-go-sdk/crypto" "github.com/onflow/flow-go-sdk/templates" + "github.com/onflow/flow-go/engine/access/rpc/backend" "github.com/onflow/flow-go/engine/common/rpc/convert" "github.com/onflow/flow-go/integration/testnet" diff --git a/integration/tests/access/cohort3/access_circuit_breaker_test.go b/integration/tests/access/cohort3/access_circuit_breaker_test.go index 772a56e6c8c..8cb745bd569 100644 --- a/integration/tests/access/cohort3/access_circuit_breaker_test.go +++ b/integration/tests/access/cohort3/access_circuit_breaker_test.go @@ -16,6 +16,7 @@ import ( sdk "github.com/onflow/flow-go-sdk" sdkcrypto "github.com/onflow/flow-go-sdk/crypto" "github.com/onflow/flow-go-sdk/templates" + "github.com/onflow/flow-go/integration/testnet" "github.com/onflow/flow-go/integration/tests/lib" "github.com/onflow/flow-go/model/flow" diff --git a/integration/tests/access/cohort3/access_store_tx_error_messages_test.go b/integration/tests/access/cohort3/access_store_tx_error_messages_test.go new file mode 100644 index 00000000000..19809e853db --- /dev/null +++ b/integration/tests/access/cohort3/access_store_tx_error_messages_test.go @@ -0,0 +1,249 @@ +package cohort3 + +import ( + "context" + "fmt" + "testing" + "time" + + "github.com/rs/zerolog" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + + sdk "github.com/onflow/flow-go-sdk" + sdkcrypto "github.com/onflow/flow-go-sdk/crypto" + "github.com/onflow/flow-go-sdk/templates" + + "github.com/onflow/flow-go/integration/client" + "github.com/onflow/flow-go/integration/convert" + "github.com/onflow/flow-go/integration/testnet" + "github.com/onflow/flow-go/integration/tests/lib" + "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/module/metrics" + "github.com/onflow/flow-go/storage/badger" +) + +const maxReceiptHeightMetric = "access_ingestion_max_receipt_height" + +func TestAccessStoreTxErrorMessages(t *testing.T) { + suite.Run(t, new(AccessStoreTxErrorMessagesSuite)) +} + +// AccessStoreTxErrorMessagesSuite tests the access for storing transaction error messages. +type AccessStoreTxErrorMessagesSuite struct { + suite.Suite + + log zerolog.Logger + + // root context for the current test + ctx context.Context + cancel context.CancelFunc + + net *testnet.FlowNetwork + + accessContainerName string +} + +func (s *AccessStoreTxErrorMessagesSuite) TearDownTest() { + s.log.Info().Msg("================> Start TearDownTest") + s.net.Remove() + s.cancel() + s.log.Info().Msg("================> Finish TearDownTest") +} + +// SetupTest sets up the test suite by starting the network. +// The access are started with correct parameters and store transaction error messages. +func (s *AccessStoreTxErrorMessagesSuite) SetupTest() { + defaultAccess := testnet.NewNodeConfig( + flow.RoleAccess, + testnet.WithLogLevel(zerolog.FatalLevel), + ) + + s.accessContainerName = "access_2" + storeTxAccess := testnet.NewNodeConfig( + flow.RoleAccess, + testnet.WithLogLevel(zerolog.InfoLevel), + testnet.WithAdditionalFlagf("--store-tx-result-error-messages=true"), + testnet.WithMetricsServer(), + ) + + consensusConfigs := []func(config *testnet.NodeConfig){ + // `cruise-ctl-fallback-proposal-duration` is set to 250ms instead to of 100ms + // to purposely slow down the block rate. This is needed since the crypto module + // update providing faster BLS operations. + testnet.WithAdditionalFlag("--cruise-ctl-fallback-proposal-duration=250ms"), + testnet.WithAdditionalFlagf("--required-verification-seal-approvals=%d", 1), + testnet.WithAdditionalFlagf("--required-construction-seal-approvals=%d", 1), + testnet.WithLogLevel(zerolog.FatalLevel), + } + + nodeConfigs := []testnet.NodeConfig{ + testnet.NewNodeConfig(flow.RoleCollection, testnet.WithLogLevel(zerolog.FatalLevel)), + testnet.NewNodeConfig(flow.RoleCollection, testnet.WithLogLevel(zerolog.FatalLevel)), + testnet.NewNodeConfig(flow.RoleExecution, testnet.WithLogLevel(zerolog.FatalLevel)), + testnet.NewNodeConfig(flow.RoleExecution, testnet.WithLogLevel(zerolog.FatalLevel)), + testnet.NewNodeConfig(flow.RoleConsensus, consensusConfigs...), + testnet.NewNodeConfig(flow.RoleConsensus, consensusConfigs...), + testnet.NewNodeConfig(flow.RoleConsensus, consensusConfigs...), + testnet.NewNodeConfig(flow.RoleVerification, testnet.WithLogLevel(zerolog.FatalLevel)), + + defaultAccess, + storeTxAccess, + } + + // prepare the network + conf := testnet.NewNetworkConfig("access_store_tx_error_messages_test", nodeConfigs) + s.net = testnet.PrepareFlowNetwork(s.T(), conf, flow.Localnet) + + // start the network + s.ctx, s.cancel = context.WithCancel(context.Background()) + + s.net.Start(s.ctx) +} + +// TestAccessStoreTxErrorMessages verifies that transaction result error messages +// are stored correctly in the database by sending a transaction, generating an error, +// and checking if the error message is properly stored and retrieved from the database. +func (s *AccessStoreTxErrorMessagesSuite) TestAccessStoreTxErrorMessages() { + // Create and send a transaction that will result in an error. + txResult := s.createAndSendTxWithTxError() + + // Wait until execution receipts are handled, transaction error messages are stored. + s.Eventually(func() bool { + value, err := s.getMaxReceiptHeight(s.accessContainerName) + return err == nil && value > txResult.BlockHeight + }, 60*time.Second, 1*time.Second) + + // Stop the network containers before checking the results. + s.net.StopContainers() + + txResults := []*sdk.TransactionResult{txResult} + txErrorMessages := s.fetchTxErrorMessages(txResults, s.accessContainerName) + s.verifyTxErrorMessage(txResults, txErrorMessages) +} + +// TestBackfillTxErrorMessages verifies that transaction error messages are backfilled correctly +// by creating a transaction that results in an error, running the backfill command, and checking +// if the error message is stored and retrieved from the database. +func (s *AccessStoreTxErrorMessagesSuite) TestBackfillTxErrorMessages() { + // Create and send a transactions that will result in an error. + transactionCount := 5 + txResults := make([]*sdk.TransactionResult, transactionCount) + for i := 0; i < transactionCount; i++ { + txResults[i] = s.createAndSendTxWithTxError() + } + + serverAddr := fmt.Sprintf("localhost:%s", s.net.ContainerByName(s.accessContainerName).Port(testnet.AdminPort)) + adminClient := client.NewAdminClient(serverAddr) + + startHeight := 1 + endHeight := txResults[len(txResults)-1].BlockHeight // last tx result block height + data := map[string]interface{}{"start-height": startHeight, "endHeight": endHeight} + // executes the backfill command for transaction error messages + _, err := adminClient.RunCommand(context.Background(), "backfill-tx-error-messages", data) + require.NoError(s.T(), err) + + // Stop the network containers before checking the results. + s.net.StopContainers() + + txErrorMessages := s.fetchTxErrorMessages(txResults, s.accessContainerName) + s.verifyTxErrorMessage(txResults, txErrorMessages) +} + +// createAndSendTxWithTxError creates and sends a transaction that will result in an error. +// This function creates a new account, causing an error during execution. +func (s *AccessStoreTxErrorMessagesSuite) createAndSendTxWithTxError() *sdk.TransactionResult { + // prepare environment to create a new account + serviceAccountClient, err := s.net.ContainerByName(testnet.PrimaryAN).TestnetClient() + s.Require().NoError(err) + + latestBlockID, err := serviceAccountClient.GetLatestBlockID(s.ctx) + s.Require().NoError(err) + + // create new account to deploy Counter to + accountPrivateKey := lib.RandomPrivateKey() + + accountKey := sdk.NewAccountKey(). + FromPrivateKey(accountPrivateKey). + SetHashAlgo(sdkcrypto.SHA3_256). + SetWeight(sdk.AccountKeyWeightThreshold) + + serviceAddress := sdk.Address(serviceAccountClient.Chain.ServiceAddress()) + + // Generate the account creation transaction + createAccountTx, err := templates.CreateAccount( + []*sdk.AccountKey{accountKey}, + []templates.Contract{}, serviceAddress) + s.Require().NoError(err) + + // Generate the account creation transaction + createAccountTx. + SetReferenceBlockID(sdk.Identifier(latestBlockID)). + SetProposalKey(serviceAddress, 1, serviceAccountClient.GetAndIncrementSeqNumber()). + SetPayer(serviceAddress). + SetComputeLimit(9999) + + // Sign and send the transaction. + err = serviceAccountClient.SignAndSendTransaction(s.ctx, createAccountTx) + s.Require().NoError(err) + + // Wait for the transaction to be sealed and return the transaction result. + accountCreationTxRes, err := serviceAccountClient.WaitForSealed(s.ctx, createAccountTx.ID()) + s.Require().NoError(err) + + return accountCreationTxRes +} + +// getMaxReceiptHeight retrieves the maximum receipt height for a given container by +// querying the metrics endpoint. This is used to confirm that the transaction receipts +// have been processed. +func (s *AccessStoreTxErrorMessagesSuite) getMaxReceiptHeight(containerName string) (uint64, error) { + node := s.net.ContainerByName(containerName) + metricsURL := fmt.Sprintf("http://0.0.0.0:%s/metrics", node.Port(testnet.MetricsPort)) + values := s.net.GetMetricFromContainer(s.T(), containerName, metricsURL, maxReceiptHeightMetric) + + // If no values are found in the metrics, return an error. + if len(values) == 0 { + return 0, fmt.Errorf("no values found") + } + + // Return the first value found as the max receipt height. + return uint64(values[0].GetGauge().GetValue()), nil +} + +// fetchTxErrorMessage retrieves the stored transaction error message for a given transaction result. +func (s *AccessStoreTxErrorMessagesSuite) fetchTxErrorMessages(txResults []*sdk.TransactionResult, containerName string) []*flow.TransactionResultErrorMessage { + accessNode := s.net.ContainerByName(containerName) + anDB, err := accessNode.DB() + require.NoError(s.T(), err, "could not open db") + + metrics := metrics.NewNoopCollector() + anTxErrorMessages := badger.NewTransactionResultErrorMessages(metrics, anDB, badger.DefaultCacheSize) + + txResultErrorMessages := make([]*flow.TransactionResultErrorMessage, len(txResults)) + for i, txResult := range txResults { + txBlockID := convert.IDFromSDK(txResult.BlockID) + txID := convert.IDFromSDK(txResult.TransactionID) + + errMsgResult, err := anTxErrorMessages.ByBlockIDTransactionID(txBlockID, txID) + s.Require().NoError(err) + + txResultErrorMessages[i] = errMsgResult + } + + return txResultErrorMessages +} + +// verifyTxErrorMessage compares the expected and retrieved error messages to verify accuracy. +func (s *AccessStoreTxErrorMessagesSuite) verifyTxErrorMessage(txResults []*sdk.TransactionResult, errMsgResults []*flow.TransactionResultErrorMessage) { + s.Require().Equal(len(txResults), len(errMsgResults)) + + for i, txResult := range txResults { + expectedTxResultErrorMessage := txResult.Error.Error() + expectedTxID := convert.IDFromSDK(txResult.TransactionID) + + s.Require().Equal(expectedTxID, errMsgResults[i].TransactionID) + s.Require().Equal(expectedTxResultErrorMessage, errMsgResults[i].ErrorMessage) + } + +} diff --git a/integration/tests/access/cohort3/rest_state_stream_test.go b/integration/tests/access/cohort3/rest_state_stream_test.go index 6e22e7d0390..4526c96e83e 100644 --- a/integration/tests/access/cohort3/rest_state_stream_test.go +++ b/integration/tests/access/cohort3/rest_state_stream_test.go @@ -15,7 +15,7 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" - "github.com/onflow/flow-go/engine/access/rest/request" + "github.com/onflow/flow-go/engine/access/rest/http/request" "github.com/onflow/flow-go/engine/access/state_stream/backend" "github.com/onflow/flow-go/engine/common/rpc/convert" "github.com/onflow/flow-go/integration/testnet" diff --git a/integration/tests/collection/recovery_test.go b/integration/tests/collection/recovery_test.go index 6d1309df18c..d1812e83c7a 100644 --- a/integration/tests/collection/recovery_test.go +++ b/integration/tests/collection/recovery_test.go @@ -8,6 +8,7 @@ import ( "github.com/stretchr/testify/suite" client "github.com/onflow/flow-go-sdk/access/grpc" + "github.com/onflow/flow-go/integration/convert" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/utils/unittest" diff --git a/integration/tests/execution/transaction_metrics_test.go b/integration/tests/execution/transaction_metrics_test.go new file mode 100644 index 00000000000..2e0ba03df5e --- /dev/null +++ b/integration/tests/execution/transaction_metrics_test.go @@ -0,0 +1,116 @@ +package execution + +import ( + "bytes" + "context" + "testing" + + "github.com/onflow/flow/protobuf/go/flow/execution" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + + "github.com/onflow/flow-go/integration/testnet" + + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + + sdk "github.com/onflow/flow-go-sdk" + + "github.com/onflow/flow-go/integration/tests/lib" +) + +func TestTransactionMetrics(t *testing.T) { + suite.Run(t, new(TransactionMetricsSuite)) +} + +type TransactionMetricsSuite struct { + Suite +} + +func (s *TransactionMetricsSuite) TestTransactionMetrics() { + accessClient := s.AccessClient() + + // wait for next height finalized (potentially first height), called blockA + currentFinalized := s.BlockState.HighestFinalizedHeight() + blockA := s.BlockState.WaitForHighestFinalizedProgress(s.T(), currentFinalized) + s.T().Logf("got blockA height %v ID %v\n", blockA.Header.Height, blockA.Header.ID()) + + // send transaction + tx, err := accessClient.DeployContract(context.Background(), sdk.Identifier(s.net.Root().ID()), lib.CounterContract) + require.NoError(s.T(), err, "could not deploy counter") + + txres, err := accessClient.WaitForExecuted(context.Background(), tx.ID()) + require.NoError(s.T(), err, "could not wait for tx to be executed") + require.NoError(s.T(), txres.Error) + + client, closeClient := s.getClient() + defer func() { + _ = closeClient() + }() + + result, err := client.GetTransactionExecutionMetricsAfter( + context.Background(), + &execution.GetTransactionExecutionMetricsAfterRequest{ + BlockHeight: 0, + }, + ) + + require.NoError(s.T(), err, "could not get transaction execution metrics") + require.NotNil(s.T(), result.Results) + // there should be at least some results, due to each block having at least 1 transaction + require.Greater(s.T(), len(result.Results), 10) + + latestBlockResult := uint64(0) + for _, result := range result.Results { + if result.BlockHeight > latestBlockResult { + latestBlockResult = result.BlockHeight + } + } + + // send another transaction + tx, err = accessClient.UpdateContract(context.Background(), sdk.Identifier(s.net.Root().ID()), lib.CounterContract) + require.NoError(s.T(), err, "could not deploy counter") + + txres, err = accessClient.WaitForExecuted(context.Background(), tx.ID()) + require.NoError(s.T(), err, "could not wait for tx to be executed") + require.NoError(s.T(), txres.Error) + + result, err = client.GetTransactionExecutionMetricsAfter( + context.Background(), + &execution.GetTransactionExecutionMetricsAfterRequest{ + BlockHeight: latestBlockResult, + }, + ) + + require.NoError(s.T(), err, "could not get transaction execution metrics") + // there could be only 1 block since the last time + require.Greater(s.T(), len(result.Results), 0) + + transactionExists := false + for _, result := range result.Results { + for _, transaction := range result.Transactions { + if bytes.Equal(transaction.TransactionId, tx.ID().Bytes()) { + transactionExists = true + + // check that the transaction metrics are not 0 + require.Greater(s.T(), transaction.ExecutionTime, uint64(0)) + require.Greater(s.T(), len(transaction.ExecutionEffortWeights), 0) + } + } + require.Less(s.T(), latestBlockResult, result.BlockHeight) + + } + require.True(s.T(), transactionExists) +} + +func (s *TransactionMetricsSuite) getClient() (execution.ExecutionAPIClient, func() error) { + + exe1ID := s.net.ContainerByID(s.exe1ID) + addr := exe1ID.Addr(testnet.GRPCPort) + + conn, err := grpc.Dial(addr, grpc.WithTransportCredentials(insecure.NewCredentials())) + require.NoError(s.T(), err, "could not create execution client") + + grpcClient := execution.NewExecutionAPIClient(conn) + return grpcClient, conn.Close +} diff --git a/integration/utils/emulator_client.go b/integration/utils/emulator_client.go index 8d42e1388fd..6d89ccf45ac 100644 --- a/integration/utils/emulator_client.go +++ b/integration/utils/emulator_client.go @@ -6,25 +6,25 @@ import ( "github.com/onflow/cadence" jsoncdc "github.com/onflow/cadence/encoding/json" - "github.com/onflow/flow-emulator/adapters" - emulator "github.com/onflow/flow-emulator/emulator" "github.com/rs/zerolog" sdk "github.com/onflow/flow-go-sdk" "github.com/onflow/flow-go-sdk/templates" + + emulator "github.com/onflow/flow-go/integration/internal/emulator" "github.com/onflow/flow-go/model/flow" ) // EmulatorClient is a wrapper around the emulator to implement the same interface // used by the SDK client. Used for testing against the emulator. type EmulatorClient struct { - adapter *adapters.SDKAdapter + adapter *emulator.SDKAdapter } func NewEmulatorClient(blockchain emulator.Emulator) *EmulatorClient { logger := zerolog.Nop() - adapter := adapters.NewSDKAdapter(&logger, blockchain) + adapter := emulator.NewSDKAdapter(&logger, blockchain) client := &EmulatorClient{ adapter: adapter, } diff --git a/ledger/common/bitutils/utils_test.go b/ledger/common/bitutils/utils_test.go index d8f23dfd1a4..f168c058ffa 100644 --- a/ledger/common/bitutils/utils_test.go +++ b/ledger/common/bitutils/utils_test.go @@ -5,9 +5,8 @@ import ( "math/big" "math/bits" "math/rand" - "time" - "testing" + "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/ledger/complete/ledger_stats.go b/ledger/complete/ledger_stats.go index 74062d5718a..c706004fc6c 100644 --- a/ledger/complete/ledger_stats.go +++ b/ledger/complete/ledger_stats.go @@ -6,6 +6,7 @@ import ( "github.com/onflow/flow-go/ledger" "github.com/onflow/flow-go/ledger/complete/mtrie/flattener" "github.com/onflow/flow-go/ledger/complete/mtrie/node" + "github.com/onflow/flow-go/ledger/complete/mtrie/trie" ) type LedgerStats struct { @@ -16,14 +17,18 @@ type LedgerStats struct { } func (l *Ledger) CollectStats(payloadCallBack func(payload *ledger.Payload)) (*LedgerStats, error) { - visitedNodes := make(map[*node.Node]uint64) - var interimNodeCounter, leafNodeCounter, totalNodeCounter uint64 - tries, err := l.Tries() if err != nil { return nil, err } + return CollectStats(tries, payloadCallBack) +} + +func CollectStats(tries []*trie.MTrie, payloadCallBack func(payload *ledger.Payload)) (*LedgerStats, error) { + visitedNodes := make(map[*node.Node]uint64) + var interimNodeCounter, leafNodeCounter, totalNodeCounter uint64 + bar := progressbar.Default(int64(len(tries)), "collecting ledger stats") for _, trie := range tries { for itr := flattener.NewUniqueNodeIterator(trie.RootNode(), visitedNodes); itr.Next(); { @@ -38,7 +43,7 @@ func (l *Ledger) CollectStats(payloadCallBack func(payload *ledger.Payload)) (*L visitedNodes[n] = totalNodeCounter totalNodeCounter++ } - if err = bar.Add(1); err != nil { + if err := bar.Add(1); err != nil { return nil, err } } diff --git a/ledger/complete/mtrie/node/node.go b/ledger/complete/mtrie/node/node.go index 8446b9e2919..bd1d6b08140 100644 --- a/ledger/complete/mtrie/node/node.go +++ b/ledger/complete/mtrie/node/node.go @@ -211,18 +211,18 @@ func (n *Node) Path() *ledger.Path { return nil } -// Payload returns the the Node's payload. +// Payload returns the Node's payload. // Do NOT MODIFY returned slices! func (n *Node) Payload() *ledger.Payload { return n.payload } -// LeftChild returns the the Node's left child. +// LeftChild returns the Node's left child. // Only INTERIM nodes have children. // Do NOT MODIFY returned Node! func (n *Node) LeftChild() *Node { return n.lChild } -// RightChild returns the the Node's right child. +// RightChild returns the Node's right child. // Only INTERIM nodes have children. // Do NOT MODIFY returned Node! func (n *Node) RightChild() *Node { return n.rChild } diff --git a/ledger/complete/wal/checkpointer.go b/ledger/complete/wal/checkpointer.go index c9081134439..b67f2385440 100644 --- a/ledger/complete/wal/checkpointer.go +++ b/ledger/complete/wal/checkpointer.go @@ -1047,7 +1047,7 @@ func CopyCheckpointFile(filename string, from string, to string) ( []string, error, ) { - // It's possible that the trie dir does not yet exist. If not this will create the the required path + // It's possible that the trie dir does not yet exist. If not this will create the required path err := os.MkdirAll(to, 0700) if err != nil { return nil, err @@ -1091,7 +1091,7 @@ func CopyCheckpointFile(filename string, from string, to string) ( // the `to` directory func SoftlinkCheckpointFile(filename string, from string, to string) ([]string, error) { - // It's possible that the trie dir does not yet exist. If not this will create the the required path + // It's possible that the trie dir does not yet exist. If not this will create the required path err := os.MkdirAll(to, 0700) if err != nil { return nil, err diff --git a/model/convert/service_event.go b/model/convert/service_event.go index f8edf54aeae..7482f2ecc6f 100644 --- a/model/convert/service_event.go +++ b/model/convert/service_event.go @@ -1170,69 +1170,12 @@ func convertVersionBoundaries(array cadence.Array) ( boundaries := make([]flow.VersionBoundary, len(array.Values)) for i, cadenceVal := range array.Values { - boundary, err := DecodeCadenceValue( - fmt.Sprintf(".Values[%d]", i), - cadenceVal, - func(structVal cadence.Struct) ( - flow.VersionBoundary, - error, - ) { - if structVal.Type() == nil { - return flow.VersionBoundary{}, fmt.Errorf("VersionBoundary struct doesn't have type") - } - - fields := cadence.FieldsMappedByName(structVal) - - const expectedFieldCount = 2 - if len(fields) < expectedFieldCount { - return flow.VersionBoundary{}, fmt.Errorf( - "incorrect number of fields (%d != %d)", - len(fields), - expectedFieldCount, - ) - } - - blockHeightValue, err := getField[cadence.Value](fields, "blockHeight") - if err != nil { - return flow.VersionBoundary{}, fmt.Errorf("failed to decode VersionBoundary struct: %w", err) - } - - versionValue, err := getField[cadence.Value](fields, "version") - if err != nil { - return flow.VersionBoundary{}, fmt.Errorf("failed to decode VersionBoundary struct: %w", err) - } - - height, err := DecodeCadenceValue( - ".blockHeight", - blockHeightValue, - func(cadenceVal cadence.UInt64) ( - uint64, - error, - ) { - return uint64(cadenceVal), nil - }, - ) - if err != nil { - return flow.VersionBoundary{}, err - } - - version, err := DecodeCadenceValue( - ".version", - versionValue, - convertSemverVersion, - ) - if err != nil { - return flow.VersionBoundary{}, err - } - - return flow.VersionBoundary{ - BlockHeight: height, - Version: version, - }, nil - }, - ) + boundary, err := VersionBoundary(cadenceVal) if err != nil { - return nil, err + return nil, decodeError{ + location: fmt.Sprintf(".Values[%d]", i), + err: err, + } } boundaries[i] = boundary } @@ -1240,6 +1183,75 @@ func convertVersionBoundaries(array cadence.Array) ( return boundaries, nil } +// VersionBoundary decodes a single version boundary from the given Cadence value. +func VersionBoundary(value cadence.Value) ( + flow.VersionBoundary, + error, +) { + boundary, err := DecodeCadenceValue( + "VersionBoundary", + value, + func(structVal cadence.Struct) ( + flow.VersionBoundary, + error, + ) { + if structVal.Type() == nil { + return flow.VersionBoundary{}, fmt.Errorf("VersionBoundary struct doesn't have type") + } + + fields := cadence.FieldsMappedByName(structVal) + + const expectedFieldCount = 2 + if len(fields) < expectedFieldCount { + return flow.VersionBoundary{}, fmt.Errorf( + "incorrect number of fields (%d != %d)", + len(fields), + expectedFieldCount, + ) + } + + blockHeightValue, err := getField[cadence.Value](fields, "blockHeight") + if err != nil { + return flow.VersionBoundary{}, fmt.Errorf("failed to decode VersionBoundary struct: %w", err) + } + + versionValue, err := getField[cadence.Value](fields, "version") + if err != nil { + return flow.VersionBoundary{}, fmt.Errorf("failed to decode VersionBoundary struct: %w", err) + } + + height, err := DecodeCadenceValue( + ".blockHeight", + blockHeightValue, + func(cadenceVal cadence.UInt64) ( + uint64, + error, + ) { + return uint64(cadenceVal), nil + }, + ) + if err != nil { + return flow.VersionBoundary{}, err + } + + version, err := DecodeCadenceValue( + ".version", + versionValue, + convertSemverVersion, + ) + if err != nil { + return flow.VersionBoundary{}, err + } + + return flow.VersionBoundary{ + BlockHeight: height, + Version: version, + }, nil + }, + ) + return boundary, err +} + func convertSemverVersion(structVal cadence.Struct) ( string, error, diff --git a/model/flow/address_test.go b/model/flow/address_test.go index a397483e303..706ae4f7861 100644 --- a/model/flow/address_test.go +++ b/model/flow/address_test.go @@ -7,7 +7,7 @@ import ( "testing" "github.com/onflow/cadence" - "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/common" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/model/flow/transaction_result.go b/model/flow/transaction_result.go index 3a327118165..f4506c47039 100644 --- a/model/flow/transaction_result.go +++ b/model/flow/transaction_result.go @@ -52,3 +52,17 @@ type LightTransactionResult struct { // ComputationUsed is amount of computation used while executing the transaction. ComputationUsed uint64 } + +// TransactionResultErrorMessage represents an error message resulting from a transaction's execution. +// This struct holds the transaction's ID, its index, any error message generated during execution, +// and the identifier of the execution node that provided the error message. +type TransactionResultErrorMessage struct { + // TransactionID is the ID of the transaction this result error was emitted from. + TransactionID Identifier + // Index is the index of the transaction this result error was emitted from. + Index uint32 + // ErrorMessage contains the error message of any error that may have occurred when the transaction was executed. + ErrorMessage string + // Executor node ID of the execution node that the message was received from. + ExecutorID Identifier +} diff --git a/model/flow/transaction_timing.go b/model/flow/transaction_timing.go index 5f2c58812de..3a9da43eee1 100644 --- a/model/flow/transaction_timing.go +++ b/model/flow/transaction_timing.go @@ -10,6 +10,7 @@ type TransactionTiming struct { Received time.Time Finalized time.Time Executed time.Time + Sealed time.Time } func (t TransactionTiming) ID() Identifier { diff --git a/module/epochs/base_client.go b/module/epochs/base_client.go index 5b372d80141..a0b845fd19a 100644 --- a/module/epochs/base_client.go +++ b/module/epochs/base_client.go @@ -12,6 +12,7 @@ import ( sdk "github.com/onflow/flow-go-sdk" sdkcrypto "github.com/onflow/flow-go-sdk/crypto" + "github.com/onflow/flow-go/network" "github.com/onflow/flow-go/module" diff --git a/module/epochs/qc_client.go b/module/epochs/qc_client.go index 8bdf32d6a57..4f891361063 100644 --- a/module/epochs/qc_client.go +++ b/module/epochs/qc_client.go @@ -13,6 +13,7 @@ import ( sdk "github.com/onflow/flow-go-sdk" sdkcrypto "github.com/onflow/flow-go-sdk/crypto" + "github.com/onflow/flow-go/network" "github.com/onflow/flow-go/consensus/hotstuff/model" diff --git a/module/execution/scripts_test.go b/module/execution/scripts_test.go index 28653047b45..e02d73f3dce 100644 --- a/module/execution/scripts_test.go +++ b/module/execution/scripts_test.go @@ -9,7 +9,7 @@ import ( "github.com/onflow/cadence" "github.com/onflow/cadence/encoding/ccf" jsoncdc "github.com/onflow/cadence/encoding/json" - "github.com/onflow/cadence/runtime/stdlib" + "github.com/onflow/cadence/stdlib" "github.com/rs/zerolog" mocks "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -178,7 +178,7 @@ func (s *scriptTestSuite) SetupTest() { s.dbDir = unittest.TempDir(s.T()) db := pebbleStorage.NewBootstrappedRegistersWithPathForTest(s.T(), s.dbDir, s.height, s.height) - pebbleRegisters, err := pebbleStorage.NewRegisters(db) + pebbleRegisters, err := pebbleStorage.NewRegisters(db, pebbleStorage.PruningDisabled) s.Require().NoError(err) s.registerIndex = pebbleRegisters diff --git a/module/executiondatasync/execution_data/mock/downloader.go b/module/executiondatasync/execution_data/mock/downloader.go index 114e2e4bba5..6d384730d7a 100644 --- a/module/executiondatasync/execution_data/mock/downloader.go +++ b/module/executiondatasync/execution_data/mock/downloader.go @@ -109,6 +109,11 @@ func (_m *Downloader) Ready() <-chan struct{} { return r0 } +// SetHeightUpdatesConsumer provides a mock function with given fields: _a0 +func (_m *Downloader) SetHeightUpdatesConsumer(_a0 execution_data.HeightUpdatesConsumer) { + _m.Called(_a0) +} + // NewDownloader creates a new instance of Downloader. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. func NewDownloader(t interface { diff --git a/module/executiondatasync/execution_data/processed_height_recorder.go b/module/executiondatasync/execution_data/processed_height_recorder.go index fd5966cb2c2..4a59effdff4 100644 --- a/module/executiondatasync/execution_data/processed_height_recorder.go +++ b/module/executiondatasync/execution_data/processed_height_recorder.go @@ -1,9 +1,13 @@ package execution_data import ( + "sync" + "go.uber.org/atomic" ) +type HeightUpdatesConsumer func(height uint64) + // ProcessedHeightRecorder is an interface for tracking the highest execution data processed // height when a block is processed and for providing this height. type ProcessedHeightRecorder interface { @@ -11,6 +15,10 @@ type ProcessedHeightRecorder interface { OnBlockProcessed(uint64) // HighestCompleteHeight returns the highest complete processed block height. HighestCompleteHeight() uint64 + + // SetHeightUpdatesConsumer subscribe consumer for processed height updates. + // Callback are called synchronously and must be non-blocking + SetHeightUpdatesConsumer(HeightUpdatesConsumer) } var _ ProcessedHeightRecorder = (*ProcessedHeightRecorderManager)(nil) @@ -18,7 +26,9 @@ var _ ProcessedHeightRecorder = (*ProcessedHeightRecorderManager)(nil) // ProcessedHeightRecorderManager manages an execution data height recorder // and tracks the highest processed block height. type ProcessedHeightRecorderManager struct { + lock sync.RWMutex highestCompleteHeight *atomic.Uint64 + consumer HeightUpdatesConsumer } // NewProcessedHeightRecorderManager creates a new ProcessedHeightRecorderManager with the given initial height. @@ -32,6 +42,12 @@ func NewProcessedHeightRecorderManager(initHeight uint64) *ProcessedHeightRecord func (e *ProcessedHeightRecorderManager) OnBlockProcessed(height uint64) { if height > e.highestCompleteHeight.Load() { e.highestCompleteHeight.Store(height) + + e.lock.RLock() + defer e.lock.RUnlock() + if e.consumer != nil { + e.consumer(height) + } } } @@ -39,3 +55,11 @@ func (e *ProcessedHeightRecorderManager) OnBlockProcessed(height uint64) { func (e *ProcessedHeightRecorderManager) HighestCompleteHeight() uint64 { return e.highestCompleteHeight.Load() } + +// SetHeightUpdatesConsumer subscribe consumers for processed height updates. +func (e *ProcessedHeightRecorderManager) SetHeightUpdatesConsumer(consumer HeightUpdatesConsumer) { + e.lock.Lock() + defer e.lock.Unlock() + + e.consumer = consumer +} diff --git a/module/hotstuff.go b/module/hotstuff.go index 8610ce0bce1..785aeed9988 100644 --- a/module/hotstuff.go +++ b/module/hotstuff.go @@ -22,7 +22,7 @@ type HotStuff interface { // // Block proposals must be submitted in order and only if they extend a // block already known to HotStuff core. - SubmitProposal(proposal *model.Proposal) + SubmitProposal(proposal *model.SignedProposal) } // HotStuffFollower is run by non-consensus nodes to observe the block chain diff --git a/module/jobqueue.go b/module/jobqueue.go index a87208a94f4..9150956504b 100644 --- a/module/jobqueue.go +++ b/module/jobqueue.go @@ -13,8 +13,9 @@ const ( ConsumeProgressExecutionDataIndexerBlockHeight = "ConsumeProgressExecutionDataIndexerBlockHeight" - ConsumeProgressIngestionEngineBlockHeight = "ConsumeProgressIngestionEngineBlockHeight" - ConsumeProgressLastFullBlockHeight = "ConsumeProgressLastFullBlockHeight" + ConsumeProgressIngestionEngineBlockHeight = "ConsumeProgressIngestionEngineBlockHeight" + ConsumeProgressEngineTxErrorMessagesBlockHeight = "ConsumeProgressEngineTxErrorMessagesBlockHeight" + ConsumeProgressLastFullBlockHeight = "ConsumeProgressLastFullBlockHeight" ) // JobID is a unique ID of the job. diff --git a/module/jobqueue/README.md b/module/jobqueue/README.md index e36bc060144..9c9dbf8f239 100644 --- a/module/jobqueue/README.md +++ b/module/jobqueue/README.md @@ -77,7 +77,7 @@ The jobqueue architecture is optimized for "pull" style processes, where the job Some use cases might require "push" style jobs where there is a job producer that create new jobs, and a consumer that processes work from the producer. This is possible with the jobqueue, but requires the producer persist the jobs to a database, then implement the `Head` and `AtIndex` methods that allow accessing jobs by sequential `uint64` indexes. ### TODOs -1. Jobs at different index are processed in parallel, it's possible that there is a job takes a long time to work on, and causing too many completed jobs cached in memory before being used to update the the last processed job index. +1. Jobs at different index are processed in parallel, it's possible that there is a job takes a long time to work on, and causing too many completed jobs cached in memory before being used to update the last processed job index. `maxSearchAhead` will allow the job consumer to stop consume more blocks if too many jobs are completed, but the job at index lastProcesssed + 1 has not been unprocessed yet. The difference between `maxSearchAhead` and `maxProcessing` is that: `maxProcessing` allows at most `maxProcessing` number of works to process jobs. However, even if there is worker available, it might not be assigned to a job, because the job at index lastProcesssed +1 has not been done, it won't work on an job with index higher than `lastProcesssed + maxSearchAhead`. 2. accept callback to get notified when the consecutive job index is finished. diff --git a/module/metrics.go b/module/metrics.go index f43c8b9325e..15204afe081 100644 --- a/module/metrics.go +++ b/module/metrics.go @@ -1019,9 +1019,6 @@ type ExecutionMetrics interface { // ExecutionCollectionRequestSent reports when a request for a collection is sent to a collection node ExecutionCollectionRequestSent() - // Unused - ExecutionCollectionRequestRetried() - // ExecutionSync reports when the state syncing is triggered or stopped. ExecutionSync(syncing bool) @@ -1076,6 +1073,10 @@ type TransactionMetrics interface { // works if the transaction was earlier added as received. TransactionFinalized(txID flow.Identifier, when time.Time) + // TransactionSealed reports the time spent between the transaction being received and sealed. Reporting only + // works if the transaction was earlier added as received. + TransactionSealed(txID flow.Identifier, when time.Time) + // TransactionExecuted reports the time spent between the transaction being received and executed. Reporting only // works if the transaction was earlier added as received. TransactionExecuted(txID flow.Identifier, when time.Time) diff --git a/module/metrics/execution.go b/module/metrics/execution.go index 7419ec3014a..e269a70de64 100644 --- a/module/metrics/execution.go +++ b/module/metrics/execution.go @@ -981,10 +981,6 @@ func (ec *ExecutionCollector) ExecutionCollectionRequestSent() { ec.collectionRequestSent.Inc() } -func (ec *ExecutionCollector) ExecutionCollectionRequestRetried() { - ec.collectionRequestRetried.Inc() -} - func (ec *ExecutionCollector) ExecutionBlockDataUploadStarted() { ec.blockDataUploadsInProgress.Inc() } @@ -1081,3 +1077,34 @@ func (ec *ExecutionCollector) ExecutionComputationResultUploaded() { func (ec *ExecutionCollector) ExecutionComputationResultUploadRetried() { ec.computationResultUploadRetriedCount.Inc() } + +type ExecutionCollectorWithTransactionCallback struct { + *ExecutionCollector + TransactionCallback func( + dur time.Duration, + stats module.TransactionExecutionResultStats, + info module.TransactionExecutionResultInfo, + ) +} + +func (ec *ExecutionCollector) WithTransactionCallback( + callback func( + time.Duration, + module.TransactionExecutionResultStats, + module.TransactionExecutionResultInfo, + ), +) *ExecutionCollectorWithTransactionCallback { + return &ExecutionCollectorWithTransactionCallback{ + ExecutionCollector: ec, + TransactionCallback: callback, + } +} + +func (ec *ExecutionCollectorWithTransactionCallback) ExecutionTransactionExecuted( + dur time.Duration, + stats module.TransactionExecutionResultStats, + info module.TransactionExecutionResultInfo, +) { + ec.ExecutionCollector.ExecutionTransactionExecuted(dur, stats, info) + ec.TransactionCallback(dur, stats, info) +} diff --git a/module/metrics/labels.go b/module/metrics/labels.go index 82260ca3c5d..20b66ad7d68 100644 --- a/module/metrics/labels.go +++ b/module/metrics/labels.go @@ -109,28 +109,30 @@ const ( ResourceNetworkingUnicastDialConfigCache = "unicast_dial_config_cache" ResourceNetworkingGossipsubDuplicateMessagesTrackerCache = "gossipsub_duplicate_messages_tracker_cache" - ResourceFollowerPendingBlocksCache = "follower_pending_block_cache" // follower engine - ResourceFollowerLoopCertifiedBlocksChannel = "follower_loop_certified_blocks_channel" // follower loop, certified blocks buffered channel - ResourceClusterBlockProposalQueue = "cluster_compliance_proposal_queue" // collection node, compliance engine - ResourceTransactionIngestQueue = "ingest_transaction_queue" // collection node, ingest engine - ResourceBeaconKey = "beacon-key" // consensus node, DKG engine - ResourceDKGMessage = "dkg_private_message" // consensus, DKG messaging engine - ResourceApprovalQueue = "sealing_approval_queue" // consensus node, sealing engine - ResourceReceiptQueue = "sealing_receipt_queue" // consensus node, sealing engine - ResourceApprovalResponseQueue = "sealing_approval_response_queue" // consensus node, sealing engine - ResourceBlockResponseQueue = "compliance_block_response_queue" // consensus node, compliance engine - ResourceBlockProposalQueue = "compliance_proposal_queue" // consensus node, compliance engine - ResourceBlockVoteQueue = "vote_aggregator_queue" // consensus/collection node, vote aggregator - ResourceTimeoutObjectQueue = "timeout_aggregator_queue" // consensus/collection node, timeout aggregator - ResourceCollectionGuaranteesQueue = "ingestion_col_guarantee_queue" // consensus node, ingestion engine - ResourceChunkDataPack = "chunk_data_pack" // execution node - ResourceChunkDataPackRequests = "chunk_data_pack_request" // execution node - ResourceEvents = "events" // execution node - ResourceServiceEvents = "service_events" // execution node - ResourceTransactionResults = "transaction_results" // execution node - ResourceTransactionResultIndices = "transaction_result_indices" // execution node - ResourceTransactionResultByBlock = "transaction_result_by_block" // execution node - ResourceExecutionDataCache = "execution_data_cache" // access node + ResourceFollowerPendingBlocksCache = "follower_pending_block_cache" // follower engine + ResourceFollowerLoopCertifiedBlocksChannel = "follower_loop_certified_blocks_channel" // follower loop, certified blocks buffered channel + ResourceClusterBlockProposalQueue = "cluster_compliance_proposal_queue" // collection node, compliance engine + ResourceTransactionIngestQueue = "ingest_transaction_queue" // collection node, ingest engine + ResourceBeaconKey = "beacon-key" // consensus node, DKG engine + ResourceDKGMessage = "dkg_private_message" // consensus, DKG messaging engine + ResourceApprovalQueue = "sealing_approval_queue" // consensus node, sealing engine + ResourceReceiptQueue = "sealing_receipt_queue" // consensus node, sealing engine + ResourceApprovalResponseQueue = "sealing_approval_response_queue" // consensus node, sealing engine + ResourceBlockResponseQueue = "compliance_block_response_queue" // consensus node, compliance engine + ResourceBlockProposalQueue = "compliance_proposal_queue" // consensus node, compliance engine + ResourceBlockVoteQueue = "vote_aggregator_queue" // consensus/collection node, vote aggregator + ResourceTimeoutObjectQueue = "timeout_aggregator_queue" // consensus/collection node, timeout aggregator + ResourceCollectionGuaranteesQueue = "ingestion_col_guarantee_queue" // consensus node, ingestion engine + ResourceChunkDataPack = "chunk_data_pack" // execution node + ResourceChunkDataPackRequests = "chunk_data_pack_request" // execution node + ResourceEvents = "events" // execution node + ResourceServiceEvents = "service_events" // execution node + ResourceTransactionResults = "transaction_results" // execution node + ResourceTransactionResultIndices = "transaction_result_indices" // execution node + ResourceTransactionResultErrorMessages = "transaction_result_error_messages" // execution node + ResourceTransactionResultErrorMessagesIndices = "transaction_result_error_messages_indices" // execution node + ResourceTransactionResultByBlock = "transaction_result_by_block" // execution node + ResourceExecutionDataCache = "execution_data_cache" // access node ) const ( diff --git a/module/metrics/noop.go b/module/metrics/noop.go index 17460bf460a..3a18e5f418b 100644 --- a/module/metrics/noop.go +++ b/module/metrics/noop.go @@ -193,7 +193,6 @@ func (nc *NoopCollector) ReadValuesSize(byte uint64) func (nc *NoopCollector) ReadDuration(duration time.Duration) {} func (nc *NoopCollector) ReadDurationPerItem(duration time.Duration) {} func (nc *NoopCollector) ExecutionCollectionRequestSent() {} -func (nc *NoopCollector) ExecutionCollectionRequestRetried() {} func (nc *NoopCollector) RuntimeTransactionParsed(dur time.Duration) {} func (nc *NoopCollector) RuntimeTransactionChecked(dur time.Duration) {} func (nc *NoopCollector) RuntimeTransactionInterpreted(dur time.Duration) {} @@ -214,6 +213,7 @@ func (nc *NoopCollector) ScriptExecutionNotIndexed() func (nc *NoopCollector) TransactionResultFetched(dur time.Duration, size int) {} func (nc *NoopCollector) TransactionReceived(txID flow.Identifier, when time.Time) {} func (nc *NoopCollector) TransactionFinalized(txID flow.Identifier, when time.Time) {} +func (nc *NoopCollector) TransactionSealed(txID flow.Identifier, when time.Time) {} func (nc *NoopCollector) TransactionExecuted(txID flow.Identifier, when time.Time) {} func (nc *NoopCollector) TransactionExpired(txID flow.Identifier) {} func (nc *NoopCollector) TransactionValidated() {} diff --git a/module/metrics/transaction.go b/module/metrics/transaction.go index 8bea3e9adea..728083f8a84 100644 --- a/module/metrics/transaction.go +++ b/module/metrics/transaction.go @@ -18,9 +18,11 @@ type TransactionCollector struct { logTimeToFinalized bool logTimeToExecuted bool logTimeToFinalizedExecuted bool + logTimeToSealed bool timeToFinalized prometheus.Summary timeToExecuted prometheus.Summary timeToFinalizedExecuted prometheus.Summary + timeToSealed prometheus.Summary transactionSubmission *prometheus.CounterVec transactionSize prometheus.Histogram scriptExecutedDuration *prometheus.HistogramVec @@ -40,6 +42,7 @@ func NewTransactionCollector( logTimeToFinalized bool, logTimeToExecuted bool, logTimeToFinalizedExecuted bool, + logTimeToSealed bool, ) *TransactionCollector { tc := &TransactionCollector{ @@ -48,6 +51,7 @@ func NewTransactionCollector( logTimeToFinalized: logTimeToFinalized, logTimeToExecuted: logTimeToExecuted, logTimeToFinalizedExecuted: logTimeToFinalizedExecuted, + logTimeToSealed: logTimeToSealed, timeToFinalized: promauto.NewSummary(prometheus.SummaryOpts{ Name: "time_to_finalized_seconds", Namespace: namespaceAccess, @@ -91,6 +95,20 @@ func NewTransactionCollector( AgeBuckets: 5, BufCap: 500, }), + timeToSealed: promauto.NewSummary(prometheus.SummaryOpts{ + Name: "time_to_seal_seconds", + Namespace: namespaceAccess, + Subsystem: subsystemTransactionTiming, + Help: "the duration of how long it took between the transaction was received until it was sealed", + Objectives: map[float64]float64{ + 0.01: 0.001, + 0.5: 0.05, + 0.99: 0.001, + }, + MaxAge: 10 * time.Minute, + AgeBuckets: 5, + BufCap: 500, + }), transactionSubmission: promauto.NewCounterVec(prometheus.CounterOpts{ Name: "transaction_submission", Namespace: namespaceAccess, @@ -240,11 +258,6 @@ func (tc *TransactionCollector) TransactionFinalized(txID flow.Identifier, when tc.trackTTF(t, tc.logTimeToFinalized) tc.trackTTFE(t, tc.logTimeToFinalizedExecuted) - - // remove transaction timing from mempool if finalized and executed - if !t.Finalized.IsZero() && !t.Executed.IsZero() { - tc.transactionTimings.Remove(txID) - } } func (tc *TransactionCollector) TransactionExecuted(txID flow.Identifier, when time.Time) { @@ -262,11 +275,25 @@ func (tc *TransactionCollector) TransactionExecuted(txID flow.Identifier, when t tc.trackTTE(t, tc.logTimeToExecuted) tc.trackTTFE(t, tc.logTimeToFinalizedExecuted) +} + +func (tc *TransactionCollector) TransactionSealed(txID flow.Identifier, when time.Time) { + t, updated := tc.transactionTimings.Adjust(txID, func(t *flow.TransactionTiming) *flow.TransactionTiming { + t.Sealed = when + return t + }) - // remove transaction timing from mempool if finalized and executed - if !t.Finalized.IsZero() && !t.Executed.IsZero() { - tc.transactionTimings.Remove(txID) + if !updated { + tc.log.Debug(). + Str("transaction_id", txID.String()). + Msg("failed to update TransactionSealed metric") + return } + + tc.trackTTS(t, tc.logTimeToSealed) + + // remove transaction timing from mempool + tc.transactionTimings.Remove(txID) } func (tc *TransactionCollector) trackTTF(t *flow.TransactionTiming, log bool) { @@ -317,6 +344,20 @@ func (tc *TransactionCollector) trackTTFE(t *flow.TransactionTiming, log bool) { } } +func (tc *TransactionCollector) trackTTS(t *flow.TransactionTiming, log bool) { + if t.Received.IsZero() || t.Sealed.IsZero() { + return + } + duration := t.Sealed.Sub(t.Received).Seconds() + + tc.timeToSealed.Observe(duration) + + if log { + tc.log.Info().Str("transaction_id", t.TransactionID.String()).Float64("duration", duration). + Msg("transaction time to sealed") + } +} + func (tc *TransactionCollector) TransactionSubmissionFailed() { tc.transactionSubmission.WithLabelValues("failed").Inc() } diff --git a/module/mock/access_metrics.go b/module/mock/access_metrics.go index 21ecc03740f..df3cb8ad8c2 100644 --- a/module/mock/access_metrics.go +++ b/module/mock/access_metrics.go @@ -138,6 +138,11 @@ func (_m *AccessMetrics) TransactionResultFetched(dur time.Duration, size int) { _m.Called(dur, size) } +// TransactionSealed provides a mock function with given fields: txID, when +func (_m *AccessMetrics) TransactionSealed(txID flow.Identifier, when time.Time) { + _m.Called(txID, when) +} + // TransactionSubmissionFailed provides a mock function with given fields: func (_m *AccessMetrics) TransactionSubmissionFailed() { _m.Called() diff --git a/module/mock/execution_metrics.go b/module/mock/execution_metrics.go index 6adc14e02a2..619fca3d60e 100644 --- a/module/mock/execution_metrics.go +++ b/module/mock/execution_metrics.go @@ -71,11 +71,6 @@ func (_m *ExecutionMetrics) ExecutionCollectionExecuted(dur time.Duration, stats _m.Called(dur, stats) } -// ExecutionCollectionRequestRetried provides a mock function with given fields: -func (_m *ExecutionMetrics) ExecutionCollectionRequestRetried() { - _m.Called() -} - // ExecutionCollectionRequestSent provides a mock function with given fields: func (_m *ExecutionMetrics) ExecutionCollectionRequestSent() { _m.Called() diff --git a/module/mock/hot_stuff.go b/module/mock/hot_stuff.go index 4801da856c7..7c1ba755027 100644 --- a/module/mock/hot_stuff.go +++ b/module/mock/hot_stuff.go @@ -60,7 +60,7 @@ func (_m *HotStuff) Start(_a0 irrecoverable.SignalerContext) { } // SubmitProposal provides a mock function with given fields: proposal -func (_m *HotStuff) SubmitProposal(proposal *model.Proposal) { +func (_m *HotStuff) SubmitProposal(proposal *model.SignedProposal) { _m.Called(proposal) } diff --git a/module/mock/transaction_metrics.go b/module/mock/transaction_metrics.go index 9345b934a9a..5e96e52a4ad 100644 --- a/module/mock/transaction_metrics.go +++ b/module/mock/transaction_metrics.go @@ -39,6 +39,11 @@ func (_m *TransactionMetrics) TransactionResultFetched(dur time.Duration, size i _m.Called(dur, size) } +// TransactionSealed provides a mock function with given fields: txID, when +func (_m *TransactionMetrics) TransactionSealed(txID flow.Identifier, when time.Time) { + _m.Called(txID, when) +} + // TransactionSubmissionFailed provides a mock function with given fields: func (_m *TransactionMetrics) TransactionSubmissionFailed() { _m.Called() diff --git a/module/signature/signing_tags.go b/module/signature/signing_tags.go index f2d142b4253..00d7e06903c 100644 --- a/module/signature/signing_tags.go +++ b/module/signature/signing_tags.go @@ -61,7 +61,7 @@ var ( // NewBLSHasher returns a hasher to be used for BLS signing and verifying // in the protocol and abstracts the hasher details from the protocol logic. // -// The hasher returned is the the expand-message step in the BLS hash-to-curve. +// The hasher returned is the expand-message step in the BLS hash-to-curve. // It uses a xof (extendable output function) based on KMAC128. It therefore has // 128-bytes outputs. func NewBLSHasher(tag string) hash.Hasher { diff --git a/module/state_synchronization/indexer/collection_executed_metric.go b/module/state_synchronization/indexer/collection_executed_metric.go index 814afbb3325..bc1ee3fd341 100644 --- a/module/state_synchronization/indexer/collection_executed_metric.go +++ b/module/state_synchronization/indexer/collection_executed_metric.go @@ -25,6 +25,8 @@ type CollectionExecutedMetricImpl struct { collections storage.Collections blocks storage.Blocks + + blockTransactions *stdmap.IdentifierMap // Map to track transactions for each block for sealed metrics } func NewCollectionExecutedMetricImpl( @@ -35,6 +37,7 @@ func NewCollectionExecutedMetricImpl( blocksToMarkExecuted *stdmap.Times, collections storage.Collections, blocks storage.Blocks, + blockTransactions *stdmap.IdentifierMap, ) (*CollectionExecutedMetricImpl, error) { return &CollectionExecutedMetricImpl{ log: log, @@ -44,16 +47,32 @@ func NewCollectionExecutedMetricImpl( blocksToMarkExecuted: blocksToMarkExecuted, collections: collections, blocks: blocks, + blockTransactions: blockTransactions, }, nil } // CollectionFinalized tracks collections to mark finalized func (c *CollectionExecutedMetricImpl) CollectionFinalized(light flow.LightCollection) { - if ti, found := c.collectionsToMarkFinalized.ByID(light.ID()); found { + lightID := light.ID() + if ti, found := c.collectionsToMarkFinalized.ByID(lightID); found { + + block, err := c.blocks.ByCollectionID(lightID) + if err != nil { + c.log.Warn().Err(err).Msg("could not find block by collection ID") + return + } + blockID := block.ID() + for _, t := range light.Transactions { c.accessMetrics.TransactionFinalized(t, ti) + + err = c.blockTransactions.Append(blockID, t) + if err != nil { + c.log.Warn().Err(err).Msg("could not append finalized tx to track sealed transactions") + continue + } } - c.collectionsToMarkFinalized.Remove(light.ID()) + c.collectionsToMarkFinalized.Remove(lightID) } } @@ -88,6 +107,24 @@ func (c *CollectionExecutedMetricImpl) BlockFinalized(block *flow.Block) { for _, t := range l.Transactions { c.accessMetrics.TransactionFinalized(t, now) + err = c.blockTransactions.Append(blockID, t) + + if err != nil { + c.log.Warn().Err(err).Msg("could not append finalized tx to track sealed transactions") + continue + } + } + } + + // Process block seals + for _, s := range block.Payload.Seals { + transactions, found := c.blockTransactions.Get(s.BlockID) + + if found { + for _, t := range transactions { + c.accessMetrics.TransactionSealed(t, now) + } + c.blockTransactions.Remove(s.BlockID) } } diff --git a/module/state_synchronization/indexer/indexer_core.go b/module/state_synchronization/indexer/indexer_core.go index aede5d6ac4f..22a6d16ea2a 100644 --- a/module/state_synchronization/indexer/indexer_core.go +++ b/module/state_synchronization/indexer/indexer_core.go @@ -287,9 +287,10 @@ func (c *IndexerCore) updateProgramCache(header *flow.Header, events []flow.Even tx.AddInvalidator(&accessInvalidator{ programs: &programInvalidator{ - invalidated: updatedContracts, + invalidated: updatedContracts, + invalidateAll: hasAuthorizedTransaction(collections, c.serviceAddress), }, - meterParamOverrides: &meterParamOverridesInvalidator{ + executionParameters: &executionParametersInvalidator{ invalidateAll: hasAuthorizedTransaction(collections, c.serviceAddress), }, }) @@ -325,7 +326,7 @@ func (c *IndexerCore) indexRegisters(registers map[ledger.Path]*ledger.Payload, return c.registers.Store(regEntries, height) } -// HandleCollection handles the response of the a collection request made earlier when a block was received. +// HandleCollection handles the response of the collection request made earlier when a block was received. // No errors expected during normal operations. func HandleCollection( collection *flow.Collection, diff --git a/module/state_synchronization/indexer/indexer_core_test.go b/module/state_synchronization/indexer/indexer_core_test.go index 5fd93d4b824..f446c0740a0 100644 --- a/module/state_synchronization/indexer/indexer_core_test.go +++ b/module/state_synchronization/indexer/indexer_core_test.go @@ -197,6 +197,8 @@ func (i *indexCoreTest) initIndexer() *indexCoreTest { require.NoError(i.t, err) blocksToMarkExecuted, err := stdmap.NewTimes(100) require.NoError(i.t, err) + blockTransactions, err := stdmap.NewIdentifierMap(100) + require.NoError(i.t, err) log := zerolog.New(os.Stdout) blocks := storagemock.NewBlocks(i.t) @@ -209,6 +211,7 @@ func (i *indexCoreTest) initIndexer() *indexCoreTest { blocksToMarkExecuted, i.collections, blocks, + blockTransactions, ) require.NoError(i.t, err) diff --git a/module/state_synchronization/indexer/util.go b/module/state_synchronization/indexer/util.go index e070c6b4ace..5526776716b 100644 --- a/module/state_synchronization/indexer/util.go +++ b/module/state_synchronization/indexer/util.go @@ -4,9 +4,9 @@ import ( "fmt" "github.com/onflow/cadence" + "github.com/onflow/cadence/common" "github.com/onflow/cadence/encoding/ccf" - "github.com/onflow/cadence/runtime/common" - "github.com/onflow/cadence/runtime/stdlib" + "github.com/onflow/cadence/stdlib" "github.com/onflow/flow-go/fvm/storage/derived" "github.com/onflow/flow-go/fvm/storage/snapshot" @@ -92,15 +92,15 @@ var _ derived.TransactionInvalidator = (*accessInvalidator)(nil) // accessInvalidator is a derived.TransactionInvalidator that invalidates programs and meter param overrides. type accessInvalidator struct { programs *programInvalidator - meterParamOverrides *meterParamOverridesInvalidator + executionParameters *executionParametersInvalidator } func (inv *accessInvalidator) ProgramInvalidator() derived.ProgramInvalidator { return inv.programs } -func (inv *accessInvalidator) MeterParamOverridesInvalidator() derived.MeterParamOverridesInvalidator { - return inv.meterParamOverrides +func (inv *accessInvalidator) ExecutionParametersInvalidator() derived.ExecutionParametersInvalidator { + return inv.executionParameters } var _ derived.ProgramInvalidator = (*programInvalidator)(nil) @@ -121,17 +121,17 @@ func (inv *programInvalidator) ShouldInvalidateEntry(location common.AddressLoca return inv.invalidateAll || ok } -var _ derived.MeterParamOverridesInvalidator = (*meterParamOverridesInvalidator)(nil) +var _ derived.ExecutionParametersInvalidator = (*executionParametersInvalidator)(nil) -// meterParamOverridesInvalidator is a derived.MeterParamOverridesInvalidator that invalidates meter param overrides. -type meterParamOverridesInvalidator struct { +// executionParametersInvalidator is a derived.ExecutionParametersInvalidator that invalidates meter param overrides and execution version. +type executionParametersInvalidator struct { invalidateAll bool } -func (inv *meterParamOverridesInvalidator) ShouldInvalidateEntries() bool { +func (inv *executionParametersInvalidator) ShouldInvalidateEntries() bool { return inv.invalidateAll } -func (inv *meterParamOverridesInvalidator) ShouldInvalidateEntry(_ struct{}, _ derived.MeterParamOverrides, _ *snapshot.ExecutionSnapshot) bool { +func (inv *executionParametersInvalidator) ShouldInvalidateEntry(_ struct{}, _ derived.StateExecutionParameters, _ *snapshot.ExecutionSnapshot) bool { return inv.invalidateAll } diff --git a/module/state_synchronization/indexer/util_test.go b/module/state_synchronization/indexer/util_test.go index 612e858e6ed..1654c590a27 100644 --- a/module/state_synchronization/indexer/util_test.go +++ b/module/state_synchronization/indexer/util_test.go @@ -4,9 +4,9 @@ import ( "testing" "github.com/onflow/cadence" + "github.com/onflow/cadence/common" "github.com/onflow/cadence/encoding/ccf" - "github.com/onflow/cadence/runtime/common" - "github.com/onflow/cadence/runtime/stdlib" + "github.com/onflow/cadence/stdlib" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/module/trace/constants.go b/module/trace/constants.go index 9e8ab96f3ad..1241ce765a0 100644 --- a/module/trace/constants.go +++ b/module/trace/constants.go @@ -185,11 +185,9 @@ const ( FVMEnvRandomSourceHistoryProvider SpanName = "fvm.env.randomSourceHistoryProvider" FVMEnvCreateAccount SpanName = "fvm.env.createAccount" FVMEnvAddAccountKey SpanName = "fvm.env.addAccountKey" - FVMEnvAddEncodedAccountKey SpanName = "fvm.env.addEncodedAccountKey" FVMEnvAccountKeysCount SpanName = "fvm.env.accountKeysCount" FVMEnvGetAccountKey SpanName = "fvm.env.getAccountKey" FVMEnvRevokeAccountKey SpanName = "fvm.env.revokeAccountKey" - FVMEnvRevokeEncodedAccountKey SpanName = "fvm.env.revokeEncodedAccountKey" FVMEnvUpdateAccountContractCode SpanName = "fvm.env.updateAccountContractCode" FVMEnvGetAccountContractCode SpanName = "fvm.env.getAccountContractCode" FVMEnvRemoveAccountContractCode SpanName = "fvm.env.removeAccountContractCode" diff --git a/network/message/Makefile b/network/message/Makefile index 9c83ad1c9dd..a9612fc3564 100644 --- a/network/message/Makefile +++ b/network/message/Makefile @@ -1,4 +1,4 @@ -# To re-generate the the protobuf go code, install tools first: +# To re-generate the protobuf go code, install tools first: # ``` # cd flow-go # make install-tools diff --git a/network/netconf/flags.go b/network/netconf/flags.go index 8b3cb8a5427..db99cfcdaa6 100644 --- a/network/netconf/flags.go +++ b/network/netconf/flags.go @@ -200,6 +200,10 @@ func AllFlagNames() []string { BuildFlagName(gossipsubKey, p2pconfig.ScoreParamsKey, p2pconfig.ScoringRegistryKey, p2pconfig.MisbehaviourPenaltiesKey, p2pconfig.IWantKey), BuildFlagName(gossipsubKey, p2pconfig.ScoreParamsKey, p2pconfig.ScoringRegistryKey, p2pconfig.MisbehaviourPenaltiesKey, p2pconfig.PublishKey), BuildFlagName(gossipsubKey, p2pconfig.ScoreParamsKey, p2pconfig.ScoringRegistryKey, p2pconfig.MisbehaviourPenaltiesKey, p2pconfig.ClusterPrefixedReductionFactorKey), + + BuildFlagName(gossipsubKey, p2pconfig.PeerGaterKey, p2pconfig.EnabledKey), + BuildFlagName(gossipsubKey, p2pconfig.PeerGaterKey, p2pconfig.SourceDecayKey), + BuildFlagName(gossipsubKey, p2pconfig.PeerGaterKey, p2pconfig.TopicDeliveryWeightsKey), } for _, scope := range []string{systemScope, transientScope, protocolScope, peerScope, peerProtocolScope} { @@ -597,6 +601,15 @@ func InitializeNetworkFlags(flags *pflag.FlagSet, config *Config) { config.GossipSub.ScoringParameters.ScoringRegistryParameters.MisbehaviourPenalties.ClusterPrefixedReductionFactor, "the factor used to reduce the penalty for control message misbehaviours on cluster prefixed topics") + flags.Bool(BuildFlagName(gossipsubKey, p2pconfig.PeerGaterKey, p2pconfig.EnabledKey), + config.GossipSub.PeerGaterEnabled, + "enable the libp2p peer gater") + flags.Duration(BuildFlagName(gossipsubKey, p2pconfig.PeerGaterKey, p2pconfig.SourceDecayKey), + config.GossipSub.PeerGaterSourceDecay, + "the per IP decay for all counters tracked by the peer gater for a peer") + flags.String(BuildFlagName(gossipsubKey, p2pconfig.PeerGaterKey, p2pconfig.TopicDeliveryWeightsKey), + config.GossipSub.PeerGaterTopicDeliveryWeightsOverride, + "topic delivery weights override, this is a comma separated with the format topic_1:2.2,topic_2:3.2,topic_3:1.7 these will be used to override the default topic weight of 1.0 for the specified topic.") } // LoadLibP2PResourceManagerFlags loads all CLI flags for the libp2p resource manager configuration on the provided pflag set. @@ -666,6 +679,7 @@ func SetAliases(conf *viper.Viper) error { // mapping should be from network-p2pconfig.key1.key2.key3... to network-config-key1-key2-key3... m[strings.Join(s[1:], "-")] = key } + // each flag name should correspond to exactly one key in our config store after it is loaded with the default config for _, flagName := range AllFlagNames() { fullKey, ok := m[flagName] diff --git a/network/p2p/builder/gossipsub/gossipSubBuilder.go b/network/p2p/builder/gossipsub/gossipSubBuilder.go index da3b74943b5..7877f7d8e15 100644 --- a/network/p2p/builder/gossipsub/gossipSubBuilder.go +++ b/network/p2p/builder/gossipsub/gossipSubBuilder.go @@ -269,6 +269,14 @@ func (g *Builder) Build(ctx irrecoverable.SignalerContext) (p2p.PubSubAdapter, e }) gossipSubConfigs.WithMessageIdFunction(utils.MessageID) + if g.gossipSubCfg.PeerGaterEnabled { + topicDeliveryWeights, err := g.gossipSubCfg.PeerGaterTopicDeliveryWeights() + if err != nil { + return nil, fmt.Errorf("failed to add peer gater option: %w", err) + } + gossipSubConfigs.WithPeerGater(topicDeliveryWeights, g.gossipSubCfg.PeerGaterSourceDecay) + } + if g.routingSystem != nil { gossipSubConfigs.WithRoutingDiscovery(g.routingSystem) } diff --git a/network/p2p/config/gossipsub.go b/network/p2p/config/gossipsub.go index 16770b7efd0..a8ea16ce604 100644 --- a/network/p2p/config/gossipsub.go +++ b/network/p2p/config/gossipsub.go @@ -1,6 +1,8 @@ package p2pconfig import ( + "strconv" + "strings" "time" ) @@ -61,6 +63,9 @@ const ( PeerScoringEnabledKey = "peer-scoring-enabled" ScoreParamsKey = "scoring-parameters" SubscriptionProviderKey = "subscription-provider" + PeerGaterKey = "peer-gater" + SourceDecayKey = "source-decay" + TopicDeliveryWeightsKey = "topic-delivery-weights-override" ) // GossipSubParameters is the configuration for the GossipSub pubsub implementation. @@ -76,6 +81,15 @@ type GossipSubParameters struct { PeerScoringEnabled bool `mapstructure:"peer-scoring-enabled"` SubscriptionProvider SubscriptionProviderParameters `mapstructure:"subscription-provider"` ScoringParameters ScoringParameters `mapstructure:"scoring-parameters"` + + // PeerGaterEnabled enables the peer gater. + PeerGaterEnabled bool `mapstructure:"peer-gater-enabled"` + // PeerGaterSourceDecay the per IP decay for all counters tracked by the peer gater for a peer. + PeerGaterSourceDecay time.Duration `mapstructure:"peer-gater-source-decay"` + // PeerGaterTopicDeliveryWeightsOverride topic delivery weights that will override the default value for the specified channel. + // This is a comma separated list "channel:weight, channel2:weight, channel3:weight". + // i.e: consensus-committee: 1.5, sync-committee: .75 + PeerGaterTopicDeliveryWeightsOverride string `mapstructure:"peer-gater-topic-delivery-weights-override"` } const ( @@ -89,6 +103,22 @@ type ScoringParameters struct { ScoringRegistryParameters ScoringRegistryParameters `validate:"required" mapstructure:"scoring-registry"` } +// PeerGaterTopicDeliveryWeights returns the topic delivery weights configured on this struct as a map[string]float64 . +// Note: When new topic delivery weights are added to the struct this func should be updated. +func (g *GossipSubParameters) PeerGaterTopicDeliveryWeights() (map[string]float64, error) { + m := make(map[string]float64) + for _, weightConfig := range strings.Split(g.PeerGaterTopicDeliveryWeightsOverride, ",") { + wc := strings.Split(weightConfig, ":") + f, err := strconv.ParseFloat(strings.TrimSpace(wc[1]), 64) + if err != nil { + return nil, err + } + m[strings.TrimSpace(wc[0])] = f + } + + return m, nil +} + // SubscriptionProviderParameters keys. const ( UpdateIntervalKey = "update-interval" diff --git a/network/p2p/config/gossipsub_rpc_inspectors.go b/network/p2p/config/gossipsub_rpc_inspectors.go index d18953d3240..4144ff2843c 100644 --- a/network/p2p/config/gossipsub_rpc_inspectors.go +++ b/network/p2p/config/gossipsub_rpc_inspectors.go @@ -48,6 +48,7 @@ const ( InspectionKey = "inspection" TruncationKey = "truncation" EnableKey = "enable" + EnabledKey = "enabled" DisabledKey = "disabled" MessageIDKey = "message-id" RejectUnstakedPeers = "reject-unstaked-peers" diff --git a/network/p2p/mock/pub_sub_adapter_config.go b/network/p2p/mock/pub_sub_adapter_config.go index 466ca754211..ac339a4683b 100644 --- a/network/p2p/mock/pub_sub_adapter_config.go +++ b/network/p2p/mock/pub_sub_adapter_config.go @@ -7,6 +7,8 @@ import ( mock "github.com/stretchr/testify/mock" routing "github.com/libp2p/go-libp2p/core/routing" + + time "time" ) // PubSubAdapterConfig is an autogenerated mock type for the PubSubAdapterConfig type @@ -19,6 +21,11 @@ func (_m *PubSubAdapterConfig) WithMessageIdFunction(f func([]byte) string) { _m.Called(f) } +// WithPeerGater provides a mock function with given fields: topicDeliveryWeights, sourceDecay +func (_m *PubSubAdapterConfig) WithPeerGater(topicDeliveryWeights map[string]float64, sourceDecay time.Duration) { + _m.Called(topicDeliveryWeights, sourceDecay) +} + // WithRoutingDiscovery provides a mock function with given fields: _a0 func (_m *PubSubAdapterConfig) WithRoutingDiscovery(_a0 routing.ContentRouting) { _m.Called(_a0) diff --git a/network/p2p/node/gossipSubAdapterConfig.go b/network/p2p/node/gossipSubAdapterConfig.go index e9b102a6e81..20d7d94b709 100644 --- a/network/p2p/node/gossipSubAdapterConfig.go +++ b/network/p2p/node/gossipSubAdapterConfig.go @@ -1,6 +1,8 @@ package p2pnode import ( + "time" + pubsub "github.com/libp2p/go-libp2p-pubsub" pb "github.com/libp2p/go-libp2p-pubsub/pb" "github.com/libp2p/go-libp2p/core/peer" @@ -97,6 +99,16 @@ func (g *GossipSubAdapterConfig) WithTracer(tracer p2p.PubSubTracer) { g.options = append(g.options, pubsub.WithRawTracer(tracer)) } +// WithPeerGater adds a peer gater option to the config. +// Args: +// - params: the topic delivery weights to use +// Returns: +// -None +func (g *GossipSubAdapterConfig) WithPeerGater(topicDeliveryWeights map[string]float64, sourceDecay time.Duration) { + peerGaterParams := pubsub.NewPeerGaterParams(pubsub.DefaultPeerGaterThreshold, pubsub.DefaultPeerGaterGlobalDecay, pubsub.ScoreParameterDecay(sourceDecay)).WithTopicDeliveryWeights(topicDeliveryWeights) + g.options = append(g.options, pubsub.WithPeerGater(peerGaterParams)) +} + // ScoreTracer returns the tracer for the peer score. // Args: // - None diff --git a/network/p2p/pubsub.go b/network/p2p/pubsub.go index 97741d0820e..acc860c6dec 100644 --- a/network/p2p/pubsub.go +++ b/network/p2p/pubsub.go @@ -84,6 +84,7 @@ type PubSubAdapterConfig interface { // This is used to expose the local scoring table of the GossipSub node to its higher level components. WithScoreTracer(tracer PeerScoreTracer) WithRpcInspector(GossipSubRPCInspector) + WithPeerGater(topicDeliveryWeights map[string]float64, sourceDecay time.Duration) } // GossipSubRPCInspector abstracts the general behavior of an app specific RPC inspector specifically diff --git a/state/protocol/prg/prg.go b/state/protocol/prg/prg.go index 36b3b77751d..d17fd1a9ac0 100644 --- a/state/protocol/prg/prg.go +++ b/state/protocol/prg/prg.go @@ -17,7 +17,7 @@ const RandomSourceLength = crypto.SignatureLenBLSBLS12381 // The diversifier is used to further diversify the PRGs beyond the customizer. A diversifier // can be a slice of any length. If no diversification is needed, `diversifier` can be `nil`. // -// The function uses an extendable-output function (xof) to extract and expand the the input source, +// The function uses an extendable-output function (xof) to extract and expand the input source, // so that any source with enough entropy (at least 128 bits) can be used (no need to pre-hash). // Current implementation generates a ChaCha20-based CSPRG. // diff --git a/storage/all.go b/storage/all.go index 2d0075aa8be..26bb89bd454 100644 --- a/storage/all.go +++ b/storage/all.go @@ -2,26 +2,27 @@ package storage // All includes all the storage modules type All struct { - Headers Headers - Guarantees Guarantees - Seals Seals - Index Index - Payloads Payloads - Blocks Blocks - QuorumCertificates QuorumCertificates - Setups EpochSetups - EpochCommits EpochCommits - Results ExecutionResults - Receipts ExecutionReceipts - ChunkDataPacks ChunkDataPacks - Commits Commits - Transactions Transactions - LightTransactionResults LightTransactionResults - TransactionResults TransactionResults - Collections Collections - Events Events - EpochProtocolStateEntries EpochProtocolStateEntries - ProtocolKVStore ProtocolKVStore - VersionBeacons VersionBeacons - RegisterIndex RegisterIndex + Headers Headers + Guarantees Guarantees + Seals Seals + Index Index + Payloads Payloads + Blocks Blocks + QuorumCertificates QuorumCertificates + Setups EpochSetups + EpochCommits EpochCommits + Results ExecutionResults + Receipts ExecutionReceipts + ChunkDataPacks ChunkDataPacks + Commits Commits + Transactions Transactions + LightTransactionResults LightTransactionResults + TransactionResults TransactionResults + TransactionResultErrorMessages TransactionResultErrorMessages + Collections Collections + Events Events + EpochProtocolStateEntries EpochProtocolStateEntries + ProtocolKVStore ProtocolKVStore + VersionBeacons VersionBeacons + RegisterIndex RegisterIndex } diff --git a/storage/badger/operation/prefix.go b/storage/badger/operation/prefix.go index ad909faf394..4113e1fcd3f 100644 --- a/storage/badger/operation/prefix.go +++ b/storage/badger/operation/prefix.go @@ -89,20 +89,22 @@ const ( codeJobQueuePointer = 72 // legacy codes (should be cleaned up) - codeChunkDataPack = 100 - codeCommit = 101 - codeEvent = 102 - codeExecutionStateInteractions = 103 - codeTransactionResult = 104 - codeFinalizedCluster = 105 - codeServiceEvent = 106 - codeTransactionResultIndex = 107 - codeLightTransactionResult = 108 - codeLightTransactionResultIndex = 109 - codeIndexCollection = 200 - codeIndexExecutionResultByBlock = 202 - codeIndexCollectionByTransaction = 203 - codeIndexResultApprovalByChunk = 204 + codeChunkDataPack = 100 + codeCommit = 101 + codeEvent = 102 + codeExecutionStateInteractions = 103 + codeTransactionResult = 104 + codeFinalizedCluster = 105 + codeServiceEvent = 106 + codeTransactionResultIndex = 107 + codeLightTransactionResult = 108 + codeLightTransactionResultIndex = 109 + codeTransactionResultErrorMessage = 110 + codeTransactionResultErrorMessageIndex = 111 + codeIndexCollection = 200 + codeIndexExecutionResultByBlock = 202 + codeIndexCollectionByTransaction = 203 + codeIndexResultApprovalByChunk = 204 // TEMPORARY codes blockedNodeIDs = 205 // manual override for adding node IDs to list of ejected nodes, applies to networking layer only diff --git a/storage/badger/operation/transaction_results.go b/storage/badger/operation/transaction_results.go index 7d5fcf47086..c4264640364 100644 --- a/storage/badger/operation/transaction_results.go +++ b/storage/badger/operation/transaction_results.go @@ -120,3 +120,51 @@ func LookupLightTransactionResultsByBlockIDUsingIndex(blockID flow.Identifier, t return traverse(makePrefix(codeLightTransactionResultIndex, blockID), txErrIterFunc) } + +// BatchInsertTransactionResultErrorMessage inserts a transaction result error message by block ID and transaction ID +// into the database using a batch write. +func BatchInsertTransactionResultErrorMessage(blockID flow.Identifier, transactionResultErrorMessage *flow.TransactionResultErrorMessage) func(batch *badger.WriteBatch) error { + return batchWrite(makePrefix(codeTransactionResultErrorMessage, blockID, transactionResultErrorMessage.TransactionID), transactionResultErrorMessage) +} + +// BatchIndexTransactionResultErrorMessage indexes a transaction result error message by index within the block using a +// batch write. +func BatchIndexTransactionResultErrorMessage(blockID flow.Identifier, transactionResultErrorMessage *flow.TransactionResultErrorMessage) func(batch *badger.WriteBatch) error { + return batchWrite(makePrefix(codeTransactionResultErrorMessageIndex, blockID, transactionResultErrorMessage.Index), transactionResultErrorMessage) +} + +// RetrieveTransactionResultErrorMessage retrieves a transaction result error message by block ID and transaction ID. +func RetrieveTransactionResultErrorMessage(blockID flow.Identifier, transactionID flow.Identifier, transactionResultErrorMessage *flow.TransactionResultErrorMessage) func(*badger.Txn) error { + return retrieve(makePrefix(codeTransactionResultErrorMessage, blockID, transactionID), transactionResultErrorMessage) +} + +// RetrieveTransactionResultErrorMessageByIndex retrieves a transaction result error message by block ID and index. +func RetrieveTransactionResultErrorMessageByIndex(blockID flow.Identifier, txIndex uint32, transactionResultErrorMessage *flow.TransactionResultErrorMessage) func(*badger.Txn) error { + return retrieve(makePrefix(codeTransactionResultErrorMessageIndex, blockID, txIndex), transactionResultErrorMessage) +} + +// TransactionResultErrorMessagesExists checks whether tx result error messages exist in the database. +func TransactionResultErrorMessagesExists(blockID flow.Identifier, blockExists *bool) func(*badger.Txn) error { + return exists(makePrefix(codeTransactionResultErrorMessageIndex, blockID), blockExists) +} + +// LookupTransactionResultErrorMessagesByBlockIDUsingIndex retrieves all tx result error messages for a block, by using +// tx_index index. This correctly handles cases of duplicate transactions within block. +func LookupTransactionResultErrorMessagesByBlockIDUsingIndex(blockID flow.Identifier, txResultErrorMessages *[]flow.TransactionResultErrorMessage) func(*badger.Txn) error { + txErrIterFunc := func() (checkFunc, createFunc, handleFunc) { + check := func(_ []byte) bool { + return true + } + var val flow.TransactionResultErrorMessage + create := func() interface{} { + return &val + } + handle := func() error { + *txResultErrorMessages = append(*txResultErrorMessages, val) + return nil + } + return check, create, handle + } + + return traverse(makePrefix(codeTransactionResultErrorMessageIndex, blockID), txErrIterFunc) +} diff --git a/storage/badger/payloads_test.go b/storage/badger/payloads_test.go index cb11074f88b..d92a593526e 100644 --- a/storage/badger/payloads_test.go +++ b/storage/badger/payloads_test.go @@ -2,7 +2,6 @@ package badger_test import ( "errors" - "testing" "github.com/dgraph-io/badger/v2" diff --git a/storage/badger/transaction_result_error_messages.go b/storage/badger/transaction_result_error_messages.go new file mode 100644 index 00000000000..e2abf659d5e --- /dev/null +++ b/storage/badger/transaction_result_error_messages.go @@ -0,0 +1,212 @@ +package badger + +import ( + "fmt" + + "github.com/dgraph-io/badger/v2" + + "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/module" + "github.com/onflow/flow-go/module/metrics" + "github.com/onflow/flow-go/storage" + "github.com/onflow/flow-go/storage/badger/operation" +) + +var _ storage.TransactionResultErrorMessages = (*TransactionResultErrorMessages)(nil) + +type TransactionResultErrorMessages struct { + db *badger.DB + cache *Cache[string, flow.TransactionResultErrorMessage] + indexCache *Cache[string, flow.TransactionResultErrorMessage] + blockCache *Cache[string, []flow.TransactionResultErrorMessage] +} + +func NewTransactionResultErrorMessages(collector module.CacheMetrics, db *badger.DB, transactionResultsCacheSize uint) *TransactionResultErrorMessages { + retrieve := func(key string) func(tx *badger.Txn) (flow.TransactionResultErrorMessage, error) { + var txResultErrMsg flow.TransactionResultErrorMessage + return func(tx *badger.Txn) (flow.TransactionResultErrorMessage, error) { + + blockID, txID, err := KeyToBlockIDTransactionID(key) + if err != nil { + return flow.TransactionResultErrorMessage{}, fmt.Errorf("could not convert key: %w", err) + } + + err = operation.RetrieveTransactionResultErrorMessage(blockID, txID, &txResultErrMsg)(tx) + if err != nil { + return flow.TransactionResultErrorMessage{}, handleError(err, flow.TransactionResultErrorMessage{}) + } + return txResultErrMsg, nil + } + } + retrieveIndex := func(key string) func(tx *badger.Txn) (flow.TransactionResultErrorMessage, error) { + var txResultErrMsg flow.TransactionResultErrorMessage + return func(tx *badger.Txn) (flow.TransactionResultErrorMessage, error) { + + blockID, txIndex, err := KeyToBlockIDIndex(key) + if err != nil { + return flow.TransactionResultErrorMessage{}, fmt.Errorf("could not convert index key: %w", err) + } + + err = operation.RetrieveTransactionResultErrorMessageByIndex(blockID, txIndex, &txResultErrMsg)(tx) + if err != nil { + return flow.TransactionResultErrorMessage{}, handleError(err, flow.TransactionResultErrorMessage{}) + } + return txResultErrMsg, nil + } + } + retrieveForBlock := func(key string) func(tx *badger.Txn) ([]flow.TransactionResultErrorMessage, error) { + var txResultErrMsg []flow.TransactionResultErrorMessage + return func(tx *badger.Txn) ([]flow.TransactionResultErrorMessage, error) { + + blockID, err := KeyToBlockID(key) + if err != nil { + return nil, fmt.Errorf("could not convert index key: %w", err) + } + + err = operation.LookupTransactionResultErrorMessagesByBlockIDUsingIndex(blockID, &txResultErrMsg)(tx) + if err != nil { + return nil, handleError(err, flow.TransactionResultErrorMessage{}) + } + return txResultErrMsg, nil + } + } + + return &TransactionResultErrorMessages{ + db: db, + cache: newCache[string, flow.TransactionResultErrorMessage](collector, metrics.ResourceTransactionResultErrorMessages, + withLimit[string, flow.TransactionResultErrorMessage](transactionResultsCacheSize), + withStore(noopStore[string, flow.TransactionResultErrorMessage]), + withRetrieve(retrieve), + ), + indexCache: newCache[string, flow.TransactionResultErrorMessage](collector, metrics.ResourceTransactionResultErrorMessagesIndices, + withLimit[string, flow.TransactionResultErrorMessage](transactionResultsCacheSize), + withStore(noopStore[string, flow.TransactionResultErrorMessage]), + withRetrieve(retrieveIndex), + ), + blockCache: newCache[string, []flow.TransactionResultErrorMessage](collector, metrics.ResourceTransactionResultErrorMessagesIndices, + withLimit[string, []flow.TransactionResultErrorMessage](transactionResultsCacheSize), + withStore(noopStore[string, []flow.TransactionResultErrorMessage]), + withRetrieve(retrieveForBlock), + ), + } +} + +// Store will store transaction result error messages for the given block ID. +// +// No errors are expected during normal operation. +func (t *TransactionResultErrorMessages) Store(blockID flow.Identifier, transactionResultErrorMessages []flow.TransactionResultErrorMessage) error { + batch := NewBatch(t.db) + + err := t.batchStore(blockID, transactionResultErrorMessages, batch) + if err != nil { + return err + } + + err = batch.Flush() + if err != nil { + return fmt.Errorf("cannot flush batch: %w", err) + } + + return nil +} + +// Exists returns true if transaction result error messages for the given ID have been stored. +// +// No errors are expected during normal operation. +func (t *TransactionResultErrorMessages) Exists(blockID flow.Identifier) (bool, error) { + // if the block is in the cache, return true + key := KeyFromBlockID(blockID) + if ok := t.blockCache.IsCached(key); ok { + return ok, nil + } + // otherwise, check badger store + var exists bool + err := t.db.View(operation.TransactionResultErrorMessagesExists(blockID, &exists)) + if err != nil { + return false, fmt.Errorf("could not check existence: %w", err) + } + return exists, nil +} + +// BatchStore inserts a batch of transaction result error messages into a batch +// +// No errors are expected during normal operation. +func (t *TransactionResultErrorMessages) batchStore( + blockID flow.Identifier, + transactionResultErrorMessages []flow.TransactionResultErrorMessage, + batch storage.BatchStorage, +) error { + writeBatch := batch.GetWriter() + + for _, result := range transactionResultErrorMessages { + err := operation.BatchInsertTransactionResultErrorMessage(blockID, &result)(writeBatch) + if err != nil { + return fmt.Errorf("cannot batch insert tx result error message: %w", err) + } + + err = operation.BatchIndexTransactionResultErrorMessage(blockID, &result)(writeBatch) + if err != nil { + return fmt.Errorf("cannot batch index tx result error message: %w", err) + } + } + + batch.OnSucceed(func() { + for _, result := range transactionResultErrorMessages { + key := KeyFromBlockIDTransactionID(blockID, result.TransactionID) + // cache for each transaction, so that it's faster to retrieve + t.cache.Insert(key, result) + + keyIndex := KeyFromBlockIDIndex(blockID, result.Index) + t.indexCache.Insert(keyIndex, result) + } + + key := KeyFromBlockID(blockID) + t.blockCache.Insert(key, transactionResultErrorMessages) + }) + return nil +} + +// ByBlockIDTransactionID returns the transaction result error message for the given block ID and transaction ID +// +// Expected errors during normal operation: +// - `storage.ErrNotFound` if no transaction error message is known at given block and transaction id. +func (t *TransactionResultErrorMessages) ByBlockIDTransactionID(blockID flow.Identifier, transactionID flow.Identifier) (*flow.TransactionResultErrorMessage, error) { + tx := t.db.NewTransaction(false) + defer tx.Discard() + key := KeyFromBlockIDTransactionID(blockID, transactionID) + transactionResultErrorMessage, err := t.cache.Get(key)(tx) + if err != nil { + return nil, err + } + return &transactionResultErrorMessage, nil +} + +// ByBlockIDTransactionIndex returns the transaction result error message for the given blockID and transaction index +// +// Expected errors during normal operation: +// - `storage.ErrNotFound` if no transaction error message is known at given block and transaction index. +func (t *TransactionResultErrorMessages) ByBlockIDTransactionIndex(blockID flow.Identifier, txIndex uint32) (*flow.TransactionResultErrorMessage, error) { + tx := t.db.NewTransaction(false) + defer tx.Discard() + key := KeyFromBlockIDIndex(blockID, txIndex) + transactionResultErrorMessage, err := t.indexCache.Get(key)(tx) + if err != nil { + return nil, err + } + return &transactionResultErrorMessage, nil +} + +// ByBlockID gets all transaction result error messages for a block, ordered by transaction index. +// Note: This method will return an empty slice both if the block is not indexed yet and if the block does not have any errors. +// +// No errors are expected during normal operation. +func (t *TransactionResultErrorMessages) ByBlockID(blockID flow.Identifier) ([]flow.TransactionResultErrorMessage, error) { + tx := t.db.NewTransaction(false) + defer tx.Discard() + key := KeyFromBlockID(blockID) + transactionResultErrorMessages, err := t.blockCache.Get(key)(tx) + if err != nil { + return nil, err + } + return transactionResultErrorMessages, nil +} diff --git a/storage/badger/transaction_result_error_messages_test.go b/storage/badger/transaction_result_error_messages_test.go new file mode 100644 index 00000000000..e21e8aaf348 --- /dev/null +++ b/storage/badger/transaction_result_error_messages_test.go @@ -0,0 +1,110 @@ +package badger_test + +import ( + "fmt" + "testing" + + "github.com/dgraph-io/badger/v2" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "golang.org/x/exp/rand" + + "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/module/metrics" + "github.com/onflow/flow-go/storage" + "github.com/onflow/flow-go/utils/unittest" + + bstorage "github.com/onflow/flow-go/storage/badger" +) + +func TestStoringTransactionResultErrorMessages(t *testing.T) { + unittest.RunWithBadgerDB(t, func(db *badger.DB) { + metrics := metrics.NewNoopCollector() + store := bstorage.NewTransactionResultErrorMessages(metrics, db, 1000) + + blockID := unittest.IdentifierFixture() + + // test db Exists by block id + exists, err := store.Exists(blockID) + require.NoError(t, err) + require.False(t, exists) + + // check retrieving by ByBlockID + messages, err := store.ByBlockID(blockID) + require.NoError(t, err) + require.Nil(t, messages) + + txErrorMessages := make([]flow.TransactionResultErrorMessage, 0) + for i := 0; i < 10; i++ { + expected := flow.TransactionResultErrorMessage{ + TransactionID: unittest.IdentifierFixture(), + ErrorMessage: fmt.Sprintf("a runtime error %d", i), + ExecutorID: unittest.IdentifierFixture(), + Index: rand.Uint32(), + } + txErrorMessages = append(txErrorMessages, expected) + } + err = store.Store(blockID, txErrorMessages) + require.NoError(t, err) + + // test db Exists by block id + exists, err = store.Exists(blockID) + require.NoError(t, err) + require.True(t, exists) + + // check retrieving by ByBlockIDTransactionID + for _, txErrorMessage := range txErrorMessages { + actual, err := store.ByBlockIDTransactionID(blockID, txErrorMessage.TransactionID) + require.NoError(t, err) + assert.Equal(t, txErrorMessage, *actual) + } + + // check retrieving by ByBlockIDTransactionIndex + for _, txErrorMessage := range txErrorMessages { + actual, err := store.ByBlockIDTransactionIndex(blockID, txErrorMessage.Index) + require.NoError(t, err) + assert.Equal(t, txErrorMessage, *actual) + } + + // check retrieving by ByBlockID + actual, err := store.ByBlockID(blockID) + require.NoError(t, err) + assert.Equal(t, txErrorMessages, actual) + + // test loading from database + newStore := bstorage.NewTransactionResultErrorMessages(metrics, db, 1000) + for _, txErrorMessage := range txErrorMessages { + actual, err := newStore.ByBlockIDTransactionID(blockID, txErrorMessage.TransactionID) + require.NoError(t, err) + assert.Equal(t, txErrorMessage, *actual) + } + + // check retrieving by index from both cache and db + for i, txErrorMessage := range txErrorMessages { + actual, err := store.ByBlockIDTransactionIndex(blockID, txErrorMessage.Index) + require.NoError(t, err) + assert.Equal(t, txErrorMessages[i], *actual) + + actual, err = newStore.ByBlockIDTransactionIndex(blockID, txErrorMessage.Index) + require.NoError(t, err) + assert.Equal(t, txErrorMessages[i], *actual) + } + }) +} + +func TestReadingNotStoreTransactionResultErrorMessage(t *testing.T) { + unittest.RunWithBadgerDB(t, func(db *badger.DB) { + metrics := metrics.NewNoopCollector() + store := bstorage.NewTransactionResultErrorMessages(metrics, db, 1000) + + blockID := unittest.IdentifierFixture() + txID := unittest.IdentifierFixture() + txIndex := rand.Uint32() + + _, err := store.ByBlockIDTransactionID(blockID, txID) + assert.ErrorIs(t, err, storage.ErrNotFound) + + _, err = store.ByBlockIDTransactionIndex(blockID, txIndex) + assert.ErrorIs(t, err, storage.ErrNotFound) + }) +} diff --git a/storage/mock/transaction_result_error_messages.go b/storage/mock/transaction_result_error_messages.go new file mode 100644 index 00000000000..c1bebf7f326 --- /dev/null +++ b/storage/mock/transaction_result_error_messages.go @@ -0,0 +1,163 @@ +// Code generated by mockery v2.43.2. DO NOT EDIT. + +package mock + +import ( + flow "github.com/onflow/flow-go/model/flow" + mock "github.com/stretchr/testify/mock" +) + +// TransactionResultErrorMessages is an autogenerated mock type for the TransactionResultErrorMessages type +type TransactionResultErrorMessages struct { + mock.Mock +} + +// ByBlockID provides a mock function with given fields: id +func (_m *TransactionResultErrorMessages) ByBlockID(id flow.Identifier) ([]flow.TransactionResultErrorMessage, error) { + ret := _m.Called(id) + + if len(ret) == 0 { + panic("no return value specified for ByBlockID") + } + + var r0 []flow.TransactionResultErrorMessage + var r1 error + if rf, ok := ret.Get(0).(func(flow.Identifier) ([]flow.TransactionResultErrorMessage, error)); ok { + return rf(id) + } + if rf, ok := ret.Get(0).(func(flow.Identifier) []flow.TransactionResultErrorMessage); ok { + r0 = rf(id) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]flow.TransactionResultErrorMessage) + } + } + + if rf, ok := ret.Get(1).(func(flow.Identifier) error); ok { + r1 = rf(id) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ByBlockIDTransactionID provides a mock function with given fields: blockID, transactionID +func (_m *TransactionResultErrorMessages) ByBlockIDTransactionID(blockID flow.Identifier, transactionID flow.Identifier) (*flow.TransactionResultErrorMessage, error) { + ret := _m.Called(blockID, transactionID) + + if len(ret) == 0 { + panic("no return value specified for ByBlockIDTransactionID") + } + + var r0 *flow.TransactionResultErrorMessage + var r1 error + if rf, ok := ret.Get(0).(func(flow.Identifier, flow.Identifier) (*flow.TransactionResultErrorMessage, error)); ok { + return rf(blockID, transactionID) + } + if rf, ok := ret.Get(0).(func(flow.Identifier, flow.Identifier) *flow.TransactionResultErrorMessage); ok { + r0 = rf(blockID, transactionID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*flow.TransactionResultErrorMessage) + } + } + + if rf, ok := ret.Get(1).(func(flow.Identifier, flow.Identifier) error); ok { + r1 = rf(blockID, transactionID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ByBlockIDTransactionIndex provides a mock function with given fields: blockID, txIndex +func (_m *TransactionResultErrorMessages) ByBlockIDTransactionIndex(blockID flow.Identifier, txIndex uint32) (*flow.TransactionResultErrorMessage, error) { + ret := _m.Called(blockID, txIndex) + + if len(ret) == 0 { + panic("no return value specified for ByBlockIDTransactionIndex") + } + + var r0 *flow.TransactionResultErrorMessage + var r1 error + if rf, ok := ret.Get(0).(func(flow.Identifier, uint32) (*flow.TransactionResultErrorMessage, error)); ok { + return rf(blockID, txIndex) + } + if rf, ok := ret.Get(0).(func(flow.Identifier, uint32) *flow.TransactionResultErrorMessage); ok { + r0 = rf(blockID, txIndex) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*flow.TransactionResultErrorMessage) + } + } + + if rf, ok := ret.Get(1).(func(flow.Identifier, uint32) error); ok { + r1 = rf(blockID, txIndex) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Exists provides a mock function with given fields: blockID +func (_m *TransactionResultErrorMessages) Exists(blockID flow.Identifier) (bool, error) { + ret := _m.Called(blockID) + + if len(ret) == 0 { + panic("no return value specified for Exists") + } + + var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(flow.Identifier) (bool, error)); ok { + return rf(blockID) + } + if rf, ok := ret.Get(0).(func(flow.Identifier) bool); ok { + r0 = rf(blockID) + } else { + r0 = ret.Get(0).(bool) + } + + if rf, ok := ret.Get(1).(func(flow.Identifier) error); ok { + r1 = rf(blockID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Store provides a mock function with given fields: blockID, transactionResultErrorMessages +func (_m *TransactionResultErrorMessages) Store(blockID flow.Identifier, transactionResultErrorMessages []flow.TransactionResultErrorMessage) error { + ret := _m.Called(blockID, transactionResultErrorMessages) + + if len(ret) == 0 { + panic("no return value specified for Store") + } + + var r0 error + if rf, ok := ret.Get(0).(func(flow.Identifier, []flow.TransactionResultErrorMessage) error); ok { + r0 = rf(blockID, transactionResultErrorMessages) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// NewTransactionResultErrorMessages creates a new instance of TransactionResultErrorMessages. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewTransactionResultErrorMessages(t interface { + mock.TestingT + Cleanup(func()) +}) *TransactionResultErrorMessages { + mock := &TransactionResultErrorMessages{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/storage/pebble/bootstrap_test.go b/storage/pebble/bootstrap_test.go index b0fccc8c269..c8a59d6fbe8 100644 --- a/storage/pebble/bootstrap_test.go +++ b/storage/pebble/bootstrap_test.go @@ -59,7 +59,7 @@ func TestRegisterBootstrap_IndexCheckpointFile_Happy(t *testing.T) { require.NoError(t, err) // create registers instance and check values - reg, err := NewRegisters(pb) + reg, err := NewRegisters(pb, PruningDisabled) require.NoError(t, err) require.Equal(t, reg.LatestHeight(), rootHeight) @@ -94,7 +94,7 @@ func TestRegisterBootstrap_IndexCheckpointFile_Empty(t *testing.T) { require.NoError(t, err) // create registers instance and check values - reg, err := NewRegisters(pb) + reg, err := NewRegisters(pb, PruningDisabled) require.NoError(t, err) require.Equal(t, reg.LatestHeight(), rootHeight) @@ -176,7 +176,7 @@ func TestRegisterBootstrap_IndexCheckpointFile_MultipleBatch(t *testing.T) { require.NoError(t, err) // create registers instance and check values - reg, err := NewRegisters(pb) + reg, err := NewRegisters(pb, PruningDisabled) require.NoError(t, err) require.Equal(t, reg.LatestHeight(), rootHeight) diff --git a/storage/pebble/open.go b/storage/pebble/open.go index 80f328ce87a..224782fed4a 100644 --- a/storage/pebble/open.go +++ b/storage/pebble/open.go @@ -1,9 +1,8 @@ package pebble import ( - "fmt" - "errors" + "fmt" "github.com/cockroachdb/pebble" "github.com/hashicorp/go-multierror" @@ -21,7 +20,7 @@ func NewBootstrappedRegistersWithPath(dir string) (*Registers, *pebble.DB, error if err != nil { return nil, nil, fmt.Errorf("failed to initialize pebble db: %w", err) } - registers, err := NewRegisters(db) + registers, err := NewRegisters(db, PruningDisabled) if err != nil { if errors.Is(err, storage.ErrNotBootstrapped) { // closing the db if not bootstrapped diff --git a/storage/pebble/open_test.go b/storage/pebble/open_test.go index 6a7ffacf592..bb5a1a32917 100644 --- a/storage/pebble/open_test.go +++ b/storage/pebble/open_test.go @@ -66,7 +66,7 @@ func TestNewBootstrappedRegistersWithPath(t *testing.T) { firstHeight := uint64(10) require.NoError(t, initHeights(db2, firstHeight)) - registers, err := NewRegisters(db2) + registers, err := NewRegisters(db2, PruningDisabled) require.NoError(t, err) require.Equal(t, firstHeight, registers.FirstHeight()) require.Equal(t, firstHeight, registers.LatestHeight()) diff --git a/storage/pebble/registers.go b/storage/pebble/registers.go index 2d889f8602f..2f5c78e8ff1 100644 --- a/storage/pebble/registers.go +++ b/storage/pebble/registers.go @@ -3,6 +3,7 @@ package pebble import ( "encoding/binary" "fmt" + "math" "github.com/cockroachdb/pebble" "github.com/pkg/errors" @@ -15,17 +16,21 @@ import ( // Registers library that implements pebble storage for registers // given a pebble instance with root block and root height populated type Registers struct { - db *pebble.DB - firstHeight uint64 - latestHeight *atomic.Uint64 + db *pebble.DB + firstHeight uint64 + latestHeight *atomic.Uint64 + pruneThreshold uint64 } +// PruningDisabled represents the absence of a pruning threshold. +const PruningDisabled = math.MaxUint64 + var _ storage.RegisterIndex = (*Registers)(nil) // NewRegisters takes a populated pebble instance with LatestHeight and FirstHeight set. // return storage.ErrNotBootstrapped if they those two keys are unavailable as it implies a uninitialized state // return other error if database is in a corrupted state -func NewRegisters(db *pebble.DB) (*Registers, error) { +func NewRegisters(db *pebble.DB, pruneThreshold uint64) (*Registers, error) { // check height keys and populate cache. These two variables will have been set firstHeight, latestHeight, err := ReadHeightsFromBootstrappedDB(db) if err != nil { @@ -33,11 +38,17 @@ func NewRegisters(db *pebble.DB) (*Registers, error) { return nil, fmt.Errorf("unable to initialize register storage, latest height unavailable in db: %w", err) } + // If no pruning threshold is provided, disable pruning. + if pruneThreshold == 0 { + pruneThreshold = PruningDisabled + } + // All registers between firstHeight and lastHeight have been indexed return &Registers{ - db: db, - firstHeight: firstHeight, - latestHeight: atomic.NewUint64(latestHeight), + db: db, + firstHeight: firstHeight, + latestHeight: atomic.NewUint64(latestHeight), + pruneThreshold: pruneThreshold, }, nil } @@ -53,12 +64,14 @@ func (s *Registers) Get( reg flow.RegisterID, height uint64, ) (flow.RegisterValue, error) { - latestHeight := s.latestHeight.Load() - if height > latestHeight || height < s.firstHeight { - return nil, errors.Wrap( - storage.ErrHeightNotIndexed, - fmt.Sprintf("height %d not indexed, indexed range is [%d-%d]", height, s.firstHeight, latestHeight), - ) + latestHeight := s.LatestHeight() + if height > latestHeight { + return nil, fmt.Errorf("height %d not indexed, latestHeight: %d, %w", height, latestHeight, storage.ErrHeightNotIndexed) + } + + firstHeight := s.calculateFirstHeight(latestHeight) + if height < firstHeight { + return nil, fmt.Errorf("height %d not indexed, indexed range: [%d-%d], %w", height, firstHeight, latestHeight, storage.ErrHeightNotIndexed) } key := newLookupKey(height, reg) return s.lookupRegister(key.Bytes()) @@ -132,6 +145,7 @@ func (s *Registers) Store( if err != nil { return fmt.Errorf("failed to commit batch: %w", err) } + s.latestHeight.Store(height) return nil @@ -144,7 +158,30 @@ func (s *Registers) LatestHeight() uint64 { // FirstHeight first indexed height found in the store, typically root block for the spork func (s *Registers) FirstHeight() uint64 { - return s.firstHeight + return s.calculateFirstHeight(s.LatestHeight()) +} + +// calculateFirstHeight calculates the first indexed height that is stored in the register index, based on the +// latest height and the configured pruning threshold. If the latest height is below the pruning threshold, the +// first indexed height will be the same as the initial height when the store was initialized. If the pruning +// threshold has been exceeded, the first indexed height is adjusted accordingly. +// +// Parameters: +// - latestHeight: the most recent height of complete registers available. +// +// Returns: +// - The first indexed height, either as the initialized height or adjusted for pruning. +func (s *Registers) calculateFirstHeight(latestHeight uint64) uint64 { + if latestHeight < s.pruneThreshold { + return s.firstHeight + } + + pruneHeight := latestHeight - s.pruneThreshold + if pruneHeight < s.firstHeight { + return s.firstHeight + } + + return pruneHeight } func firstStoredHeight(db *pebble.DB) (uint64, error) { diff --git a/storage/pebble/registers_test.go b/storage/pebble/registers_test.go index 34fd26ad602..7cf5329c3c7 100644 --- a/storage/pebble/registers_test.go +++ b/storage/pebble/registers_test.go @@ -25,7 +25,7 @@ func TestRegisters_Initialize(t *testing.T) { t.Parallel() p, dir := unittest.TempPebbleDBWithOpts(t, nil) // fail on blank database without FirstHeight and LastHeight set - _, err := NewRegisters(p) + _, err := NewRegisters(p, PruningDisabled) require.Error(t, err) // verify the error type require.True(t, errors.Is(err, storage.ErrNotBootstrapped)) @@ -296,7 +296,7 @@ func Benchmark_PayloadStorage(b *testing.B) { dbpath := path.Join(b.TempDir(), "benchmark1.db") db, err := pebble.Open(dbpath, opts) require.NoError(b, err) - s, err := NewRegisters(db) + s, err := NewRegisters(db, PruningDisabled) require.NoError(b, err) require.NotNil(b, s) diff --git a/storage/pebble/testutil.go b/storage/pebble/testutil.go index 64bd5a71a21..0d890fef85d 100644 --- a/storage/pebble/testutil.go +++ b/storage/pebble/testutil.go @@ -12,7 +12,7 @@ import ( func RunWithRegistersStorageAtInitialHeights(tb testing.TB, first uint64, latest uint64, f func(r *Registers)) { unittest.RunWithTempDir(tb, func(dir string) { db := NewBootstrappedRegistersWithPathForTest(tb, dir, first, latest) - r, err := NewRegisters(db) + r, err := NewRegisters(db, PruningDisabled) require.NoError(tb, err) f(r) diff --git a/storage/transaction_results.go b/storage/transaction_results.go index bb66023bc83..6aaacc3a880 100644 --- a/storage/transaction_results.go +++ b/storage/transaction_results.go @@ -33,3 +33,35 @@ type LightTransactionResults interface { // ByBlockID gets all transaction results for a block, ordered by transaction index ByBlockID(id flow.Identifier) ([]flow.LightTransactionResult, error) } + +// TransactionResultErrorMessages represents persistent storage for transaction result error messages +type TransactionResultErrorMessages interface { + + // Store will store transaction result error messages for the given block ID. + // + // No errors are expected during normal operation. + Store(blockID flow.Identifier, transactionResultErrorMessages []flow.TransactionResultErrorMessage) error + + // Exists returns true if transaction result error messages for the given ID have been stored. + // + // No errors are expected during normal operation. + Exists(blockID flow.Identifier) (bool, error) + + // ByBlockIDTransactionID returns the transaction result error message for the given block ID and transaction ID. + // + // Expected errors during normal operation: + // - `storage.ErrNotFound` if no transaction error message is known at given block and transaction id. + ByBlockIDTransactionID(blockID flow.Identifier, transactionID flow.Identifier) (*flow.TransactionResultErrorMessage, error) + + // ByBlockIDTransactionIndex returns the transaction result error message for the given blockID and transaction index. + // + // Expected errors during normal operation: + // - `storage.ErrNotFound` if no transaction error message is known at given block and transaction index. + ByBlockIDTransactionIndex(blockID flow.Identifier, txIndex uint32) (*flow.TransactionResultErrorMessage, error) + + // ByBlockID gets all transaction result error messages for a block, ordered by transaction index. + // Note: This method will return an empty slice both if the block is not indexed yet and if the block does not have any errors. + // + // No errors are expected during normal operation. + ByBlockID(id flow.Identifier) ([]flow.TransactionResultErrorMessage, error) +} diff --git a/utils/unittest/execution_state.go b/utils/unittest/execution_state.go index ee6da7b082d..a5e911b3771 100644 --- a/utils/unittest/execution_state.go +++ b/utils/unittest/execution_state.go @@ -23,7 +23,7 @@ const ServiceAccountPrivateKeySignAlgo = crypto.ECDSAP256 const ServiceAccountPrivateKeyHashAlgo = hash.SHA2_256 // Pre-calculated state commitment with root account with the above private key -const GenesisStateCommitmentHex = "ba479ddabd34159a9d6326ea78c659aa6dd71db63791714bdccdbea859ba1b8e" +const GenesisStateCommitmentHex = "c42fc978c2702793d2640e3ed8644ba54db4e92aa5d0501234dfbb9bbc5784fd" var GenesisStateCommitment flow.StateCommitment @@ -87,10 +87,10 @@ func genesisCommitHexByChainID(chainID flow.ChainID) string { return GenesisStateCommitmentHex } if chainID == flow.Testnet { - return "9485c620254319da8ea93978909d7ed8327e9dd1f4cb9ec74c816919166b5a2c" + return "e29456decb9ee90ad3ed1e1239383c18897b031ea851ff07f5f616657df4d4a0" } if chainID == flow.Sandboxnet { return "e1c08b17f9e5896f03fe28dd37ca396c19b26628161506924fbf785834646ea1" } - return "66df5ceb8ca13532a5004bb6014677b0ff72199a5ea42388d5e0947368739c94" + return "e1989abf50fba23015251a313eefe2ceff45639a75252f4da5970dcda32dd95e" } diff --git a/utils/unittest/fixtures.go b/utils/unittest/fixtures.go index 0bce5e99b41..9c3f9784493 100644 --- a/utils/unittest/fixtures.go +++ b/utils/unittest/fixtures.go @@ -19,6 +19,7 @@ import ( "github.com/stretchr/testify/require" sdk "github.com/onflow/flow-go-sdk" + hotstuff "github.com/onflow/flow-go/consensus/hotstuff/model" "github.com/onflow/flow-go/engine" "github.com/onflow/flow-go/engine/access/rest/util" diff --git a/utils/unittest/generator/events.go b/utils/unittest/generator/events.go index 8ea969c1240..5817b1930a3 100644 --- a/utils/unittest/generator/events.go +++ b/utils/unittest/generator/events.go @@ -5,10 +5,10 @@ import ( "testing" "github.com/onflow/cadence" + "github.com/onflow/cadence/common" "github.com/onflow/cadence/encoding/ccf" jsoncdc "github.com/onflow/cadence/encoding/json" - "github.com/onflow/cadence/runtime/common" - "github.com/onflow/cadence/runtime/stdlib" + "github.com/onflow/cadence/stdlib" "github.com/onflow/flow/protobuf/go/flow/entities" "github.com/stretchr/testify/require" diff --git a/utils/unittest/mocks/closer.go b/utils/unittest/mocks/closer.go new file mode 100644 index 00000000000..21961f0f982 --- /dev/null +++ b/utils/unittest/mocks/closer.go @@ -0,0 +1,5 @@ +package mocks + +type MockCloser struct{} + +func (mc *MockCloser) Close() error { return nil } diff --git a/utils/unittest/service_events_fixtures.go b/utils/unittest/service_events_fixtures.go index 55b9fae3561..96d970a2920 100644 --- a/utils/unittest/service_events_fixtures.go +++ b/utils/unittest/service_events_fixtures.go @@ -10,9 +10,9 @@ import ( "github.com/stretchr/testify/require" "github.com/onflow/cadence" + "github.com/onflow/cadence/common" "github.com/onflow/cadence/encoding/ccf" jsoncdc "github.com/onflow/cadence/encoding/json" - "github.com/onflow/cadence/runtime/common" "github.com/onflow/crypto" "github.com/onflow/flow-go/fvm/systemcontracts"