From fe87e5c4cc5f9412cb718a9c7e1882b619d7d4da Mon Sep 17 00:00:00 2001 From: Matt Kocubinski Date: Fri, 8 Nov 2024 08:05:25 -0600 Subject: [PATCH] refactor(store/v2): simplify genesis flow (#22435) Co-authored-by: Marko (cherry picked from commit 43e28b43ad7aa787c99c161d83ccc0cbd872612b) # Conflicts: # core/CHANGELOG.md # core/store/changeset.go # runtime/v2/go.mod # runtime/v2/go.sum # server/v2/cometbft/go.mod # server/v2/cometbft/go.sum # server/v2/go.mod # server/v2/go.sum # server/v2/store/server.go # simapp/v2/go.mod # simapp/v2/go.sum # store/v2/commitment/iavl/tree.go # store/v2/commitment/mem/tree.go # store/v2/commitment/store.go # store/v2/commitment/store_bench_test.go # store/v2/commitment/store_test_suite.go # store/v2/commitment/tree.go # store/v2/database.go # store/v2/go.mod # store/v2/go.sum # store/v2/internal/encoding/changeset_test.go # store/v2/migration/manager.go # store/v2/migration/manager_test.go # store/v2/mock/db_mock.go # store/v2/pruning/manager.go # store/v2/pruning/manager_test.go # store/v2/root/builder.go # store/v2/root/migrate_test.go # store/v2/root/store.go # store/v2/root/store_mock_test.go # store/v2/root/store_test.go # store/v2/root/upgrade_test.go # store/v2/storage/storage_bench_test.go # store/v2/storage/storage_test_suite.go # store/v2/storage/store.go # store/v2/store.go # tests/go.mod # tests/go.sum # tests/integration/v2/app.go --- core/CHANGELOG.md | 181 +++ core/store/changeset.go | 105 ++ runtime/v2/go.mod | 90 ++ runtime/v2/go.sum | 351 ++++++ server/v2/cometbft/abci.go | 30 +- server/v2/cometbft/abci_test.go | 35 +- server/v2/cometbft/go.mod | 11 + server/v2/cometbft/go.sum | 5 + .../v2/cometbft/internal/mock/mock_store.go | 20 +- server/v2/cometbft/types/store.go | 4 - server/v2/go.mod | 114 ++ server/v2/go.sum | 507 ++++++++ server/v2/store/server.go | 92 ++ simapp/v2/app_test.go | 8 +- simapp/v2/go.mod | 17 + simapp/v2/go.sum | 3 + store/v2/commitment/iavl/tree.go | 164 +++ store/v2/commitment/mem/tree.go | 67 ++ store/v2/commitment/store.go | 558 +++++++++ store/v2/commitment/store_bench_test.go | 129 ++ store/v2/commitment/store_test_suite.go | 469 ++++++++ store/v2/commitment/tree.go | 59 + store/v2/database.go | 70 ++ store/v2/go.mod | 68 ++ store/v2/go.sum | 328 +++++ store/v2/internal/encoding/changeset_test.go | 95 ++ store/v2/migration/manager.go | 278 +++++ store/v2/migration/manager_test.go | 220 ++++ store/v2/mock/db_mock.go | 409 +++++++ store/v2/pruning/manager.go | 70 ++ store/v2/pruning/manager_test.go | 259 ++++ store/v2/root/builder.go | 95 ++ store/v2/root/migrate_test.go | 163 +++ store/v2/root/store.go | 453 +++++++ store/v2/root/store_mock_test.go | 120 ++ store/v2/root/store_test.go | 789 ++++++++++++ store/v2/root/upgrade_test.go | 157 +++ store/v2/storage/storage_bench_test.go | 187 +++ store/v2/storage/storage_test_suite.go | 1056 +++++++++++++++++ store/v2/storage/store.go | 161 +++ store/v2/store.go | 100 ++ tests/go.mod | 12 + tests/go.sum | 3 + tests/integration/v2/app.go | 411 +++++++ 44 files changed, 8464 insertions(+), 59 deletions(-) create mode 100644 core/CHANGELOG.md create mode 100644 core/store/changeset.go create mode 100644 runtime/v2/go.mod create mode 100644 runtime/v2/go.sum create mode 100644 server/v2/go.mod create mode 100644 server/v2/go.sum create mode 100644 server/v2/store/server.go create mode 100644 store/v2/commitment/iavl/tree.go create mode 100644 store/v2/commitment/mem/tree.go create mode 100644 store/v2/commitment/store.go create mode 100644 store/v2/commitment/store_bench_test.go create mode 100644 store/v2/commitment/store_test_suite.go create mode 100644 store/v2/commitment/tree.go create mode 100644 store/v2/database.go create mode 100644 store/v2/go.mod create mode 100644 store/v2/go.sum create mode 100644 store/v2/internal/encoding/changeset_test.go create mode 100644 store/v2/migration/manager.go create mode 100644 store/v2/migration/manager_test.go create mode 100644 store/v2/mock/db_mock.go create mode 100644 store/v2/pruning/manager.go create mode 100644 store/v2/pruning/manager_test.go create mode 100644 store/v2/root/builder.go create mode 100644 store/v2/root/migrate_test.go create mode 100644 store/v2/root/store.go create mode 100644 store/v2/root/store_mock_test.go create mode 100644 store/v2/root/store_test.go create mode 100644 store/v2/root/upgrade_test.go create mode 100644 store/v2/storage/storage_bench_test.go create mode 100644 store/v2/storage/storage_test_suite.go create mode 100644 store/v2/storage/store.go create mode 100644 store/v2/store.go create mode 100644 tests/integration/v2/app.go diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md new file mode 100644 index 000000000000..94859275a220 --- /dev/null +++ b/core/CHANGELOG.md @@ -0,0 +1,181 @@ + + +# Changelog + +## [Unreleased] + +### API Breaking + +* [#22435](https://github.com/cosmos/cosmos-sdk/pull/22435) Add `Version uint64` field to `store.Changeset` and update `Changeset` constructors to accept a `version uint64` as their first argument. + +### Features + +* [#22326](https://github.com/cosmos/cosmos-sdk/pull/22326) Introduce codec package in order to facilitate removal of Cosmos SDK dependency in modules. +* [*22267](https://github.com/cosmos/cosmos-sdk/pull/22267) Add `server.ConfigMap` and `server.ModuleConfigMap` to replace `server.DynamicConfig` in module configuration. + +## [v1.0.0-alpha.3](https://github.com/cosmos/cosmos-sdk/releases/tag/core%2Fv1.0.0-alpha.3) + +### Features + +* [#21719](https://github.com/cosmos/cosmos-sdk/pull/21719) Make `core/event` as a type alias of `schema/appdata`. + +## [v1.0.0-alpha.2](https://github.com/cosmos/cosmos-sdk/releases/tag/core%2Fv1.0.0-alpha.2) + +### Features + +* [#21635](https://github.com/cosmos/cosmos-sdk/pull/21635) Add `server.DynamicConfig` to abstract config providers (f.e Viper) + +## [v1.0.0-alpha.1](https://github.com/cosmos/cosmos-sdk/releases/tag/core%2Fv1.0.0-alpha.1) + +### Features + +* [#21531](https://github.com/cosmos/cosmos-sdk/pull/21531) Add `registry.AminoRegistrar` to register types on the amino codec from modules. +* [#21222](https://github.com/cosmos/cosmos-sdk/pull/21222) Make `Iterator` a type alias so that `KVStore` is structurally typed. +* [#21166](https://github.com/cosmos/cosmos-sdk/pull/21166) Comment out `appmodule.HasServices` to simplify dependencies. This interface is however still supported. +* [#19953](https://github.com/cosmos/cosmos-sdk/pull/19953) Add transaction service. +* [#18379](https://github.com/cosmos/cosmos-sdk/pull/18379) Add branch service. +* [#18457](https://github.com/cosmos/cosmos-sdk/pull/18457) Add branch.ExecuteWithGasLimit. +* [#19041](https://github.com/cosmos/cosmos-sdk/pull/19041) Add `appmodule.Environment` interface to fetch different services +* [#19370](https://github.com/cosmos/cosmos-sdk/pull/19370) Add `appmodule.Migrations` interface to handle migrations +* [#19571](https://github.com/cosmos/cosmos-sdk/pull/19571) Add `router.Service` and add it in `appmodule.Environment` +* [#19617](https://github.com/cosmos/cosmos-sdk/pull/19617) Server/v2 compatible interface: + * Add DataBaseService to store non-consensus data in a database + * Create V2 appmodule with v2 api for runtime/v2 + * Introduce `Transaction.Tx` for use in runtime/v2 + * Introduce `HasUpdateValidators` interface and `ValidatorUpdate` struct for validator updates + * Introduce `HasTxValidation` interface for modules to register tx validation handlers + * `HasGenesis` interface for modules to register import, export, validation and default genesis handlers. The new api works with `proto.Message` + * Add `PreMsghandler`and `PostMsgHandler` for pre and post message hooks + * Add `MsgHandler` as an alternative to grpc handlers + * Provide separate `MigrationRegistrar` instead of grouping with `RegisterServices` +* [#19758](https://github.com/cosmos/cosmos-sdk/pull/19758) Add `registry.InterfaceRegistrar` to interact with the interface registry in modules. + +### API Breaking Changes + +* [#19672](https://github.com/cosmos/cosmos-sdk/pull/19672) `PreBlock` now returns only an error for consistency with server/v2. The SDK has upgraded x/upgrade accordingly. +* [#18857](https://github.com/cosmos/cosmos-sdk/pull/18857) Moved `FormatCoins` to `x/tx`. +* [#18861](https://github.com/cosmos/cosmos-sdk/pull/18861) Moved `coin.ParseCoin` to `client/v2/internal`. +* [#18866](https://github.com/cosmos/cosmos-sdk/pull/18866) All items related to depinject have been moved to `cosmossdk.io/depinject` (`Provide`, `Invoke`, `Register`) +* [#19041](https://github.com/cosmos/cosmos-sdk/pull/19041) `HasEventListeners` was removed from appmodule due to the fact that it was not used anywhere in the SDK nor implemented +* [#17689](https://github.com/cosmos/cosmos-sdk/pull/17689) Move Comet service to return structs instead of interfaces. + * `BlockInfo` was renamed to `Info` and `BlockInfoService` was renamed to `CometInfoService` +* [#17693](https://github.com/cosmos/cosmos-sdk/pull/17693) Remove `appmodule.UpgradeModule` interface in favor of preblock + +## [v0.11.1](https://github.com/cosmos/cosmos-sdk/releases/tag/core%2Fv0.11.1) + +* [#21022](https://github.com/cosmos/cosmos-sdk/pull/21022) Upgrade depinject to v1.0.0. + +## [v0.11.0](https://github.com/cosmos/cosmos-sdk/releases/tag/core%2Fv0.11.0) + +* [#17468](https://github.com/cosmos/cosmos-sdk/pull/17468) Add `appmodule.HasPreBlocker` interface. + +## [v0.10.0](https://github.com/cosmos/cosmos-sdk/releases/tag/core%2Fv0.10.0) + +* [#17383](https://github.com/cosmos/cosmos-sdk/pull/17383) Add `appmodule.UpgradeModule` interface. + +## [v0.9.0](https://github.com/cosmos/cosmos-sdk/releases/tag/core%2Fv0.9.0) + +* [#16739](https://github.com/cosmos/cosmos-sdk/pull/16739) Add `AppHash` to header.Info. + +## [v0.8.0](https://github.com/cosmos/cosmos-sdk/releases/tag/core%2Fv0.8.0) + +* [#15519](https://github.com/cosmos/cosmos-sdk/pull/15519) Update `comet.VoteInfo` for CometBFT v0.38. +* [#16310](https://github.com/cosmos/cosmos-sdk/pull/16310) Add `gas.Service` and `gas.GasMeter` interfaces. + +## [v0.7.0](https://github.com/cosmos/cosmos-sdk/releases/tag/core%2Fv0.7.0) + +* [#15850](https://github.com/cosmos/cosmos-sdk/pull/15850) Add `comet` and `header` packages. +* [#15923](https://github.com/cosmos/cosmos-sdk/pull/15923) Add `appmodule.HasPrepareCheckState` `appmodule.HasPrecommit` extension interfaces. +* [#15434](https://github.com/cosmos/cosmos-sdk/pull/15434) Add `coin.ParseCoin` for parsing a coin from a string. +* [#15999](https://github.com/cosmos/cosmos-sdk/pull/15999) Add `genesis.GenesisTxHandler` interface. + +## [v0.6.1](https://github.com/cosmos/cosmos-sdk/releases/tag/core%2Fv0.6.1) + +* [#15364](https://github.com/cosmos/cosmos-sdk/pull/15364) Add address codec to core. + +## [v0.6.0](https://github.com/cosmos/cosmos-sdk/releases/tag/core%2Fv0.6.0) + +* [#15045](https://github.com/cosmos/cosmos-sdk/pull/15045) Add error return parameter to `RegisterServices` method from `appmodule.HasServices` interface. +* [#14859](https://github.com/cosmos/cosmos-sdk/pull/14859) Simplify event service interface. + +## [v0.5.1](https://github.com/cosmos/cosmos-sdk/releases/tag/core%2Fv0.5.1) + +* [#14686](https://github.com/cosmos/cosmos-sdk/pull/14686) Add event service. +* [#14735](https://github.com/cosmos/cosmos-sdk/pull/14735) Specify event listener API. + +## [v0.5.0](https://github.com/cosmos/cosmos-sdk/releases/tag/core%2Fv0.5.0) + +* [#14604](https://github.com/cosmos/cosmos-sdk/pull/14604) Add begin/end block extension interfaces. +* [#14605](https://github.com/cosmos/cosmos-sdk/pull/14605) Add register services extension interface. + +## [v0.4.1](https://github.com/cosmos/cosmos-sdk/releases/tag/core%2Fv0.4.1) + +* [#14329](https://github.com/cosmos/cosmos-sdk/pull/14329) Implement basic core API genesis source and target. + +## [v0.4.0](https://github.com/cosmos/cosmos-sdk/releases/tag/core%2Fv0.4.0) + +* [#14416](https://github.com/cosmos/cosmos-sdk/pull/14416) Update core to use cosmos-db instead of tm-db. +* [#14326](https://github.com/cosmos/cosmos-sdk/pull/14326) Remove `appmodule.Service` from core. + +## [v0.3.4](https://github.com/cosmos/cosmos-sdk/releases/tag/core%2Fv0.3.4) + +* [#14223](https://github.com/cosmos/cosmos-sdk/pull/14223) Add genesis API. + +## [v0.3.3](https://github.com/cosmos/cosmos-sdk/releases/tag/core%2Fv0.3.3) + +* [#14227](https://github.com/cosmos/cosmos-sdk/pull/14227) Add store API. +* [#13696](https://github.com/cosmos/cosmos-sdk/pull/13696) Update `FormatCoins` where empty coins are rendered as "zero". + +## [v0.3.2](https://github.com/cosmos/cosmos-sdk/releases/tag/core%2Fv0.3.2) + +* [#13631](https://github.com/cosmos/cosmos-sdk/pull/13631) Add ADR 033 (inter-module communication) Client interface. + +## [v0.3.1](https://github.com/cosmos/cosmos-sdk/releases/tag/core%2Fv0.3.1) + +* [#13306](https://github.com/cosmos/cosmos-sdk/pull/13306) Move `FormatCoins` to core. +* [#13607](https://github.com/cosmos/cosmos-sdk/pull/13115) Add `AppModule` tag interface. + +## [v0.3.0](https://github.com/cosmos/cosmos-sdk/releases/tag/core%2Fv0.3.0) + +* [#13115](https://github.com/cosmos/cosmos-sdk/pull/13115) Update core module to depinject changes. + +## [v0.2.0](https://github.com/cosmos/cosmos-sdk/releases/tag/core%2Fv0.2.0) + +* [#12020](https://github.com/cosmos/cosmos-sdk/pull/12020) Use cosmossdk.io/depinject instead of container. +* [#12367](https://github.com/cosmos/cosmos-sdk/pull/12367) Add support for golang_bindings in app.yaml. + +## [v0.1.0](https://github.com/cosmos/cosmos-sdk/releases/tag/core%2Fv0.1.0) + +* [#11914](https://github.com/cosmos/cosmos-sdk/pull/11914) Add core module with app config support. diff --git a/core/store/changeset.go b/core/store/changeset.go new file mode 100644 index 000000000000..475a861445b6 --- /dev/null +++ b/core/store/changeset.go @@ -0,0 +1,105 @@ +package store + +import ( + "bytes" +) + +// Changeset is a list of changes to be written to disk +type Changeset struct { + Version uint64 + Changes []StateChanges +} + +// StateChanges represents a set of changes to the state of an actor in storage. +type StateChanges = struct { + Actor []byte // actor represents the space in storage where state is stored, previously this was called a "storekey" + StateChanges KVPairs // StateChanges is a list of key-value pairs representing the changes to the state. +} + +// KVPairs represents a set of key-value pairs. +type KVPairs = []KVPair + +// KVPair represents a change in a key and value of state. +// Remove being true signals the key must be removed from state. +type KVPair = struct { + // Key defines the key being updated. + Key []byte + // Value defines the value associated with the updated key. + Value []byte + // Remove is true when the key must be removed from state. + Remove bool +} + +func NewChangeset(version uint64) *Changeset { + return &Changeset{Version: version} +} + +func NewChangesetWithPairs(version uint64, pairs map[string]KVPairs) *Changeset { + changes := make([]StateChanges, len(pairs)) + i := 0 + for storeKey, kvPairs := range pairs { + changes[i] = StateChanges{ + Actor: []byte(storeKey), + StateChanges: kvPairs, + } + i++ + } + return &Changeset{ + Version: version, + Changes: changes, + } +} + +// Size returns the number of key-value pairs in the batch. +func (cs *Changeset) Size() int { + cnt := 0 + for _, pairs := range cs.Changes { + cnt += len(pairs.StateChanges) + } + + return cnt +} + +// Add adds a key-value pair to the ChangeSet. +func (cs *Changeset) Add(storeKey, key, value []byte, remove bool) { + found := false + for i, pairs := range cs.Changes { + if bytes.Equal(storeKey, pairs.Actor) { + pairs.StateChanges = append(pairs.StateChanges, KVPair{ + Key: key, + Value: value, + Remove: remove, + }) + cs.Changes[i] = pairs + found = true + break + } + } + + if !found { + cs.Changes = append(cs.Changes, StateChanges{ + Actor: storeKey, + StateChanges: []KVPair{{Key: key, Value: value, Remove: remove}}, + }) + } +} + +// AddKVPair adds a KVPair to the ChangeSet. +func (cs *Changeset) AddKVPair(storeKey []byte, pair KVPair) { + found := false + for i, pairs := range cs.Changes { + if bytes.Equal(storeKey, pairs.Actor) { + pairs.StateChanges = append(pairs.StateChanges, pair) + cs.Changes[i] = pairs + found = true + break + } + } + + if !found { + cs.Changes = append(cs.Changes, StateChanges{ + Actor: storeKey, + StateChanges: []KVPair{pair}, + }) + } +} diff --git a/runtime/v2/go.mod b/runtime/v2/go.mod new file mode 100644 index 000000000000..3ef8e79f7cde --- /dev/null +++ b/runtime/v2/go.mod @@ -0,0 +1,90 @@ +module cosmossdk.io/runtime/v2 + +go 1.23 + +// server v2 integration +replace ( + cosmossdk.io/api => ../../api + cosmossdk.io/core => ../../core + cosmossdk.io/core/testing => ../../core/testing + cosmossdk.io/server/v2/appmanager => ../../server/v2/appmanager + cosmossdk.io/server/v2/stf => ../../server/v2/stf + cosmossdk.io/store/v2 => ../../store/v2 + cosmossdk.io/x/tx => ../../x/tx +) + +require ( + cosmossdk.io/api v0.7.6 + cosmossdk.io/core v1.0.0-alpha.5 + cosmossdk.io/depinject v1.1.0 + cosmossdk.io/log v1.4.1 + cosmossdk.io/schema v0.3.0 + cosmossdk.io/server/v2/appmanager v0.0.0-00010101000000-000000000000 + cosmossdk.io/server/v2/stf v0.0.0-00010101000000-000000000000 + cosmossdk.io/store/v2 v2.0.0-00010101000000-000000000000 + cosmossdk.io/x/tx v1.0.0-alpha.1 + github.com/cosmos/gogoproto v1.7.0 + github.com/stretchr/testify v1.9.0 + google.golang.org/grpc v1.68.0 + google.golang.org/protobuf v1.35.1 +) + +require ( + buf.build/gen/go/cometbft/cometbft/protocolbuffers/go v1.35.1-20240701160653-fedbb9acfd2f.1 // indirect + buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.35.1-20240130113600-88ef6483f90f.1 // indirect + cosmossdk.io/core/testing v0.0.0 // indirect + cosmossdk.io/errors/v2 v2.0.0-20240731132947-df72853b3ca5 // indirect + github.com/DataDog/zstd v1.5.5 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/cockroachdb/errors v1.11.1 // indirect + github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect + github.com/cockroachdb/pebble v1.1.0 // indirect + github.com/cockroachdb/redact v1.1.5 // indirect + github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect + github.com/cosmos/cosmos-proto v1.0.0-beta.5 // indirect + github.com/cosmos/iavl v1.3.1 // indirect + github.com/cosmos/ics23/go v0.11.0 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/emicklei/dot v1.6.2 // indirect + github.com/getsentry/sentry-go v0.27.0 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/snappy v0.0.4 // indirect + github.com/google/btree v1.1.2 // indirect + github.com/google/go-cmp v0.6.0 // indirect + github.com/hashicorp/go-immutable-radix v1.3.1 // indirect + github.com/hashicorp/go-metrics v0.5.3 // indirect + github.com/hashicorp/golang-lru v1.0.2 // indirect + github.com/klauspost/compress v1.17.9 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/linxGnu/grocksdb v1.9.3 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-sqlite3 v1.14.22 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/onsi/gomega v1.28.1 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/prometheus/client_golang v1.20.5 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.60.1 // indirect + github.com/prometheus/procfs v0.15.1 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect + github.com/rs/zerolog v1.33.0 // indirect + github.com/spf13/cast v1.7.0 // indirect + github.com/stretchr/objx v0.5.2 // indirect + github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect + github.com/tendermint/go-amino v0.16.0 // indirect + github.com/tidwall/btree v1.7.0 // indirect + golang.org/x/crypto v0.28.0 // indirect + golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc // indirect + golang.org/x/net v0.30.0 // indirect + golang.org/x/sync v0.9.0 // indirect + golang.org/x/sys v0.27.0 // indirect + golang.org/x/text v0.20.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + sigs.k8s.io/yaml v1.4.0 // indirect +) diff --git a/runtime/v2/go.sum b/runtime/v2/go.sum new file mode 100644 index 000000000000..59f78300b39b --- /dev/null +++ b/runtime/v2/go.sum @@ -0,0 +1,351 @@ +buf.build/gen/go/cometbft/cometbft/protocolbuffers/go v1.35.1-20240701160653-fedbb9acfd2f.1 h1:DIUSA9vcIz63uUotWfbXXlwv1iTL+C0O2kEMLsnIIbc= +buf.build/gen/go/cometbft/cometbft/protocolbuffers/go v1.35.1-20240701160653-fedbb9acfd2f.1/go.mod h1:JTBMfyi+qAXUHumX+rcD2WIq9FNWmdcNh5MjBnSw0L0= +buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.35.1-20240130113600-88ef6483f90f.1 h1:F78ecjvMtgd1aZ1Aj9cvBjURxVGCYvRM+OOy5eR+pjw= +buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.35.1-20240130113600-88ef6483f90f.1/go.mod h1:zqi/LZjZhyvjCMTEVIwAf5VRlkLduuCfqmZxgoormq0= +cosmossdk.io/depinject v1.1.0 h1:wLan7LG35VM7Yo6ov0jId3RHWCGRhe8E8bsuARorl5E= +cosmossdk.io/depinject v1.1.0/go.mod h1:kkI5H9jCGHeKeYWXTqYdruogYrEeWvBQCw1Pj4/eCFI= +cosmossdk.io/errors/v2 v2.0.0-20240731132947-df72853b3ca5 h1:IQNdY2kB+k+1OM2DvqFG1+UgeU1JzZrWtwuWzI3ZfwA= +cosmossdk.io/errors/v2 v2.0.0-20240731132947-df72853b3ca5/go.mod h1:0CuYKkFHxc1vw2JC+t21THBCALJVROrWVR/3PQ1urpc= +cosmossdk.io/log v1.4.1 h1:wKdjfDRbDyZRuWa8M+9nuvpVYxrEOwbD/CA8hvhU8QM= +cosmossdk.io/log v1.4.1/go.mod h1:k08v0Pyq+gCP6phvdI6RCGhLf/r425UT6Rk/m+o74rU= +cosmossdk.io/schema v0.3.0 h1:01lcaM4trhzZ1HQTfTV8z6Ma1GziOZ/YmdzBN3F720c= +cosmossdk.io/schema v0.3.0/go.mod h1:RDAhxIeNB4bYqAlF4NBJwRrgtnciMcyyg0DOKnhNZQQ= +github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ= +github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= +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/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +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/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= +github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= +github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f h1:otljaYPt5hWxV3MUfO5dFPFiOXg9CyG5/kCfayTqsJ4= +github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= +github.com/cockroachdb/errors v1.11.1 h1:xSEW75zKaKCWzR3OfxXUxgrk/NtT4G1MiOv5lWZazG8= +github.com/cockroachdb/errors v1.11.1/go.mod h1:8MUxA3Gi6b25tYlFEBGLf+D8aISL+M4MIpiWMSNRfxw= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= +github.com/cockroachdb/pebble v1.1.0 h1:pcFh8CdCIt2kmEpK0OIatq67Ln9uGDYY3d5XnE0LJG4= +github.com/cockroachdb/pebble v1.1.0/go.mod h1:sEHm5NOXxyiAoKWhoFxT8xMgd/f3RA6qUqQ1BXKrh2E= +github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= +github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cosmos/cosmos-proto v1.0.0-beta.5 h1:eNcayDLpip+zVLRLYafhzLvQlSmyab+RC5W7ZfmxJLA= +github.com/cosmos/cosmos-proto v1.0.0-beta.5/go.mod h1:hQGLpiIUloJBMdQMMWb/4wRApmI9hjHH05nefC0Ojec= +github.com/cosmos/gogoproto v1.7.0 h1:79USr0oyXAbxg3rspGh/m4SWNyoz/GLaAh0QlCe2fro= +github.com/cosmos/gogoproto v1.7.0/go.mod h1:yWChEv5IUEYURQasfyBW5ffkMHR/90hiHgbNgrtp4j0= +github.com/cosmos/iavl v1.3.1 h1:+W1G2uSUtJMqMGpwz/fKiwZxY2DDT/9/0hyNLm6Geu0= +github.com/cosmos/iavl v1.3.1/go.mod h1:T6SfBcyhulVIY2G/ZtAtQm/QiJvsuhIos52V4dWYk88= +github.com/cosmos/ics23/go v0.11.0 h1:jk5skjT0TqX5e5QJbEnwXIS2yI2vnmLOgpQPeM5RtnU= +github.com/cosmos/ics23/go v0.11.0/go.mod h1:A8OjxPE67hHST4Icw94hOxxFEJMBG031xIGF/JHNIY0= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emicklei/dot v1.6.2 h1:08GN+DD79cy/tzN6uLCT84+2Wk9u+wvqP+Hkx/dIR8A= +github.com/emicklei/dot v1.6.2/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= +github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +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/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-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-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-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +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.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0= +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.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= +github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +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/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= +github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-metrics v0.5.3 h1:M5uADWMOGCTUNU1YuC4hfknOeHNaX54LDm4oYSucoNE= +github.com/hashicorp/go-metrics v0.5.3/go.mod h1:KEjodfebIOuBYSAe/bHTm+HChmKSxAOXPBieMLYozDE= +github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +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/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= +github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +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.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/linxGnu/grocksdb v1.9.3 h1:s1cbPcOd0cU2SKXRG1nEqCOWYAELQjdqg3RVI2MH9ik= +github.com/linxGnu/grocksdb v1.9.3/go.mod h1:QYiYypR2d4v63Wj1adOOfzglnoII0gLj3PNh4fZkcFA= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.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-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= +github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +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/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +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/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= +github.com/onsi/gomega v1.28.1 h1:MijcGUbfYuznzK/5R4CPNoUP/9Xvuo20sXfEm6XxoTA= +github.com/onsi/gomega v1.28.1/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= +github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= +github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= +github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pkg/errors v0.8.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/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= +github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +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.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= +github.com/prometheus/common v0.60.1 h1:FUas6GcOw66yB/73KC+BOZoFJmbo/1pojoILArPAaSc= +github.com/prometheus/common v0.60.1/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= +github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= +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/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= +github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +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/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs= +github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= +github.com/tendermint/go-amino v0.16.0 h1:GyhmgQKvqF82e2oZeuMSp9JTN0N09emoSZlb2lyGa2E= +github.com/tendermint/go-amino v0.16.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME= +github.com/tidwall/btree v1.7.0 h1:L1fkJH/AuEh5zBnnBbmTwQ5Lt+bRJ5A8EWecslvo9iI= +github.com/tidwall/btree v1.7.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY= +github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= +go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +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-20240531132922-fd00a4e0eefc h1:O9NuF4s+E/PvMIy+9IUZB9znFwUIXEWSstNjek6VpVg= +golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= +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/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +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-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= +golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= +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-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= +golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= +golang.org/x/sys v0.27.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/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= +golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 h1:hjSy6tcFQZ171igDaN5QHOw2n6vx40juYbC/x67CEhc= +google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:qpvKtACPCQhAdu3PyQgV4l3LMXZEtft7y8QcarRsp9I= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28 h1:XVhgTWWV3kGQlwJHR3upFWZeTsei6Oks1apkZSeonIE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI= +google.golang.org/grpc v1.68.0 h1:aHQeeJbo8zAkAa3pRzrVjZlbz6uSfeOXlJNQM0RAbz0= +google.golang.org/grpc v1.68.0/go.mod h1:fmSPC5AsjSBCK54MyHRx48kpOti1/jRfOlwEWywNjWA= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +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.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= +google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +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-20190902080502-41f04d3bba15/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/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.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.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.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= +gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/server/v2/cometbft/abci.go b/server/v2/cometbft/abci.go index 45a5be69da89..7b2f751b6ce2 100644 --- a/server/v2/cometbft/abci.go +++ b/server/v2/cometbft/abci.go @@ -329,7 +329,7 @@ func (c *Consensus[T]) InitChain(ctx context.Context, req *abciproto.InitChainRe IsGenesis: true, } - blockresponse, genesisState, err := c.app.InitGenesis( + blockResponse, genesisState, err := c.app.InitGenesis( ctx, br, req.AppStateBytes, @@ -338,17 +338,16 @@ func (c *Consensus[T]) InitChain(ctx context.Context, req *abciproto.InitChainRe return nil, fmt.Errorf("genesis state init failure: %w", err) } - for _, txRes := range blockresponse.TxResults { + for _, txRes := range blockResponse.TxResults { if err := txRes.Error; err != nil { - space, code, log := errorsmod.ABCIInfo(err, c.cfg.AppTomlConfig.Trace) - c.logger.Warn("genesis tx failed", "codespace", space, "code", code, "log", log) + space, code, txLog := errorsmod.ABCIInfo(err, c.cfg.AppTomlConfig.Trace) + c.logger.Warn("genesis tx failed", "codespace", space, "code", code, "log", txLog) } } - validatorUpdates := intoABCIValidatorUpdates(blockresponse.ValidatorUpdates) + validatorUpdates := intoABCIValidatorUpdates(blockResponse.ValidatorUpdates) - // set the initial version of the store - if err := c.store.SetInitialVersion(uint64(req.InitialHeight)); err != nil { + if err := c.store.SetInitialVersion(uint64(req.InitialHeight - 1)); err != nil { return nil, fmt.Errorf("failed to set initial version: %w", err) } @@ -357,9 +356,10 @@ func (c *Consensus[T]) InitChain(ctx context.Context, req *abciproto.InitChainRe return nil, err } cs := &store.Changeset{ + Version: uint64(req.InitialHeight - 1), Changes: stateChanges, } - stateRoot, err := c.store.WorkingHash(cs) + stateRoot, err := c.store.Commit(cs) if err != nil { return nil, fmt.Errorf("unable to write the changeset: %w", err) } @@ -455,18 +455,6 @@ func (c *Consensus[T]) FinalizeBlock( return nil, err } - // we don't need to deliver the block in the genesis block - if req.Height == int64(c.initialHeight) { - appHash, err := c.store.Commit(store.NewChangeset()) - if err != nil { - return nil, fmt.Errorf("unable to commit the changeset: %w", err) - } - c.lastCommittedHeight.Store(req.Height) - return &abciproto.FinalizeBlockResponse{ - AppHash: appHash, - }, nil - } - // TODO(tip): can we expect some txs to not decode? if so, what we do in this case? this does not seem to be the case, // considering that prepare and process always decode txs, assuming they're the ones providing txs we should never // have a tx that fails decoding. @@ -507,7 +495,7 @@ func (c *Consensus[T]) FinalizeBlock( if err != nil { return nil, err } - appHash, err := c.store.Commit(&store.Changeset{Changes: stateChanges}) + appHash, err := c.store.Commit(&store.Changeset{Version: uint64(req.Height), Changes: stateChanges}) if err != nil { return nil, fmt.Errorf("unable to commit the changeset: %w", err) } diff --git a/server/v2/cometbft/abci_test.go b/server/v2/cometbft/abci_test.go index d58e87aa9ef9..85433d426cfe 100644 --- a/server/v2/cometbft/abci_test.go +++ b/server/v2/cometbft/abci_test.go @@ -32,6 +32,7 @@ import ( var ( sum = sha256.Sum256([]byte("test-hash")) + emptyHash = sha256.Sum256([]byte("")) DefaulConsensusParams = &v1.ConsensusParams{ Block: &v1.BlockParams{ MaxGas: 5000000, @@ -124,6 +125,7 @@ func TestConsensus_InitChain_Without_UpdateParam(t *testing.T) { _, err = c.FinalizeBlock(context.Background(), &abciproto.FinalizeBlockRequest{ Time: time.Now(), Height: 1, + Hash: emptyHash[:], }) require.NoError(t, err) assertStoreLatestVersion(t, mockStore, 1) @@ -144,6 +146,7 @@ func TestConsensus_InitChain_With_UpdateParam(t *testing.T) { _, err = c.FinalizeBlock(context.Background(), &abciproto.FinalizeBlockRequest{ Time: time.Now(), Height: 1, + Hash: emptyHash[:], }) require.NoError(t, err) @@ -159,15 +162,16 @@ func TestConsensus_InitChain_Invalid_Height(t *testing.T) { InitialHeight: 2, }) require.NoError(t, err) - assertStoreLatestVersion(t, mockStore, 0) + assertStoreLatestVersion(t, mockStore, 1) - // Shouldn't be able to commit genesis block 2 + // Shouldn't be able to commit genesis block 3 _, err = c.FinalizeBlock(context.Background(), &abciproto.FinalizeBlockRequest{ Time: time.Now(), - Height: 2, + Height: 3, + Hash: emptyHash[:], }) require.Error(t, err) - require.True(t, strings.Contains(err.Error(), "unable to commit the changeset")) + require.True(t, strings.Contains(err.Error(), "invalid height")) } func TestConsensus_FinalizeBlock_Invalid_Height(t *testing.T) { @@ -182,12 +186,14 @@ func TestConsensus_FinalizeBlock_Invalid_Height(t *testing.T) { _, err = c.FinalizeBlock(context.Background(), &abciproto.FinalizeBlockRequest{ Time: time.Now(), Height: 1, + Hash: emptyHash[:], }) require.NoError(t, err) _, err = c.FinalizeBlock(context.Background(), &abciproto.FinalizeBlockRequest{ Time: time.Now(), Height: 3, + Hash: emptyHash[:], }) require.Error(t, err) } @@ -206,6 +212,7 @@ func TestConsensus_FinalizeBlock_NoTxs(t *testing.T) { _, err = c.FinalizeBlock(context.Background(), &abciproto.FinalizeBlockRequest{ Time: time.Now(), Height: 1, + Hash: emptyHash[:], }) require.NoError(t, err) @@ -236,6 +243,7 @@ func TestConsensus_FinalizeBlock_MultiTxs_OutOfGas(t *testing.T) { _, err = c.FinalizeBlock(context.Background(), &abciproto.FinalizeBlockRequest{ Time: time.Now(), Height: 1, + Hash: emptyHash[:], }) require.NoError(t, err) @@ -267,6 +275,7 @@ func TestConsensus_FinalizeBlock_MultiTxs(t *testing.T) { _, err = c.FinalizeBlock(context.Background(), &abciproto.FinalizeBlockRequest{ Time: time.Now(), Height: 1, + Hash: emptyHash[:], }) require.NoError(t, err) @@ -554,6 +563,7 @@ func TestConsensus_Info(t *testing.T) { _, err = c.FinalizeBlock(context.Background(), &abciproto.FinalizeBlockRequest{ Time: time.Now(), Height: 1, + Hash: emptyHash[:], }) require.NoError(t, err) @@ -570,7 +580,8 @@ func TestConsensus_Query(t *testing.T) { c := setUpConsensus(t, 100_000, cometmock.MockMempool[mock.Tx]{}) // Write data to state storage - err := c.store.GetStateStorage().ApplyChangeset(1, &store.Changeset{ + err := c.store.GetStateStorage().ApplyChangeset(&store.Changeset{ + Version: 1, Changes: []store.StateChanges{ { Actor: actorName, @@ -597,6 +608,7 @@ func TestConsensus_Query(t *testing.T) { Time: time.Now(), Height: 1, Txs: [][]byte{mockTx.Bytes()}, + Hash: emptyHash[:], }) require.NoError(t, err) @@ -687,22 +699,19 @@ func setUpConsensus(t *testing.T, gasLimit uint64, mempool mempool.Mempool[mock. nil, ) - return NewConsensus[mock.Tx](log.NewNopLogger(), "testing-app", am, func() error { return nil }, mempool, map[string]struct{}{}, nil, mockStore, Config{AppTomlConfig: DefaultAppTomlConfig()}, mock.TxCodec{}, "test") + return NewConsensus[mock.Tx](log.NewNopLogger(), "testing-app", am, func() error { return nil }, + mempool, map[string]struct{}{}, nil, mockStore, + Config{AppTomlConfig: DefaultAppTomlConfig()}, mock.TxCodec{}, "test") } // Check target version same with store's latest version // And should have commit info of target version -// If block 0, commitInfo returned should be nil func assertStoreLatestVersion(t *testing.T, store types.Store, target uint64) { t.Helper() version, err := store.GetLatestVersion() require.NoError(t, err) - require.Equal(t, version, target) + require.Equal(t, target, version) commitInfo, err := store.GetStateCommitment().GetCommitInfo(version) require.NoError(t, err) - if target != 0 { - require.Equal(t, commitInfo.Version, target) - } else { - require.Nil(t, commitInfo) - } + require.Equal(t, target, commitInfo.Version) } diff --git a/server/v2/cometbft/go.mod b/server/v2/cometbft/go.mod index e67006cf9f16..0294702f6cd2 100644 --- a/server/v2/cometbft/go.mod +++ b/server/v2/cometbft/go.mod @@ -3,10 +3,21 @@ module cosmossdk.io/server/v2/cometbft go 1.23.1 replace ( +<<<<<<< HEAD // pseudo version lower than the latest tag cosmossdk.io/api => cosmossdk.io/api v0.7.3-0.20240924065902-eb7653cfecdf // main // pseudo version lower than the latest tag cosmossdk.io/store => cosmossdk.io/store v1.0.0-rc.0.0.20241106093505-9611c5a0e6e3 // main +======= + cosmossdk.io/api => ../../../api + cosmossdk.io/core => ../../../core + cosmossdk.io/core/testing => ../../../core/testing + cosmossdk.io/server/v2 => ../ + cosmossdk.io/server/v2/appmanager => ../appmanager + cosmossdk.io/server/v2/stf => ../stf + cosmossdk.io/store => ../../../store + cosmossdk.io/store/v2 => ../../../store/v2 +>>>>>>> 43e28b43a (refactor(store/v2): simplify genesis flow (#22435)) cosmossdk.io/x/bank => ../../../x/bank cosmossdk.io/x/consensus => ../../../x/consensus cosmossdk.io/x/staking => ../../../x/staking diff --git a/server/v2/cometbft/go.sum b/server/v2/cometbft/go.sum index e341ed06cc47..81702db3ad43 100644 --- a/server/v2/cometbft/go.sum +++ b/server/v2/cometbft/go.sum @@ -4,6 +4,7 @@ buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.34.2-20240130113600-88e buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.34.2-20240130113600-88ef6483f90f.2/go.mod h1:HqcXMSa5qnNuakaMUo+hWhF51mKbcrZxGl9Vp5EeJXc= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +<<<<<<< HEAD cosmossdk.io/api v0.7.3-0.20240924065902-eb7653cfecdf h1:CttA/mEIxGm4E7vwrjUpju7/Iespns08d9bOza70cIc= cosmossdk.io/api v0.7.3-0.20240924065902-eb7653cfecdf/go.mod h1:YMfx2ATpgITsoydD3hIBa8IkDHtyXp/14rmG0d3sEew= cosmossdk.io/collections v0.4.1-0.20241107084833-00f3065e70ee h1:OLqvi9ekfShobmdgr4Q/8pu+LjzYJSrNl9tiadPg2xY= @@ -12,6 +13,10 @@ cosmossdk.io/core v1.0.0-alpha.5 h1:McjYXAQ6XcT20v2uHyH7PhoWH8V+mebzfVFqT3GinsI= cosmossdk.io/core v1.0.0-alpha.5/go.mod h1:3u9cWq1FAVtiiCrDPpo4LhR+9V6k/ycSG4/Y/tREWCY= cosmossdk.io/core/testing v0.0.0-20240923163230-04da382a9f29 h1:NxxUo0GMJUbIuVg0R70e3cbn9eFTEuMr7ev1AFvypdY= cosmossdk.io/core/testing v0.0.0-20240923163230-04da382a9f29/go.mod h1:8s2tPeJtSiQuoyPmr2Ag7meikonISO4Fv4MoO8+ORrs= +======= +cosmossdk.io/collections v0.4.0 h1:PFmwj2W8szgpD5nOd8GWH6AbYNi1f2J6akWXJ7P5t9s= +cosmossdk.io/collections v0.4.0/go.mod h1:oa5lUING2dP+gdDquow+QjlF45eL1t4TJDypgGd+tv0= +>>>>>>> 43e28b43a (refactor(store/v2): simplify genesis flow (#22435)) cosmossdk.io/depinject v1.1.0 h1:wLan7LG35VM7Yo6ov0jId3RHWCGRhe8E8bsuARorl5E= cosmossdk.io/depinject v1.1.0/go.mod h1:kkI5H9jCGHeKeYWXTqYdruogYrEeWvBQCw1Pj4/eCFI= cosmossdk.io/errors v1.0.1 h1:bzu+Kcr0kS/1DuPBtUFdWjzLqyUuCiyHjyJB6srBV/0= diff --git a/server/v2/cometbft/internal/mock/mock_store.go b/server/v2/cometbft/internal/mock/mock_store.go index 765659ce6042..e9f7be3edcb3 100644 --- a/server/v2/cometbft/internal/mock/mock_store.go +++ b/server/v2/cometbft/internal/mock/mock_store.go @@ -59,8 +59,7 @@ func (s *MockStore) StateLatest() (uint64, corestore.ReaderMap, error) { } func (s *MockStore) Commit(changeset *corestore.Changeset) (corestore.Hash, error) { - v, _, _ := s.StateLatest() - err := s.Storage.ApplyChangeset(v, changeset) + err := s.Storage.ApplyChangeset(changeset) if err != nil { return []byte{}, err } @@ -70,8 +69,7 @@ func (s *MockStore) Commit(changeset *corestore.Changeset) (corestore.Hash, erro return []byte{}, err } - commitInfo, err := s.Committer.Commit(v + 1) - fmt.Println("commitInfo", commitInfo, err) + _, err = s.Committer.Commit(changeset.Version) return []byte{}, err } @@ -127,17 +125,3 @@ func (s *MockStore) LastCommitID() (proof.CommitID, error) { func (s *MockStore) SetInitialVersion(v uint64) error { return s.Committer.SetInitialVersion(v) } - -func (s *MockStore) WorkingHash(changeset *corestore.Changeset) (corestore.Hash, error) { - v, _, _ := s.StateLatest() - err := s.Storage.ApplyChangeset(v, changeset) - if err != nil { - return []byte{}, err - } - - err = s.Committer.WriteChangeset(changeset) - if err != nil { - return []byte{}, err - } - return []byte{}, nil -} diff --git a/server/v2/cometbft/types/store.go b/server/v2/cometbft/types/store.go index 3e603ff18ced..b78dbf8f102c 100644 --- a/server/v2/cometbft/types/store.go +++ b/server/v2/cometbft/types/store.go @@ -19,10 +19,6 @@ type Store interface { // SetInitialVersion sets the initial version of the store. SetInitialVersion(uint64) error - // WorkingHash writes the provided changeset to the state and returns - // the working hash of the state. - WorkingHash(*store.Changeset) (store.Hash, error) - // Commit commits the provided changeset and returns // the new state root of the state. Commit(*store.Changeset) (store.Hash, error) diff --git a/server/v2/go.mod b/server/v2/go.mod new file mode 100644 index 000000000000..fa0350ebfa21 --- /dev/null +++ b/server/v2/go.mod @@ -0,0 +1,114 @@ +module cosmossdk.io/server/v2 + +go 1.23 + +replace ( + cosmossdk.io/api => ../../api + cosmossdk.io/core => ../../core + cosmossdk.io/server/v2/appmanager => ./appmanager + cosmossdk.io/server/v2/stf => ./stf + cosmossdk.io/store/v2 => ../../store/v2 + cosmossdk.io/store/v2/db => ../../store/v2/db + cosmossdk.io/x/tx => ../../x/tx +) + +require ( + cosmossdk.io/api v0.7.6 + cosmossdk.io/core v1.0.0-alpha.5 + cosmossdk.io/core/testing v0.0.0-20240923163230-04da382a9f29 + cosmossdk.io/log v1.4.1 + cosmossdk.io/server/v2/appmanager v0.0.0-00010101000000-000000000000 + cosmossdk.io/store/v2 v2.0.0-00010101000000-000000000000 + github.com/cosmos/cosmos-proto v1.0.0-beta.5 + github.com/cosmos/gogogateway v1.2.0 + github.com/cosmos/gogoproto v1.7.0 + github.com/golang/protobuf v1.5.4 + github.com/grpc-ecosystem/grpc-gateway v1.16.0 + github.com/hashicorp/go-hclog v1.6.3 + github.com/hashicorp/go-metrics v0.5.3 + github.com/hashicorp/go-plugin v1.6.2 + github.com/mitchellh/mapstructure v1.5.0 + github.com/pelletier/go-toml/v2 v2.2.2 + github.com/prometheus/client_golang v1.20.5 + github.com/prometheus/common v0.60.1 + github.com/rs/zerolog v1.33.0 + github.com/spf13/cobra v1.8.1 + github.com/spf13/pflag v1.0.5 + github.com/spf13/viper v1.19.0 + github.com/stretchr/testify v1.9.0 + golang.org/x/sync v0.9.0 + google.golang.org/grpc v1.68.0 + google.golang.org/protobuf v1.35.1 +) + +require ( + cosmossdk.io/errors/v2 v2.0.0-20240731132947-df72853b3ca5 // indirect + cosmossdk.io/schema v0.3.1-0.20241010135032-192601639cac // indirect + github.com/DataDog/datadog-go v4.8.3+incompatible // indirect + github.com/DataDog/zstd v1.5.5 // indirect + github.com/Microsoft/go-winio v0.6.1 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/cockroachdb/errors v1.11.1 // indirect + github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect + github.com/cockroachdb/pebble v1.1.0 // indirect + github.com/cockroachdb/redact v1.1.5 // indirect + github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect + github.com/cosmos/iavl v1.3.1 // indirect + github.com/cosmos/ics23/go v0.11.0 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/emicklei/dot v1.6.2 // indirect + github.com/fatih/color v1.18.0 // indirect + github.com/fsnotify/fsnotify v1.8.0 // indirect + github.com/getsentry/sentry-go v0.27.0 // indirect + github.com/gogo/googleapis v1.4.1 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/snappy v0.0.4 // indirect + github.com/google/btree v1.1.2 // indirect + github.com/google/go-cmp v0.6.0 // indirect + github.com/hashicorp/go-immutable-radix v1.3.1 // indirect + github.com/hashicorp/golang-lru v1.0.2 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/hashicorp/yamux v0.1.2 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/jhump/protoreflect v1.17.0 // indirect + github.com/klauspost/compress v1.17.9 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/linxGnu/grocksdb v1.9.3 // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-sqlite3 v1.14.22 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/oklog/run v1.1.0 // indirect + github.com/onsi/gomega v1.28.1 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/procfs v0.15.1 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect + github.com/sagikazarmark/locafero v0.4.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.11.0 // indirect + github.com/spf13/cast v1.7.0 // indirect + github.com/stretchr/objx v0.5.2 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect + github.com/tendermint/go-amino v0.16.0 // indirect + github.com/tidwall/btree v1.7.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/crypto v0.28.0 // indirect + golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc // indirect + golang.org/x/mod v0.17.0 // indirect + golang.org/x/net v0.30.0 // indirect + golang.org/x/sys v0.27.0 // indirect + golang.org/x/text v0.20.0 // indirect + golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect + google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/server/v2/go.sum b/server/v2/go.sum new file mode 100644 index 000000000000..dc39111546ed --- /dev/null +++ b/server/v2/go.sum @@ -0,0 +1,507 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cosmossdk.io/core/testing v0.0.0-20240923163230-04da382a9f29 h1:NxxUo0GMJUbIuVg0R70e3cbn9eFTEuMr7ev1AFvypdY= +cosmossdk.io/core/testing v0.0.0-20240923163230-04da382a9f29/go.mod h1:8s2tPeJtSiQuoyPmr2Ag7meikonISO4Fv4MoO8+ORrs= +cosmossdk.io/errors/v2 v2.0.0-20240731132947-df72853b3ca5 h1:IQNdY2kB+k+1OM2DvqFG1+UgeU1JzZrWtwuWzI3ZfwA= +cosmossdk.io/errors/v2 v2.0.0-20240731132947-df72853b3ca5/go.mod h1:0CuYKkFHxc1vw2JC+t21THBCALJVROrWVR/3PQ1urpc= +cosmossdk.io/log v1.4.1 h1:wKdjfDRbDyZRuWa8M+9nuvpVYxrEOwbD/CA8hvhU8QM= +cosmossdk.io/log v1.4.1/go.mod h1:k08v0Pyq+gCP6phvdI6RCGhLf/r425UT6Rk/m+o74rU= +cosmossdk.io/schema v0.3.1-0.20241010135032-192601639cac h1:3joNZZWZ3k7fMsrBDL1ktuQ2xQwYLZOaDhkruadDFmc= +cosmossdk.io/schema v0.3.1-0.20241010135032-192601639cac/go.mod h1:RDAhxIeNB4bYqAlF4NBJwRrgtnciMcyyg0DOKnhNZQQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/DataDog/datadog-go v4.8.3+incompatible h1:fNGaYSuObuQb5nzeTQqowRAd9bpDIRRV4/gUtIBjh8Q= +github.com/DataDog/datadog-go v4.8.3+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ= +github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= +github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= +github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= +github.com/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/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +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/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw= +github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= +github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f h1:otljaYPt5hWxV3MUfO5dFPFiOXg9CyG5/kCfayTqsJ4= +github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= +github.com/cockroachdb/errors v1.11.1 h1:xSEW75zKaKCWzR3OfxXUxgrk/NtT4G1MiOv5lWZazG8= +github.com/cockroachdb/errors v1.11.1/go.mod h1:8MUxA3Gi6b25tYlFEBGLf+D8aISL+M4MIpiWMSNRfxw= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= +github.com/cockroachdb/pebble v1.1.0 h1:pcFh8CdCIt2kmEpK0OIatq67Ln9uGDYY3d5XnE0LJG4= +github.com/cockroachdb/pebble v1.1.0/go.mod h1:sEHm5NOXxyiAoKWhoFxT8xMgd/f3RA6qUqQ1BXKrh2E= +github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= +github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cosmos/cosmos-proto v1.0.0-beta.5 h1:eNcayDLpip+zVLRLYafhzLvQlSmyab+RC5W7ZfmxJLA= +github.com/cosmos/cosmos-proto v1.0.0-beta.5/go.mod h1:hQGLpiIUloJBMdQMMWb/4wRApmI9hjHH05nefC0Ojec= +github.com/cosmos/gogogateway v1.2.0 h1:Ae/OivNhp8DqBi/sh2A8a1D0y638GpL3tkmLQAiKxTE= +github.com/cosmos/gogogateway v1.2.0/go.mod h1:iQpLkGWxYcnCdz5iAdLcRBSw3h7NXeOkZ4GUkT+tbFI= +github.com/cosmos/gogoproto v1.4.2/go.mod h1:cLxOsn1ljAHSV527CHOtaIP91kK6cCrZETRBrkzItWU= +github.com/cosmos/gogoproto v1.7.0 h1:79USr0oyXAbxg3rspGh/m4SWNyoz/GLaAh0QlCe2fro= +github.com/cosmos/gogoproto v1.7.0/go.mod h1:yWChEv5IUEYURQasfyBW5ffkMHR/90hiHgbNgrtp4j0= +github.com/cosmos/iavl v1.3.1 h1:+W1G2uSUtJMqMGpwz/fKiwZxY2DDT/9/0hyNLm6Geu0= +github.com/cosmos/iavl v1.3.1/go.mod h1:T6SfBcyhulVIY2G/ZtAtQm/QiJvsuhIos52V4dWYk88= +github.com/cosmos/ics23/go v0.11.0 h1:jk5skjT0TqX5e5QJbEnwXIS2yI2vnmLOgpQPeM5RtnU= +github.com/cosmos/ics23/go v0.11.0/go.mod h1:A8OjxPE67hHST4Icw94hOxxFEJMBG031xIGF/JHNIY0= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emicklei/dot v1.6.2 h1:08GN+DD79cy/tzN6uLCT84+2Wk9u+wvqP+Hkx/dIR8A= +github.com/emicklei/dot v1.6.2/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= +github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +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/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= +github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= +github.com/go-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-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-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/googleapis v1.4.1-0.20201022092350-68b0159b7869/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= +github.com/gogo/googleapis v1.4.1 h1:1Yx4Myt7BxzvUr5ldGSbwYiZG6t9wGBZ+8/fX3Wvtq0= +github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +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/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= +github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.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.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= +github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= +github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-metrics v0.5.3 h1:M5uADWMOGCTUNU1YuC4hfknOeHNaX54LDm4oYSucoNE= +github.com/hashicorp/go-metrics v0.5.3/go.mod h1:KEjodfebIOuBYSAe/bHTm+HChmKSxAOXPBieMLYozDE= +github.com/hashicorp/go-plugin v1.6.2 h1:zdGAEd0V1lCaU0u+MxWQhtSDQmahpkwOun8U8EiRVog= +github.com/hashicorp/go-plugin v1.6.2/go.mod h1:CkgLQ5CZqNmdL9U9JzM532t8ZiYQ35+pj3b1FD37R0Q= +github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +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/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8= +github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jhump/protoreflect v1.17.0 h1:qOEr613fac2lOuTgWN4tPAtLL7fUSbuJL5X5XumQh94= +github.com/jhump/protoreflect v1.17.0/go.mod h1:h9+vUUL38jiBzck8ck+6G/aeMX8Z4QUY/NiJPwPNi+8= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +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/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= +github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +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.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/linxGnu/grocksdb v1.9.3 h1:s1cbPcOd0cU2SKXRG1nEqCOWYAELQjdqg3RVI2MH9ik= +github.com/linxGnu/grocksdb v1.9.3/go.mod h1:QYiYypR2d4v63Wj1adOOfzglnoII0gLj3PNh4fZkcFA= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mattn/go-colorable v0.1.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.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.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-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= +github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +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/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +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/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +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/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= +github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= +github.com/onsi/gomega v1.28.1 h1:MijcGUbfYuznzK/5R4CPNoUP/9Xvuo20sXfEm6XxoTA= +github.com/onsi/gomega v1.28.1/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= +github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= +github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= +github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pkg/errors v0.8.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/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= +github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +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.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= +github.com/prometheus/common v0.60.1 h1:FUas6GcOw66yB/73KC+BOZoFJmbo/1pojoILArPAaSc= +github.com/prometheus/common v0.60.1/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= +github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= +github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= +github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= +github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs= +github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= +github.com/tendermint/go-amino v0.16.0 h1:GyhmgQKvqF82e2oZeuMSp9JTN0N09emoSZlb2lyGa2E= +github.com/tendermint/go-amino v0.16.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME= +github.com/tidwall/btree v1.7.0 h1:L1fkJH/AuEh5zBnnBbmTwQ5Lt+bRJ5A8EWecslvo9iI= +github.com/tidwall/btree v1.7.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY= +github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= +go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +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-20240531132922-fd00a4e0eefc h1:O9NuF4s+E/PvMIy+9IUZB9znFwUIXEWSstNjek6VpVg= +golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/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.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-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-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= +golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/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-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= +golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-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-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86/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-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-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= +golang.org/x/sys v0.27.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/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/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.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= +golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/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-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.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-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20220314164441-57ef72a4c106/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= +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-20240903143218-8af14fe29dc1 h1:hjSy6tcFQZ171igDaN5QHOw2n6vx40juYbC/x67CEhc= +google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:qpvKtACPCQhAdu3PyQgV4l3LMXZEtft7y8QcarRsp9I= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28 h1:XVhgTWWV3kGQlwJHR3upFWZeTsei6Oks1apkZSeonIE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= +google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.68.0 h1:aHQeeJbo8zAkAa3pRzrVjZlbz6uSfeOXlJNQM0RAbz0= +google.golang.org/grpc v1.68.0/go.mod h1:fmSPC5AsjSBCK54MyHRx48kpOti1/jRfOlwEWywNjWA= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= +google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +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-20190902080502-41f04d3bba15/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/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.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.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= +gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/server/v2/store/server.go b/server/v2/store/server.go new file mode 100644 index 000000000000..247ee624251e --- /dev/null +++ b/server/v2/store/server.go @@ -0,0 +1,92 @@ +package store + +import ( + "context" + "fmt" + + "github.com/spf13/cobra" + + "cosmossdk.io/core/server" + "cosmossdk.io/core/transaction" + serverv2 "cosmossdk.io/server/v2" + storev2 "cosmossdk.io/store/v2" + "cosmossdk.io/store/v2/root" +) + +var ( + _ serverv2.ServerComponent[transaction.Tx] = (*Server[transaction.Tx])(nil) + _ serverv2.HasConfig = (*Server[transaction.Tx])(nil) + _ serverv2.HasCLICommands = (*Server[transaction.Tx])(nil) +) + +const ServerName = "store" + +// Server manages store config and contains prune & snapshot commands +type Server[T transaction.Tx] struct { + config *root.Config + store storev2.RootStore +} + +func New[T transaction.Tx](store storev2.RootStore, cfg server.ConfigMap) (*Server[T], error) { + config, err := UnmarshalConfig(cfg) + if err != nil { + return nil, err + } + return &Server[T]{ + store: store, + config: config, + }, nil +} + +func (s *Server[T]) Name() string { + return ServerName +} + +func (s *Server[T]) Start(context.Context) error { + return nil +} + +func (s *Server[T]) Stop(context.Context) error { + return nil +} + +func (s *Server[T]) CLICommands() serverv2.CLIConfig { + return serverv2.CLIConfig{ + Commands: []*cobra.Command{ + s.PrunesCmd(), + s.ExportSnapshotCmd(), + s.DeleteSnapshotCmd(), + s.ListSnapshotsCmd(), + s.DumpArchiveCmd(), + s.LoadArchiveCmd(), + s.RestoreSnapshotCmd(), + }, + } +} + +func (s *Server[T]) Config() any { + if s.config == nil || s.config.AppDBBackend == "" { + return root.DefaultConfig() + } + + return s.config +} + +// UnmarshalConfig unmarshals the store config from the given map. +// If the config is not found in the map, the default config is returned. +// If the home directory is found in the map, it sets the home directory in the config. +// An empty home directory *is* permitted at this stage, but attempting to build +// the store with an empty home directory will fail. +func UnmarshalConfig(cfg map[string]any) (*root.Config, error) { + config := &root.Config{ + Options: root.DefaultStoreOptions(), + } + if err := serverv2.UnmarshalSubConfig(cfg, ServerName, config); err != nil { + return nil, fmt.Errorf("failed to unmarshal store config: %w", err) + } + home := cfg[serverv2.FlagHome] + if home != nil { + config.Home = home.(string) + } + return config, nil +} diff --git a/simapp/v2/app_test.go b/simapp/v2/app_test.go index c3c51489a7e5..3ea361e3becb 100644 --- a/simapp/v2/app_test.go +++ b/simapp/v2/app_test.go @@ -94,6 +94,7 @@ func NewTestApp(t *testing.T) (*SimApp[transaction.Tx], context.Context) { ChainId: "theChain", AppHash: ci.Hash, IsGenesis: true, + Height: 1, }, genesisBytes, nil, @@ -103,7 +104,7 @@ func NewTestApp(t *testing.T) (*SimApp[transaction.Tx], context.Context) { changes, err := newState.GetStateChanges() require.NoError(t, err) - _, err = st.Commit(&store.Changeset{Changes: changes}) + _, err = st.Commit(&store.Changeset{Version: 1, Changes: changes}) require.NoError(t, err) return app, ctx @@ -119,6 +120,7 @@ func MoveNextBlock(t *testing.T, app *SimApp[transaction.Tx], ctx context.Contex require.NoError(t, err) height, err := app.LoadLatestHeight() + height++ require.NoError(t, err) // TODO: this is a hack to set the comet info in the context for distribution module dependency. @@ -132,7 +134,7 @@ func MoveNextBlock(t *testing.T, app *SimApp[transaction.Tx], ctx context.Contex _, newState, err := app.DeliverBlock( ctx, &server.BlockRequest[transaction.Tx]{ - Height: height + 1, + Height: height, Time: time.Now(), Hash: bz[:], AppHash: ci.Hash, @@ -142,7 +144,7 @@ func MoveNextBlock(t *testing.T, app *SimApp[transaction.Tx], ctx context.Contex changes, err := newState.GetStateChanges() require.NoError(t, err) - _, err = st.Commit(&store.Changeset{Changes: changes}) + _, err = st.Commit(&store.Changeset{Version: height, Changes: changes}) require.NoError(t, err) } diff --git a/simapp/v2/go.mod b/simapp/v2/go.mod index 34bc985d778d..658cbab465f8 100644 --- a/simapp/v2/go.mod +++ b/simapp/v2/go.mod @@ -292,3 +292,20 @@ replace ( // replace broken goleveldb github.com/syndtr/goleveldb => github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 ) +<<<<<<< HEAD +======= + +// server v2 integration +replace ( + cosmossdk.io/api => ../../api + cosmossdk.io/core => ../../core + cosmossdk.io/core/testing => ../../core/testing + cosmossdk.io/runtime/v2 => ../../runtime/v2 + cosmossdk.io/server/v2 => ../../server/v2 + cosmossdk.io/server/v2/appmanager => ../../server/v2/appmanager + cosmossdk.io/server/v2/cometbft => ../../server/v2/cometbft + cosmossdk.io/server/v2/stf => ../../server/v2/stf + cosmossdk.io/store => ../../store + cosmossdk.io/store/v2 => ../../store/v2 +) +>>>>>>> 43e28b43a (refactor(store/v2): simplify genesis flow (#22435)) diff --git a/simapp/v2/go.sum b/simapp/v2/go.sum index ba1f09dadc9f..83158fa654a1 100644 --- a/simapp/v2/go.sum +++ b/simapp/v2/go.sum @@ -192,6 +192,7 @@ cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xX cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg= 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= +<<<<<<< HEAD cosmossdk.io/api v0.7.3-0.20240924065902-eb7653cfecdf h1:CttA/mEIxGm4E7vwrjUpju7/Iespns08d9bOza70cIc= cosmossdk.io/api v0.7.3-0.20240924065902-eb7653cfecdf/go.mod h1:YMfx2ATpgITsoydD3hIBa8IkDHtyXp/14rmG0d3sEew= cosmossdk.io/collections v0.4.1-0.20241107084833-00f3065e70ee h1:OLqvi9ekfShobmdgr4Q/8pu+LjzYJSrNl9tiadPg2xY= @@ -200,6 +201,8 @@ cosmossdk.io/core v1.0.0-alpha.5 h1:McjYXAQ6XcT20v2uHyH7PhoWH8V+mebzfVFqT3GinsI= cosmossdk.io/core v1.0.0-alpha.5/go.mod h1:3u9cWq1FAVtiiCrDPpo4LhR+9V6k/ycSG4/Y/tREWCY= cosmossdk.io/core/testing v0.0.0-20240923163230-04da382a9f29 h1:NxxUo0GMJUbIuVg0R70e3cbn9eFTEuMr7ev1AFvypdY= cosmossdk.io/core/testing v0.0.0-20240923163230-04da382a9f29/go.mod h1:8s2tPeJtSiQuoyPmr2Ag7meikonISO4Fv4MoO8+ORrs= +======= +>>>>>>> 43e28b43a (refactor(store/v2): simplify genesis flow (#22435)) cosmossdk.io/depinject v1.1.0 h1:wLan7LG35VM7Yo6ov0jId3RHWCGRhe8E8bsuARorl5E= cosmossdk.io/depinject v1.1.0/go.mod h1:kkI5H9jCGHeKeYWXTqYdruogYrEeWvBQCw1Pj4/eCFI= cosmossdk.io/errors v1.0.1 h1:bzu+Kcr0kS/1DuPBtUFdWjzLqyUuCiyHjyJB6srBV/0= diff --git a/store/v2/commitment/iavl/tree.go b/store/v2/commitment/iavl/tree.go new file mode 100644 index 000000000000..5503218e66a1 --- /dev/null +++ b/store/v2/commitment/iavl/tree.go @@ -0,0 +1,164 @@ +package iavl + +import ( + "fmt" + + "github.com/cosmos/iavl" + ics23 "github.com/cosmos/ics23/go" + + "cosmossdk.io/core/log" + corestore "cosmossdk.io/core/store" + "cosmossdk.io/store/v2" + "cosmossdk.io/store/v2/commitment" +) + +var ( + _ commitment.Tree = (*IavlTree)(nil) + _ commitment.Reader = (*IavlTree)(nil) + _ store.PausablePruner = (*IavlTree)(nil) +) + +// IavlTree is a wrapper around iavl.MutableTree. +type IavlTree struct { + tree *iavl.MutableTree +} + +// NewIavlTree creates a new IavlTree instance. +func NewIavlTree(db corestore.KVStoreWithBatch, logger log.Logger, cfg *Config) *IavlTree { + tree := iavl.NewMutableTree(db, cfg.CacheSize, cfg.SkipFastStorageUpgrade, logger, iavl.AsyncPruningOption(true)) + return &IavlTree{ + tree: tree, + } +} + +// Remove removes the given key from the tree. +func (t *IavlTree) Remove(key []byte) error { + _, _, err := t.tree.Remove(key) + if err != nil { + return err + } + return nil +} + +// Set sets the given key-value pair in the tree. +func (t *IavlTree) Set(key, value []byte) error { + _, err := t.tree.Set(key, value) + return err +} + +// Hash returns the hash of the latest saved version of the tree. +func (t *IavlTree) Hash() []byte { + return t.tree.Hash() +} + +// Version returns the current version of the tree. +func (t *IavlTree) Version() uint64 { + return uint64(t.tree.Version()) +} + +// WorkingHash returns the working hash of the tree. +// Danger! iavl.MutableTree.WorkingHash() is a mutating operation! +// It advances the tree version by 1. +func (t *IavlTree) WorkingHash() []byte { + return t.tree.WorkingHash() +} + +// LoadVersion loads the state at the given version. +func (t *IavlTree) LoadVersion(version uint64) error { + return t.tree.LoadVersionForOverwriting(int64(version)) +} + +// Commit commits the current state to the tree. +func (t *IavlTree) Commit() ([]byte, uint64, error) { + hash, v, err := t.tree.SaveVersion() + return hash, uint64(v), err +} + +// GetProof returns a proof for the given key and version. +func (t *IavlTree) GetProof(version uint64, key []byte) (*ics23.CommitmentProof, error) { + immutableTree, err := t.tree.GetImmutable(int64(version)) + if err != nil { + return nil, fmt.Errorf("failed to get immutable tree at version %d: %w", version, err) + } + + return immutableTree.GetProof(key) +} + +// Get implements the Reader interface. +func (t *IavlTree) Get(version uint64, key []byte) ([]byte, error) { + immutableTree, err := t.tree.GetImmutable(int64(version)) + if err != nil { + return nil, fmt.Errorf("failed to get immutable tree at version %d: %w", version, err) + } + + return immutableTree.Get(key) +} + +// Iterator implements the Reader interface. +func (t *IavlTree) Iterator(version uint64, start, end []byte, ascending bool) (corestore.Iterator, error) { + immutableTree, err := t.tree.GetImmutable(int64(version)) + if err != nil { + return nil, fmt.Errorf("failed to get immutable tree at version %d: %w", version, err) + } + + return immutableTree.Iterator(start, end, ascending) +} + +// GetLatestVersion returns the latest version of the tree. +func (t *IavlTree) GetLatestVersion() (uint64, error) { + v, err := t.tree.GetLatestVersion() + return uint64(v), err +} + +// SetInitialVersion sets the initial version of the database. +func (t *IavlTree) SetInitialVersion(version uint64) error { + t.tree.SetInitialVersion(version) + return nil +} + +// Prune prunes all versions up to and including the provided version. +func (t *IavlTree) Prune(version uint64) error { + return t.tree.DeleteVersionsTo(int64(version)) +} + +// PausePruning pauses the pruning process. +func (t *IavlTree) PausePruning(pause bool) { + if pause { + t.tree.SetCommitting() + } else { + t.tree.UnsetCommitting() + } +} + +// Export exports the tree exporter at the given version. +func (t *IavlTree) Export(version uint64) (commitment.Exporter, error) { + tree, err := t.tree.GetImmutable(int64(version)) + if err != nil { + return nil, err + } + exporter, err := tree.Export() + if err != nil { + return nil, err + } + + return &Exporter{ + exporter: exporter, + }, nil +} + +// Import imports the tree importer at the given version. +func (t *IavlTree) Import(version uint64) (commitment.Importer, error) { + importer, err := t.tree.Import(int64(version)) + if err != nil { + return nil, err + } + + return &Importer{ + importer: importer, + }, nil +} + +// Close closes the iavl tree. +func (t *IavlTree) Close() error { + return t.tree.Close() +} diff --git a/store/v2/commitment/mem/tree.go b/store/v2/commitment/mem/tree.go new file mode 100644 index 000000000000..cbc28ce7d9ae --- /dev/null +++ b/store/v2/commitment/mem/tree.go @@ -0,0 +1,67 @@ +package mem + +import ( + ics23 "github.com/cosmos/ics23/go" + + "cosmossdk.io/store/v2/commitment" + "cosmossdk.io/store/v2/db" +) + +var _ commitment.Tree = (*Tree)(nil) + +// Tree is a simple in-memory implementation of commitment.Tree. +type Tree struct { + *db.MemDB +} + +func (t *Tree) Remove(key []byte) error { + return t.MemDB.Delete(key) +} + +func (t *Tree) GetLatestVersion() (uint64, error) { + return 0, nil +} + +func (t *Tree) Hash() []byte { + return nil +} + +func (t *Tree) Version() uint64 { + return 0 +} + +func (t *Tree) LoadVersion(version uint64) error { + return nil +} + +func (t *Tree) Commit() ([]byte, uint64, error) { + return nil, 0, nil +} + +func (t *Tree) SetInitialVersion(version uint64) error { + return nil +} + +func (t *Tree) GetProof(version uint64, key []byte) (*ics23.CommitmentProof, error) { + return nil, nil +} + +func (t *Tree) Get(version uint64, key []byte) ([]byte, error) { + return t.MemDB.Get(key) +} + +func (t *Tree) Prune(version uint64) error { + return nil +} + +func (t *Tree) Export(version uint64) (commitment.Exporter, error) { + return nil, nil +} + +func (t *Tree) Import(version uint64) (commitment.Importer, error) { + return nil, nil +} + +func New() *Tree { + return &Tree{MemDB: db.NewMemDB()} +} diff --git a/store/v2/commitment/store.go b/store/v2/commitment/store.go new file mode 100644 index 000000000000..e9f2ee8379c7 --- /dev/null +++ b/store/v2/commitment/store.go @@ -0,0 +1,558 @@ +package commitment + +import ( + "errors" + "fmt" + "io" + "maps" + "math" + "slices" + + protoio "github.com/cosmos/gogoproto/io" + + corelog "cosmossdk.io/core/log" + corestore "cosmossdk.io/core/store" + "cosmossdk.io/store/v2" + "cosmossdk.io/store/v2/internal" + "cosmossdk.io/store/v2/internal/conv" + "cosmossdk.io/store/v2/proof" + "cosmossdk.io/store/v2/snapshots" + snapshotstypes "cosmossdk.io/store/v2/snapshots/types" +) + +var ( + _ store.Committer = (*CommitStore)(nil) + _ store.UpgradeableStore = (*CommitStore)(nil) + _ snapshots.CommitSnapshotter = (*CommitStore)(nil) + _ store.PausablePruner = (*CommitStore)(nil) + + // NOTE: It is not recommended to use the CommitStore as a reader. This is only used + // during the migration process. Generally, the SC layer does not provide a reader + // in the store/v2. + _ store.VersionedReader = (*CommitStore)(nil) +) + +// MountTreeFn is a function that mounts a tree given a store key. +// It is used to lazily mount trees when needed (e.g. during upgrade or proof generation). +type MountTreeFn func(storeKey string) (Tree, error) + +// CommitStore is a wrapper around multiple Tree objects mapped by a unique store +// key. Each store key reflects dedicated and unique usage within a module. A caller +// can construct a CommitStore with one or more store keys. It is expected that a +// RootStore use a CommitStore as an abstraction to handle multiple store keys +// and trees. +type CommitStore struct { + logger corelog.Logger + metadata *MetadataStore + multiTrees map[string]Tree + // oldTrees is a map of store keys to old trees that have been deleted or renamed. + // It is used to get the proof for the old store keys. + oldTrees map[string]Tree +} + +// NewCommitStore creates a new CommitStore instance. +func NewCommitStore(trees, oldTrees map[string]Tree, db corestore.KVStoreWithBatch, logger corelog.Logger) (*CommitStore, error) { + return &CommitStore{ + logger: logger, + multiTrees: trees, + oldTrees: oldTrees, + metadata: NewMetadataStore(db), + }, nil +} + +func (c *CommitStore) WriteChangeset(cs *corestore.Changeset) error { + for _, pairs := range cs.Changes { + key := conv.UnsafeBytesToStr(pairs.Actor) + + tree, ok := c.multiTrees[key] + if !ok { + return fmt.Errorf("store key %s not found in multiTrees", key) + } + for _, kv := range pairs.StateChanges { + if kv.Remove { + if err := tree.Remove(kv.Key); err != nil { + return err + } + } else if err := tree.Set(kv.Key, kv.Value); err != nil { + return err + } + } + } + + return nil +} + +func (c *CommitStore) LoadVersion(targetVersion uint64) error { + storeKeys := make([]string, 0, len(c.multiTrees)) + for storeKey := range c.multiTrees { + storeKeys = append(storeKeys, storeKey) + } + return c.loadVersion(targetVersion, storeKeys) +} + +// LoadVersionAndUpgrade implements store.UpgradeableStore. +func (c *CommitStore) LoadVersionAndUpgrade(targetVersion uint64, upgrades *corestore.StoreUpgrades) error { + // deterministic iteration order for upgrades (as the underlying store may change and + // upgrades make store changes where the execution order may matter) + storeKeys := slices.Sorted(maps.Keys(c.multiTrees)) + removeTree := func(storeKey string) error { + if oldTree, ok := c.multiTrees[storeKey]; ok { + if err := oldTree.Close(); err != nil { + return err + } + delete(c.multiTrees, storeKey) + } + return nil + } + + newStoreKeys := make([]string, 0, len(c.multiTrees)) + removedStoreKeys := make([]string, 0) + for _, storeKey := range storeKeys { + // If it has been deleted, remove the tree. + if upgrades.IsDeleted(storeKey) { + if err := removeTree(storeKey); err != nil { + return err + } + removedStoreKeys = append(removedStoreKeys, storeKey) + continue + } + + // If it has been added, set the initial version. + if upgrades.IsAdded(storeKey) { + if err := c.multiTrees[storeKey].SetInitialVersion(targetVersion + 1); err != nil { + return err + } + // This is the empty tree, no need to load the version. + continue + } + + newStoreKeys = append(newStoreKeys, storeKey) + } + + if err := c.metadata.flushRemovedStoreKeys(targetVersion, removedStoreKeys); err != nil { + return err + } + + return c.loadVersion(targetVersion, newStoreKeys) +} + +func (c *CommitStore) loadVersion(targetVersion uint64, storeKeys []string) error { + // Rollback the metadata to the target version. + latestVersion, err := c.GetLatestVersion() + if err != nil { + return err + } + if targetVersion < latestVersion { + for version := latestVersion; version > targetVersion; version-- { + if err = c.metadata.deleteCommitInfo(version); err != nil { + return err + } + } + if err := c.metadata.setLatestVersion(targetVersion); err != nil { + return err + } + } + + for _, storeKey := range storeKeys { + if err := c.multiTrees[storeKey].LoadVersion(targetVersion); err != nil { + return err + } + } + + // If the target version is greater than the latest version, it is the snapshot + // restore case, we should create a new commit info for the target version. + if targetVersion > latestVersion { + cInfo, err := c.GetCommitInfo(targetVersion) + if err != nil { + return err + } + return c.metadata.flushCommitInfo(targetVersion, cInfo) + } + + return nil +} + +func (c *CommitStore) Commit(version uint64) (*proof.CommitInfo, error) { + storeInfos := make([]proof.StoreInfo, 0, len(c.multiTrees)) + + for storeKey, tree := range c.multiTrees { + if internal.IsMemoryStoreKey(storeKey) { + continue + } + hash, cversion, err := tree.Commit() + if err != nil { + return nil, err + } + if cversion != version { + return nil, fmt.Errorf("commit version %d does not match the target version %d", cversion, version) + } + commitID := proof.CommitID{ + Version: version, + Hash: hash, + } + storeInfos = append(storeInfos, proof.StoreInfo{ + Name: []byte(storeKey), + CommitID: commitID, + }) + } + + cInfo := &proof.CommitInfo{ + Version: version, + StoreInfos: storeInfos, + } + + if err := c.metadata.flushCommitInfo(version, cInfo); err != nil { + return nil, err + } + + return cInfo, nil +} + +func (c *CommitStore) SetInitialVersion(version uint64) error { + for _, tree := range c.multiTrees { + if err := tree.SetInitialVersion(version); err != nil { + return err + } + } + + return nil +} + +func (c *CommitStore) GetProof(storeKey []byte, version uint64, key []byte) ([]proof.CommitmentOp, error) { + rawStoreKey := conv.UnsafeBytesToStr(storeKey) + tree, ok := c.multiTrees[rawStoreKey] + if !ok { + tree, ok = c.oldTrees[rawStoreKey] + if !ok { + return nil, fmt.Errorf("store %s not found", rawStoreKey) + } + } + + iProof, err := tree.GetProof(version, key) + if err != nil { + return nil, err + } + cInfo, err := c.metadata.GetCommitInfo(version) + if err != nil { + return nil, err + } + if cInfo == nil { + return nil, fmt.Errorf("commit info not found for version %d", version) + } + commitOp := proof.NewIAVLCommitmentOp(key, iProof) + _, storeCommitmentOp, err := cInfo.GetStoreProof(storeKey) + if err != nil { + return nil, err + } + + return []proof.CommitmentOp{commitOp, *storeCommitmentOp}, nil +} + +// getReader returns a reader for the given store key. It will return an error if the +// store key does not exist or the tree does not implement the Reader interface. +// WARNING: This function is only used during the migration process. The SC layer +// generally does not provide a reader for the CommitStore. +func (c *CommitStore) getReader(storeKey string) (Reader, error) { + tree, ok := c.multiTrees[storeKey] + if !ok { + return nil, fmt.Errorf("store %s not found", storeKey) + } + + reader, ok := tree.(Reader) + if !ok { + return nil, fmt.Errorf("tree for store %s does not implement Reader", storeKey) + } + + return reader, nil +} + +// VersionExists implements store.VersionedReader. +func (c *CommitStore) VersionExists(version uint64) (bool, error) { + ci, err := c.metadata.GetCommitInfo(version) + return ci != nil, err +} + +// Get implements store.VersionedReader. +func (c *CommitStore) Get(storeKey []byte, version uint64, key []byte) ([]byte, error) { + reader, err := c.getReader(conv.UnsafeBytesToStr(storeKey)) + if err != nil { + return nil, err + } + + bz, err := reader.Get(version, key) + if err != nil { + return nil, fmt.Errorf("failed to get key %s from store %s: %w", key, storeKey, err) + } + + return bz, nil +} + +// Has implements store.VersionedReader. +func (c *CommitStore) Has(storeKey []byte, version uint64, key []byte) (bool, error) { + val, err := c.Get(storeKey, version, key) + return val != nil, err +} + +// Iterator implements store.VersionedReader. +func (c *CommitStore) Iterator(storeKey []byte, version uint64, start, end []byte) (corestore.Iterator, error) { + reader, err := c.getReader(conv.UnsafeBytesToStr(storeKey)) + if err != nil { + return nil, err + } + + return reader.Iterator(version, start, end, true) +} + +// ReverseIterator implements store.VersionedReader. +func (c *CommitStore) ReverseIterator(storeKey []byte, version uint64, start, end []byte) (corestore.Iterator, error) { + reader, err := c.getReader(conv.UnsafeBytesToStr(storeKey)) + if err != nil { + return nil, err + } + + return reader.Iterator(version, start, end, false) +} + +// Prune implements store.Pruner. +func (c *CommitStore) Prune(version uint64) error { + // prune the metadata + for v := version; v > 0; v-- { + if err := c.metadata.deleteCommitInfo(v); err != nil { + return err + } + } + // prune the trees + for _, tree := range c.multiTrees { + if err := tree.Prune(version); err != nil { + return err + } + } + // prune the removed store keys + if err := c.pruneRemovedStoreKeys(version); err != nil { + return err + } + + return nil +} + +func (c *CommitStore) pruneRemovedStoreKeys(version uint64) error { + clearKVStore := func(storeKey []byte, version uint64) (err error) { + tree, ok := c.oldTrees[string(storeKey)] + if !ok { + return fmt.Errorf("store %s not found in oldTrees", storeKey) + } + return tree.Prune(version) + } + return c.metadata.deleteRemovedStoreKeys(version, clearKVStore) +} + +// PausePruning implements store.PausablePruner. +func (c *CommitStore) PausePruning(pause bool) { + for _, tree := range c.multiTrees { + if pruner, ok := tree.(store.PausablePruner); ok { + pruner.PausePruning(pause) + } + } +} + +// Snapshot implements snapshotstypes.CommitSnapshotter. +func (c *CommitStore) Snapshot(version uint64, protoWriter protoio.Writer) error { + if version == 0 { + return errors.New("the snapshot version must be greater than 0") + } + + latestVersion, err := c.GetLatestVersion() + if err != nil { + return err + } + if version > latestVersion { + return fmt.Errorf("the snapshot version %d is greater than the latest version %d", version, latestVersion) + } + + for storeKey, tree := range c.multiTrees { + // TODO: check the parallelism of this loop + if err := func() error { + exporter, err := tree.Export(version) + if err != nil { + return fmt.Errorf("failed to export tree for version %d: %w", version, err) + } + defer exporter.Close() + + err = protoWriter.WriteMsg(&snapshotstypes.SnapshotItem{ + Item: &snapshotstypes.SnapshotItem_Store{ + Store: &snapshotstypes.SnapshotStoreItem{ + Name: storeKey, + }, + }, + }) + if err != nil { + return fmt.Errorf("failed to write store name: %w", err) + } + + for { + item, err := exporter.Next() + if errors.Is(err, ErrorExportDone) { + break + } else if err != nil { + return fmt.Errorf("failed to get the next export node: %w", err) + } + + if err = protoWriter.WriteMsg(&snapshotstypes.SnapshotItem{ + Item: &snapshotstypes.SnapshotItem_IAVL{ + IAVL: item, + }, + }); err != nil { + return fmt.Errorf("failed to write iavl node: %w", err) + } + } + + return nil + }(); err != nil { + return err + } + } + + return nil +} + +// Restore implements snapshotstypes.CommitSnapshotter. +func (c *CommitStore) Restore( + version uint64, + format uint32, + protoReader protoio.Reader, + chStorage chan<- *corestore.StateChanges, +) (snapshotstypes.SnapshotItem, error) { + var ( + importer Importer + snapshotItem snapshotstypes.SnapshotItem + storeKey []byte + ) + +loop: + for { + snapshotItem = snapshotstypes.SnapshotItem{} + err := protoReader.ReadMsg(&snapshotItem) + if errors.Is(err, io.EOF) { + break + } else if err != nil { + return snapshotstypes.SnapshotItem{}, fmt.Errorf("invalid protobuf message: %w", err) + } + + switch item := snapshotItem.Item.(type) { + case *snapshotstypes.SnapshotItem_Store: + if importer != nil { + if err := importer.Commit(); err != nil { + return snapshotstypes.SnapshotItem{}, fmt.Errorf("failed to commit importer: %w", err) + } + if err := importer.Close(); err != nil { + return snapshotstypes.SnapshotItem{}, fmt.Errorf("failed to close importer: %w", err) + } + } + + storeKey = []byte(item.Store.Name) + tree := c.multiTrees[item.Store.Name] + if tree == nil { + return snapshotstypes.SnapshotItem{}, fmt.Errorf("store %s not found", item.Store.Name) + } + importer, err = tree.Import(version) + if err != nil { + return snapshotstypes.SnapshotItem{}, fmt.Errorf("failed to import tree for version %d: %w", version, err) + } + defer importer.Close() + + case *snapshotstypes.SnapshotItem_IAVL: + if importer == nil { + return snapshotstypes.SnapshotItem{}, errors.New("received IAVL node item before store item") + } + node := item.IAVL + if node.Height > int32(math.MaxInt8) { + return snapshotstypes.SnapshotItem{}, fmt.Errorf("node height %v cannot exceed %v", + item.IAVL.Height, math.MaxInt8) + } + // Protobuf does not differentiate between []byte{} and nil, but fortunately IAVL does + // not allow nil keys nor nil values for leaf nodes, so we can always set them to empty. + if node.Key == nil { + node.Key = []byte{} + } + if node.Height == 0 { + if node.Value == nil { + node.Value = []byte{} + } + + // If the node is a leaf node, it will be written to the storage. + chStorage <- &corestore.StateChanges{ + Actor: storeKey, + StateChanges: []corestore.KVPair{ + { + Key: node.Key, + Value: node.Value, + }, + }, + } + } + err := importer.Add(node) + if err != nil { + return snapshotstypes.SnapshotItem{}, fmt.Errorf("failed to add node to importer: %w", err) + } + default: + break loop + } + } + + if importer != nil { + if err := importer.Commit(); err != nil { + return snapshotstypes.SnapshotItem{}, fmt.Errorf("failed to commit importer: %w", err) + } + } + + return snapshotItem, c.LoadVersion(version) +} + +func (c *CommitStore) GetCommitInfo(version uint64) (*proof.CommitInfo, error) { + // if the commit info is already stored, return it + ci, err := c.metadata.GetCommitInfo(version) + if err != nil { + return nil, err + } + if ci != nil { + return ci, nil + } + // otherwise built the commit info from the trees + storeInfos := make([]proof.StoreInfo, 0, len(c.multiTrees)) + for storeKey, tree := range c.multiTrees { + if internal.IsMemoryStoreKey(storeKey) { + continue + } + v := tree.Version() + if v != version { + return nil, fmt.Errorf("tree version %d does not match the target version %d", v, version) + } + bz := []byte(storeKey) + storeInfos = append(storeInfos, proof.StoreInfo{ + Name: bz, + CommitID: proof.CommitID{ + Version: v, + Hash: tree.Hash(), + }, + }) + } + + ci = &proof.CommitInfo{ + Version: version, + StoreInfos: storeInfos, + } + return ci, nil +} + +func (c *CommitStore) GetLatestVersion() (uint64, error) { + return c.metadata.GetLatestVersion() +} + +func (c *CommitStore) Close() error { + for _, tree := range c.multiTrees { + if err := tree.Close(); err != nil { + return err + } + } + return nil +} diff --git a/store/v2/commitment/store_bench_test.go b/store/v2/commitment/store_bench_test.go new file mode 100644 index 000000000000..c8343d942b72 --- /dev/null +++ b/store/v2/commitment/store_bench_test.go @@ -0,0 +1,129 @@ +//go:build rocksdb +// +build rocksdb + +package commitment_test + +import ( + "fmt" + "math/rand" + "testing" + + "github.com/stretchr/testify/require" + + corestore "cosmossdk.io/core/store" + coretesting "cosmossdk.io/core/testing" + "cosmossdk.io/store/v2/commitment" + "cosmossdk.io/store/v2/commitment/iavl" + dbm "cosmossdk.io/store/v2/db" +) + +var ( + storeKeys = []string{"store1", "store2", "store3"} + dbBackends = map[string]func(dataDir string) (corestore.KVStoreWithBatch, error){ + "rocksdb_opts": func(dataDir string) (corestore.KVStoreWithBatch, error) { + return dbm.NewRocksDB("test", dataDir) + }, + "pebbledb_opts": func(dataDir string) (corestore.KVStoreWithBatch, error) { + return dbm.NewPebbleDB("test", dataDir) + }, + "goleveldb_opts": func(dataDir string) (corestore.KVStoreWithBatch, error) { + return dbm.NewGoLevelDB("test", dataDir, nil) + }, + } + rng = rand.New(rand.NewSource(543210)) + changesets = make([]*corestore.Changeset, 1000) +) + +func init() { + for i := uint64(0); i < 1000; i++ { + cs := corestore.NewChangeset(i) + for _, storeKey := range storeKeys { + for j := 0; j < 100; j++ { + key := make([]byte, 16) + val := make([]byte, 16) + + _, err := rng.Read(key) + if err != nil { + panic(err) + } + _, err = rng.Read(val) + if err != nil { + panic(err) + } + + cs.AddKVPair([]byte(storeKey), corestore.KVPair{Key: key, Value: val}) + } + } + changesets[i] = cs + } +} + +func getCommitStore(b *testing.B, db corestore.KVStoreWithBatch) *commitment.CommitStore { + b.Helper() + multiTrees := make(map[string]commitment.Tree) + for _, storeKey := range storeKeys { + prefixDB := dbm.NewPrefixDB(db, []byte(storeKey)) + multiTrees[storeKey] = iavl.NewIavlTree(prefixDB, coretesting.NewNopLogger(), iavl.DefaultConfig()) + } + + sc, err := commitment.NewCommitStore(multiTrees, nil, db, coretesting.NewNopLogger()) + require.NoError(b, err) + + return sc +} + +func BenchmarkCommit(b *testing.B) { + for ty, fn := range dbBackends { + b.Run(fmt.Sprintf("backend_%s", ty), func(b *testing.B) { + b.ResetTimer() + b.ReportAllocs() + b.StopTimer() + for i := 0; i < b.N; i++ { + db, err := fn(b.TempDir()) + require.NoError(b, err) + sc := getCommitStore(b, db) + b.StartTimer() + for j, cs := range changesets { + require.NoError(b, sc.WriteChangeset(cs)) + _, err := sc.Commit(uint64(j + 1)) + require.NoError(b, err) + } + b.StopTimer() + require.NoError(b, db.Close()) + } + }) + } +} + +func BenchmarkGetProof(b *testing.B) { + for ty, fn := range dbBackends { + db, err := fn(b.TempDir()) + require.NoError(b, err) + sc := getCommitStore(b, db) + + b.Run(fmt.Sprintf("backend_%s", ty), func(b *testing.B) { + b.ResetTimer() + b.ReportAllocs() + b.StopTimer() + // commit some changesets + for i, cs := range changesets { + require.NoError(b, sc.WriteChangeset(cs)) + _, err = sc.Commit(uint64(i + 1)) + require.NoError(b, err) + } + b.StartTimer() + + for i := 0; i < b.N; i++ { + // non-existing proof + p, err := sc.GetProof([]byte(storeKeys[0]), 500, []byte("key-1-1")) + require.NoError(b, err) + require.NotNil(b, p) + // existing proof + p, err = sc.GetProof([]byte(storeKeys[1]), 500, changesets[499].Changes[1].StateChanges[1].Key) + require.NoError(b, err) + require.NotNil(b, p) + } + }) + require.NoError(b, db.Close()) + } +} diff --git a/store/v2/commitment/store_test_suite.go b/store/v2/commitment/store_test_suite.go new file mode 100644 index 000000000000..afb468de8595 --- /dev/null +++ b/store/v2/commitment/store_test_suite.go @@ -0,0 +1,469 @@ +package commitment + +import ( + "bytes" + "fmt" + "io" + "sync" + + "github.com/stretchr/testify/suite" + + corelog "cosmossdk.io/core/log" + corestore "cosmossdk.io/core/store" + coretesting "cosmossdk.io/core/testing" + "cosmossdk.io/store/v2" + dbm "cosmossdk.io/store/v2/db" + "cosmossdk.io/store/v2/proof" + "cosmossdk.io/store/v2/snapshots" + snapshotstypes "cosmossdk.io/store/v2/snapshots/types" +) + +const ( + storeKey1 = "store1" + storeKey2 = "store2" + storeKey3 = "store3" +) + +// CommitStoreTestSuite is a test suite to be used for all tree backends. +type CommitStoreTestSuite struct { + suite.Suite + + NewStore func(db corestore.KVStoreWithBatch, storeKeys, oldStoreKeys []string, logger corelog.Logger) (*CommitStore, error) +} + +func (s *CommitStoreTestSuite) TestStore_Snapshotter() { + storeKeys := []string{storeKey1, storeKey2} + commitStore, err := s.NewStore(dbm.NewMemDB(), storeKeys, nil, coretesting.NewNopLogger()) + s.Require().NoError(err) + + latestVersion := uint64(10) + kvCount := 10 + var cInfo *proof.CommitInfo + for i := uint64(1); i <= latestVersion; i++ { + kvPairs := make(map[string]corestore.KVPairs) + for _, storeKey := range storeKeys { + kvPairs[storeKey] = corestore.KVPairs{} + for j := 0; j < kvCount; j++ { + key := []byte(fmt.Sprintf("key-%d-%d", i, j)) + value := []byte(fmt.Sprintf("value-%d-%d", i, j)) + kvPairs[storeKey] = append(kvPairs[storeKey], corestore.KVPair{Key: key, Value: value}) + } + } + s.Require().NoError(commitStore.WriteChangeset(corestore.NewChangesetWithPairs(i, kvPairs))) + + cInfo, err = commitStore.Commit(i) + s.Require().NoError(err) + } + + s.Require().Equal(len(storeKeys), len(cInfo.StoreInfos)) + + // create a snapshot + dummyExtensionItem := snapshotstypes.SnapshotItem{ + Item: &snapshotstypes.SnapshotItem_Extension{ + Extension: &snapshotstypes.SnapshotExtensionMeta{ + Name: "test", + Format: 1, + }, + }, + } + + targetStore, err := s.NewStore(dbm.NewMemDB(), storeKeys, nil, coretesting.NewNopLogger()) + s.Require().NoError(err) + + chunks := make(chan io.ReadCloser, kvCount*int(latestVersion)) + go func() { + streamWriter := snapshots.NewStreamWriter(chunks) + s.Require().NotNil(streamWriter) + defer streamWriter.Close() + err := commitStore.Snapshot(latestVersion, streamWriter) + s.Require().NoError(err) + // write an extension metadata + err = streamWriter.WriteMsg(&dummyExtensionItem) + s.Require().NoError(err) + }() + + streamReader, err := snapshots.NewStreamReader(chunks) + s.Require().NoError(err) + chStorage := make(chan *corestore.StateChanges, 100) + leaves := make(map[string]string) + wg := sync.WaitGroup{} + wg.Add(1) + go func() { + for kv := range chStorage { + for _, actor := range kv.StateChanges { + leaves[fmt.Sprintf("%s_%s", kv.Actor, actor.Key)] = string(actor.Value) + } + } + wg.Done() + }() + nextItem, err := targetStore.Restore(latestVersion, snapshotstypes.CurrentFormat, streamReader, chStorage) + s.Require().NoError(err) + s.Require().Equal(*dummyExtensionItem.GetExtension(), *nextItem.GetExtension()) + + close(chStorage) + wg.Wait() + s.Require().Equal(len(storeKeys)*kvCount*int(latestVersion), len(leaves)) + for _, storeKey := range storeKeys { + for i := 1; i <= int(latestVersion); i++ { + for j := 0; j < kvCount; j++ { + key := fmt.Sprintf("%s_key-%d-%d", storeKey, i, j) + s.Require().Equal(leaves[key], fmt.Sprintf("value-%d-%d", i, j)) + } + } + } + + // check the restored tree hash + targetCommitInfo, err := targetStore.GetCommitInfo(latestVersion) + s.Require().NoError(err) + for _, storeInfo := range targetCommitInfo.StoreInfos { + matched := false + for _, latestStoreInfo := range cInfo.StoreInfos { + if bytes.Equal(storeInfo.Name, latestStoreInfo.Name) { + s.Require().Equal(latestStoreInfo.GetHash(), storeInfo.GetHash()) + matched = true + } + } + s.Require().True(matched) + } +} + +func (s *CommitStoreTestSuite) TestStore_LoadVersion() { + storeKeys := []string{storeKey1, storeKey2} + mdb := dbm.NewMemDB() + commitStore, err := s.NewStore(mdb, storeKeys, nil, coretesting.NewNopLogger()) + s.Require().NoError(err) + + latestVersion := uint64(10) + kvCount := 10 + for i := uint64(1); i <= latestVersion; i++ { + kvPairs := make(map[string]corestore.KVPairs) + for _, storeKey := range storeKeys { + kvPairs[storeKey] = corestore.KVPairs{} + for j := 0; j < kvCount; j++ { + key := []byte(fmt.Sprintf("key-%d-%d", i, j)) + value := []byte(fmt.Sprintf("value-%d-%d", i, j)) + kvPairs[storeKey] = append(kvPairs[storeKey], corestore.KVPair{Key: key, Value: value}) + } + } + s.Require().NoError(commitStore.WriteChangeset(corestore.NewChangesetWithPairs(i, kvPairs))) + _, err = commitStore.Commit(i) + s.Require().NoError(err) + } + + // load the store with the latest version + targetStore, err := s.NewStore(mdb, storeKeys, nil, coretesting.NewNopLogger()) + s.Require().NoError(err) + err = targetStore.LoadVersion(latestVersion) + s.Require().NoError(err) + // check the store + for i := uint64(1); i <= latestVersion; i++ { + commitInfo, _ := targetStore.GetCommitInfo(i) + s.Require().NotNil(commitInfo) + s.Require().Equal(i, commitInfo.Version) + } + + // rollback to a previous version + rollbackVersion := uint64(5) + rollbackStore, err := s.NewStore(mdb, storeKeys, nil, coretesting.NewNopLogger()) + s.Require().NoError(err) + err = rollbackStore.LoadVersion(rollbackVersion) + s.Require().NoError(err) + // check the store + v, err := rollbackStore.GetLatestVersion() + s.Require().NoError(err) + s.Require().Equal(rollbackVersion, v) + for i := uint64(1); i <= latestVersion; i++ { + commitInfo, _ := rollbackStore.GetCommitInfo(i) + if i > rollbackVersion { + s.Require().Nil(commitInfo) + } else { + s.Require().NotNil(commitInfo) + } + } +} + +func (s *CommitStoreTestSuite) TestStore_Pruning() { + storeKeys := []string{storeKey1, storeKey2} + pruneOpts := store.NewPruningOptionWithCustom(10, 5) + commitStore, err := s.NewStore(dbm.NewMemDB(), storeKeys, nil, coretesting.NewNopLogger()) + s.Require().NoError(err) + + latestVersion := uint64(100) + kvCount := 10 + for i := uint64(1); i <= latestVersion; i++ { + kvPairs := make(map[string]corestore.KVPairs) + for _, storeKey := range storeKeys { + kvPairs[storeKey] = corestore.KVPairs{} + for j := 0; j < kvCount; j++ { + key := []byte(fmt.Sprintf("key-%d-%d", i, j)) + value := []byte(fmt.Sprintf("value-%d-%d", i, j)) + kvPairs[storeKey] = append(kvPairs[storeKey], corestore.KVPair{Key: key, Value: value}) + } + } + s.Require().NoError(commitStore.WriteChangeset(corestore.NewChangesetWithPairs(i, kvPairs))) + + _, err = commitStore.Commit(i) + s.Require().NoError(err) + + if prune, pruneVersion := pruneOpts.ShouldPrune(i); prune { + s.Require().NoError(commitStore.Prune(pruneVersion)) + } + + } + + pruneVersion := latestVersion - pruneOpts.KeepRecent - 1 + // check the store + for i := uint64(1); i <= latestVersion; i++ { + commitInfo, _ := commitStore.GetCommitInfo(i) + if i <= pruneVersion { + s.Require().Nil(commitInfo) + } else { + s.Require().NotNil(commitInfo) + } + } +} + +func (s *CommitStoreTestSuite) TestStore_GetProof() { + storeKeys := []string{storeKey1, storeKey2} + commitStore, err := s.NewStore(dbm.NewMemDB(), storeKeys, nil, coretesting.NewNopLogger()) + s.Require().NoError(err) + + toVersion := uint64(10) + keyCount := 5 + + // commit some changes + for version := uint64(1); version <= toVersion; version++ { + cs := corestore.NewChangeset(version) + for _, storeKey := range storeKeys { + for i := 0; i < keyCount; i++ { + cs.Add([]byte(storeKey), []byte(fmt.Sprintf("key-%d-%d", version, i)), []byte(fmt.Sprintf("value-%d-%d", version, i)), false) + } + } + err := commitStore.WriteChangeset(cs) + s.Require().NoError(err) + _, err = commitStore.Commit(version) + s.Require().NoError(err) + } + + // get proof + for version := uint64(1); version <= toVersion; version++ { + for _, storeKey := range storeKeys { + for i := 0; i < keyCount; i++ { + _, err := commitStore.GetProof([]byte(storeKey), version, []byte(fmt.Sprintf("key-%d-%d", version, i))) + s.Require().NoError(err) + } + } + } + + // prune version 1 + s.Require().NoError(commitStore.Prune(1)) + + // check if proof for version 1 is pruned + _, err = commitStore.GetProof([]byte(storeKeys[0]), 1, []byte(fmt.Sprintf("key-%d-%d", 1, 0))) + s.Require().Error(err) + // check the commit info + commit, _ := commitStore.GetCommitInfo(1) + s.Require().Nil(commit) +} + +func (s *CommitStoreTestSuite) TestStore_Get() { + storeKeys := []string{storeKey1, storeKey2} + commitStore, err := s.NewStore(dbm.NewMemDB(), storeKeys, nil, coretesting.NewNopLogger()) + s.Require().NoError(err) + + toVersion := uint64(10) + keyCount := 5 + + // commit some changes + for version := uint64(1); version <= toVersion; version++ { + cs := corestore.NewChangeset(version) + for _, storeKey := range storeKeys { + for i := 0; i < keyCount; i++ { + cs.Add([]byte(storeKey), []byte(fmt.Sprintf("key-%d-%d", version, i)), []byte(fmt.Sprintf("value-%d-%d", version, i)), false) + } + } + err := commitStore.WriteChangeset(cs) + s.Require().NoError(err) + _, err = commitStore.Commit(version) + s.Require().NoError(err) + } + + // get proof + for version := uint64(1); version <= toVersion; version++ { + for _, storeKey := range storeKeys { + for i := 0; i < keyCount; i++ { + val, err := commitStore.Get([]byte(storeKey), version, []byte(fmt.Sprintf("key-%d-%d", version, i))) + s.Require().NoError(err) + s.Require().Equal([]byte(fmt.Sprintf("value-%d-%d", version, i)), val) + } + } + } +} + +func (s *CommitStoreTestSuite) TestStore_Upgrades() { + storeKeys := []string{storeKey1, storeKey2, storeKey3} + commitDB := dbm.NewMemDB() + commitStore, err := s.NewStore(commitDB, storeKeys, nil, coretesting.NewNopLogger()) + s.Require().NoError(err) + + latestVersion := uint64(10) + kvCount := 10 + for i := uint64(1); i <= latestVersion; i++ { + kvPairs := make(map[string]corestore.KVPairs) + for _, storeKey := range storeKeys { + kvPairs[storeKey] = corestore.KVPairs{} + for j := 0; j < kvCount; j++ { + key := []byte(fmt.Sprintf("key-%d-%d", i, j)) + value := []byte(fmt.Sprintf("value-%d-%d", i, j)) + kvPairs[storeKey] = append(kvPairs[storeKey], corestore.KVPair{Key: key, Value: value}) + } + } + s.Require().NoError(commitStore.WriteChangeset(corestore.NewChangesetWithPairs(i, kvPairs))) + _, err = commitStore.Commit(i) + s.Require().NoError(err) + } + + // create a new commitment store with upgrades + upgrades := &corestore.StoreUpgrades{ + Added: []string{"newStore1", "newStore2"}, + Deleted: []string{storeKey3}, + } + newStoreKeys := []string{storeKey1, storeKey2, storeKey3, "newStore1", "newStore2"} + realStoreKeys := []string{storeKey1, storeKey2, "newStore1", "newStore2"} + oldStoreKeys := []string{storeKey1, storeKey3} + commitStore, err = s.NewStore(commitDB, newStoreKeys, oldStoreKeys, coretesting.NewNopLogger()) + s.Require().NoError(err) + err = commitStore.LoadVersionAndUpgrade(latestVersion, upgrades) + s.Require().NoError(err) + + // GetProof should work for the old stores + for _, storeKey := range []string{storeKey1, storeKey3} { + for i := uint64(1); i <= latestVersion; i++ { + for j := 0; j < kvCount; j++ { + proof, err := commitStore.GetProof([]byte(storeKey), i, []byte(fmt.Sprintf("key-%d-%d", i, j))) + s.Require().NoError(err) + s.Require().NotNil(proof) + } + } + } + // GetProof should fail for the new stores against the old versions + for _, storeKey := range []string{"newStore1", "newStore2"} { + for i := uint64(1); i <= latestVersion; i++ { + for j := 0; j < kvCount; j++ { + _, err := commitStore.GetProof([]byte(storeKey), i, []byte(fmt.Sprintf("key-%d-%d", i, j))) + s.Require().Error(err) + } + } + } + + // apply the changeset again + for i := latestVersion + 1; i < latestVersion*2; i++ { + kvPairs := make(map[string]corestore.KVPairs) + for _, storeKey := range realStoreKeys { + kvPairs[storeKey] = corestore.KVPairs{} + for j := 0; j < kvCount; j++ { + key := []byte(fmt.Sprintf("key-%d-%d", i, j)) + value := []byte(fmt.Sprintf("value-%d-%d", i, j)) + kvPairs[storeKey] = append(kvPairs[storeKey], corestore.KVPair{Key: key, Value: value}) + } + } + s.Require().NoError(commitStore.WriteChangeset(corestore.NewChangesetWithPairs(i, kvPairs))) + commitInfo, err := commitStore.Commit(i) + s.Require().NoError(err) + s.Require().NotNil(commitInfo) + s.Require().Equal(len(realStoreKeys), len(commitInfo.StoreInfos)) + for _, storeKey := range realStoreKeys { + s.Require().NotNil(commitInfo.GetStoreCommitID([]byte(storeKey))) + } + } + + // verify new stores + for _, storeKey := range []string{"newStore1", "newStore2"} { + for i := latestVersion + 1; i < latestVersion*2; i++ { + for j := 0; j < kvCount; j++ { + proof, err := commitStore.GetProof([]byte(storeKey), i, []byte(fmt.Sprintf("key-%d-%d", i, j))) + s.Require().NoError(err) + s.Require().NotNil(proof) + } + } + } + + // verify existing store + for i := uint64(1); i < latestVersion*2; i++ { + for j := 0; j < kvCount; j++ { + proof, err := commitStore.GetProof([]byte(storeKey2), i, []byte(fmt.Sprintf("key-%d-%d", i, j))) + s.Require().NoError(err) + s.Require().NotNil(proof) + } + } + + // create a new commitment store with one more upgrades + upgrades = &corestore.StoreUpgrades{ + Added: []string{storeKey3}, + Deleted: []string{storeKey2}, + } + newRealStoreKeys := []string{storeKey1, storeKey3, "newStore1", "newStore2"} + oldStoreKeys = []string{storeKey2, storeKey3} + commitStore, err = s.NewStore(commitDB, newStoreKeys, oldStoreKeys, coretesting.NewNopLogger()) + s.Require().NoError(err) + err = commitStore.LoadVersionAndUpgrade(2*latestVersion-1, upgrades) + s.Require().NoError(err) + + // apply the changeset again + for i := latestVersion * 2; i < latestVersion*3; i++ { + kvPairs := make(map[string]corestore.KVPairs) + for _, storeKey := range newRealStoreKeys { + kvPairs[storeKey] = corestore.KVPairs{} + for j := 0; j < kvCount; j++ { + key := []byte(fmt.Sprintf("key-%d-%d", i, j)) + value := []byte(fmt.Sprintf("value-%d-%d", i, j)) + kvPairs[storeKey] = append(kvPairs[storeKey], corestore.KVPair{Key: key, Value: value}) + } + } + s.Require().NoError(commitStore.WriteChangeset(corestore.NewChangesetWithPairs(i, kvPairs))) + commitInfo, err := commitStore.Commit(i) + s.Require().NoError(err) + s.Require().NotNil(commitInfo) + s.Require().Equal(len(newRealStoreKeys), len(commitInfo.StoreInfos)) + for _, storeKey := range newRealStoreKeys { + s.Require().NotNil(commitInfo.GetStoreCommitID([]byte(storeKey))) + } + } + + // prune the old stores + s.Require().NoError(commitStore.Prune(latestVersion)) + // GetProof should fail for the old stores + for _, storeKey := range []string{storeKey1, storeKey3} { + for i := uint64(1); i <= latestVersion; i++ { + for j := 0; j < kvCount; j++ { + _, err := commitStore.GetProof([]byte(storeKey), i, []byte(fmt.Sprintf("key-%d-%d", i, j))) + s.Require().Error(err) + } + } + } + // GetProof should not fail for the newly removed store + for i := latestVersion + 1; i < latestVersion*2; i++ { + for j := 0; j < kvCount; j++ { + proof, err := commitStore.GetProof([]byte(storeKey2), i, []byte(fmt.Sprintf("key-%d-%d", i, j))) + s.Require().NoError(err) + s.Require().NotNil(proof) + } + } + + s.Require().NoError(commitStore.Prune(latestVersion * 2)) + // GetProof should fail for the newly deleted stores + for i := uint64(1); i < latestVersion*2; i++ { + for j := 0; j < kvCount; j++ { + _, err := commitStore.GetProof([]byte(storeKey2), i, []byte(fmt.Sprintf("key-%d-%d", i, j))) + s.Require().Error(err) + } + } + // GetProof should work for the new added store + for i := latestVersion*2 + 1; i < latestVersion*3; i++ { + for j := 0; j < kvCount; j++ { + proof, err := commitStore.GetProof([]byte(storeKey3), i, []byte(fmt.Sprintf("key-%d-%d", i, j))) + s.Require().NoError(err) + s.Require().NotNil(proof) + } + } +} diff --git a/store/v2/commitment/tree.go b/store/v2/commitment/tree.go new file mode 100644 index 000000000000..f57eabd20724 --- /dev/null +++ b/store/v2/commitment/tree.go @@ -0,0 +1,59 @@ +package commitment + +import ( + "errors" + "io" + + ics23 "github.com/cosmos/ics23/go" + + corestore "cosmossdk.io/core/store" + snapshotstypes "cosmossdk.io/store/v2/snapshots/types" +) + +// ErrorExportDone is returned by Exporter.Next() when all items have been exported. +var ErrorExportDone = errors.New("export is complete") + +// Tree is the interface that wraps the basic Tree methods. +type Tree interface { + Set(key, value []byte) error + Remove(key []byte) error + GetLatestVersion() (uint64, error) + + // Hash returns the hash of the current version of the tree + Hash() []byte + // Version returns the current version of the tree + Version() uint64 + + LoadVersion(version uint64) error + Commit() ([]byte, uint64, error) + SetInitialVersion(version uint64) error + GetProof(version uint64, key []byte) (*ics23.CommitmentProof, error) + + Prune(version uint64) error + Export(version uint64) (Exporter, error) + Import(version uint64) (Importer, error) + + io.Closer +} + +// Reader is the optional interface that is only used to read data from the tree +// during the migration process. +type Reader interface { + Get(version uint64, key []byte) ([]byte, error) + Iterator(version uint64, start, end []byte, ascending bool) (corestore.Iterator, error) +} + +// Exporter is the interface that wraps the basic Export methods. +type Exporter interface { + Next() (*snapshotstypes.SnapshotIAVLItem, error) + + io.Closer +} + +// Importer is the interface that wraps the basic Import methods. +type Importer interface { + Add(*snapshotstypes.SnapshotIAVLItem) error + Commit() error + + io.Closer +} diff --git a/store/v2/database.go b/store/v2/database.go new file mode 100644 index 000000000000..27d0973ec18e --- /dev/null +++ b/store/v2/database.go @@ -0,0 +1,70 @@ +package store + +import ( + "io" + + corestore "cosmossdk.io/core/store" + "cosmossdk.io/store/v2/proof" +) + +// VersionedWriter defines an API for a versioned database that allows reads, +// writes, iteration and commitment over a series of versions. +type VersionedWriter interface { + VersionedReader + + SetLatestVersion(version uint64) error + ApplyChangeset(cs *corestore.Changeset) error + + // Closer releases associated resources. It should NOT be idempotent. It must + // only be called once and any call after may panic. + io.Closer +} + +type VersionedReader interface { + Has(storeKey []byte, version uint64, key []byte) (bool, error) + Get(storeKey []byte, version uint64, key []byte) ([]byte, error) + + GetLatestVersion() (uint64, error) + VersionExists(v uint64) (bool, error) + + Iterator(storeKey []byte, version uint64, start, end []byte) (corestore.Iterator, error) + ReverseIterator(storeKey []byte, version uint64, start, end []byte) (corestore.Iterator, error) +} + +// UpgradableDatabase defines an API for a versioned database that allows pruning +// deleted storeKeys +type UpgradableDatabase interface { + // PruneStoreKeys prunes all data associated with the given storeKeys whenever + // the given version is pruned. + PruneStoreKeys(storeKeys []string, version uint64) error +} + +// Committer defines an API for committing state. +type Committer interface { + // WriteChangeset writes the changeset to the commitment state. + WriteChangeset(cs *corestore.Changeset) error + + // GetLatestVersion returns the latest version. + GetLatestVersion() (uint64, error) + + // LoadVersion loads the tree at the given version. + LoadVersion(targetVersion uint64) error + + // Commit commits the working tree to the database. + Commit(version uint64) (*proof.CommitInfo, error) + + // GetProof returns the proof of existence or non-existence for the given key. + GetProof(storeKey []byte, version uint64, key []byte) ([]proof.CommitmentOp, error) + + // SetInitialVersion sets the initial version of the committer. + SetInitialVersion(version uint64) error + + // GetCommitInfo returns the CommitInfo for the given version. + GetCommitInfo(version uint64) (*proof.CommitInfo, error) + + Get(storeKey []byte, version uint64, key []byte) ([]byte, error) + + // Closer releases associated resources. It should NOT be idempotent. It must + // only be called once and any call after may panic. + io.Closer +} diff --git a/store/v2/go.mod b/store/v2/go.mod new file mode 100644 index 000000000000..0a93547212b9 --- /dev/null +++ b/store/v2/go.mod @@ -0,0 +1,68 @@ +module cosmossdk.io/store/v2 + +go 1.23 + +require ( + cosmossdk.io/core v1.0.0-alpha.5 + cosmossdk.io/core/testing v0.0.0-20240923163230-04da382a9f29 + cosmossdk.io/errors/v2 v2.0.0-20240731132947-df72853b3ca5 + cosmossdk.io/log v1.4.1 + github.com/cockroachdb/pebble v1.1.0 + github.com/cosmos/cosmos-proto v1.0.0-beta.5 + github.com/cosmos/gogoproto v1.7.0 + github.com/cosmos/iavl v1.3.1 + github.com/cosmos/ics23/go v0.11.0 + github.com/google/btree v1.1.2 + github.com/hashicorp/go-metrics v0.5.3 + github.com/linxGnu/grocksdb v1.9.3 + github.com/mattn/go-sqlite3 v1.14.22 + github.com/spf13/cast v1.7.0 + github.com/stretchr/testify v1.9.0 + github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d + go.uber.org/mock v0.5.0 + golang.org/x/sync v0.9.0 +) + +require ( + cosmossdk.io/schema v0.3.0 // indirect + github.com/DataDog/zstd v1.5.5 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/cockroachdb/errors v1.11.1 // indirect + github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect + github.com/cockroachdb/redact v1.1.5 // indirect + github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/emicklei/dot v1.6.2 // indirect + github.com/fsnotify/fsnotify v1.8.0 // indirect + github.com/getsentry/sentry-go v0.27.0 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/snappy v0.0.4 // indirect + github.com/google/go-cmp v0.6.0 // indirect + github.com/hashicorp/go-immutable-radix v1.3.1 // indirect + github.com/hashicorp/go-uuid v1.0.1 // indirect + github.com/hashicorp/golang-lru v1.0.2 // indirect + github.com/klauspost/compress v1.17.9 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/prometheus/client_golang v1.20.5 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.60.1 // indirect + github.com/prometheus/procfs v0.15.1 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect + github.com/rs/zerolog v1.33.0 // indirect + github.com/tidwall/btree v1.7.0 // indirect + golang.org/x/crypto v0.28.0 // indirect + golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect + golang.org/x/sys v0.26.0 // indirect + golang.org/x/text v0.19.0 // indirect + google.golang.org/protobuf v1.35.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) + +replace cosmossdk.io/core => ../../core diff --git a/store/v2/go.sum b/store/v2/go.sum new file mode 100644 index 000000000000..aa99a8ec9345 --- /dev/null +++ b/store/v2/go.sum @@ -0,0 +1,328 @@ +cosmossdk.io/core/testing v0.0.0-20240923163230-04da382a9f29 h1:NxxUo0GMJUbIuVg0R70e3cbn9eFTEuMr7ev1AFvypdY= +cosmossdk.io/core/testing v0.0.0-20240923163230-04da382a9f29/go.mod h1:8s2tPeJtSiQuoyPmr2Ag7meikonISO4Fv4MoO8+ORrs= +cosmossdk.io/errors/v2 v2.0.0-20240731132947-df72853b3ca5 h1:IQNdY2kB+k+1OM2DvqFG1+UgeU1JzZrWtwuWzI3ZfwA= +cosmossdk.io/errors/v2 v2.0.0-20240731132947-df72853b3ca5/go.mod h1:0CuYKkFHxc1vw2JC+t21THBCALJVROrWVR/3PQ1urpc= +cosmossdk.io/log v1.4.1 h1:wKdjfDRbDyZRuWa8M+9nuvpVYxrEOwbD/CA8hvhU8QM= +cosmossdk.io/log v1.4.1/go.mod h1:k08v0Pyq+gCP6phvdI6RCGhLf/r425UT6Rk/m+o74rU= +cosmossdk.io/schema v0.3.0 h1:01lcaM4trhzZ1HQTfTV8z6Ma1GziOZ/YmdzBN3F720c= +cosmossdk.io/schema v0.3.0/go.mod h1:RDAhxIeNB4bYqAlF4NBJwRrgtnciMcyyg0DOKnhNZQQ= +github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ= +github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= +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/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +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/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= +github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= +github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f h1:otljaYPt5hWxV3MUfO5dFPFiOXg9CyG5/kCfayTqsJ4= +github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= +github.com/cockroachdb/errors v1.11.1 h1:xSEW75zKaKCWzR3OfxXUxgrk/NtT4G1MiOv5lWZazG8= +github.com/cockroachdb/errors v1.11.1/go.mod h1:8MUxA3Gi6b25tYlFEBGLf+D8aISL+M4MIpiWMSNRfxw= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= +github.com/cockroachdb/pebble v1.1.0 h1:pcFh8CdCIt2kmEpK0OIatq67Ln9uGDYY3d5XnE0LJG4= +github.com/cockroachdb/pebble v1.1.0/go.mod h1:sEHm5NOXxyiAoKWhoFxT8xMgd/f3RA6qUqQ1BXKrh2E= +github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= +github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cosmos/cosmos-proto v1.0.0-beta.5 h1:eNcayDLpip+zVLRLYafhzLvQlSmyab+RC5W7ZfmxJLA= +github.com/cosmos/cosmos-proto v1.0.0-beta.5/go.mod h1:hQGLpiIUloJBMdQMMWb/4wRApmI9hjHH05nefC0Ojec= +github.com/cosmos/gogoproto v1.7.0 h1:79USr0oyXAbxg3rspGh/m4SWNyoz/GLaAh0QlCe2fro= +github.com/cosmos/gogoproto v1.7.0/go.mod h1:yWChEv5IUEYURQasfyBW5ffkMHR/90hiHgbNgrtp4j0= +github.com/cosmos/iavl v1.3.1 h1:+W1G2uSUtJMqMGpwz/fKiwZxY2DDT/9/0hyNLm6Geu0= +github.com/cosmos/iavl v1.3.1/go.mod h1:T6SfBcyhulVIY2G/ZtAtQm/QiJvsuhIos52V4dWYk88= +github.com/cosmos/ics23/go v0.11.0 h1:jk5skjT0TqX5e5QJbEnwXIS2yI2vnmLOgpQPeM5RtnU= +github.com/cosmos/ics23/go v0.11.0/go.mod h1:A8OjxPE67hHST4Icw94hOxxFEJMBG031xIGF/JHNIY0= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emicklei/dot v1.6.2 h1:08GN+DD79cy/tzN6uLCT84+2Wk9u+wvqP+Hkx/dIR8A= +github.com/emicklei/dot v1.6.2/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= +github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +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/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-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-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-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +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.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= +github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= +github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-metrics v0.5.3 h1:M5uADWMOGCTUNU1YuC4hfknOeHNaX54LDm4oYSucoNE= +github.com/hashicorp/go-metrics v0.5.3/go.mod h1:KEjodfebIOuBYSAe/bHTm+HChmKSxAOXPBieMLYozDE= +github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +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/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= +github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +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.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/linxGnu/grocksdb v1.9.3 h1:s1cbPcOd0cU2SKXRG1nEqCOWYAELQjdqg3RVI2MH9ik= +github.com/linxGnu/grocksdb v1.9.3/go.mod h1:QYiYypR2d4v63Wj1adOOfzglnoII0gLj3PNh4fZkcFA= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.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-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= +github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +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/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +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/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= +github.com/onsi/gomega v1.26.0 h1:03cDLK28U6hWvCAns6NeydX3zIm4SF3ci69ulidS32Q= +github.com/onsi/gomega v1.26.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM= +github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= +github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= +github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pkg/errors v0.8.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/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= +github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +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.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= +github.com/prometheus/common v0.60.1 h1:FUas6GcOw66yB/73KC+BOZoFJmbo/1pojoILArPAaSc= +github.com/prometheus/common v0.60.1/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= +github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= +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/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= +github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +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/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs= +github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= +github.com/tidwall/btree v1.7.0 h1:L1fkJH/AuEh5zBnnBbmTwQ5Lt+bRJ5A8EWecslvo9iI= +github.com/tidwall/btree v1.7.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY= +github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= +go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +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-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= +golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= +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/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +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-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= +golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= +golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +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/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +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.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= +google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +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-20190902080502-41f04d3bba15/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/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.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.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.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= +gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= diff --git a/store/v2/internal/encoding/changeset_test.go b/store/v2/internal/encoding/changeset_test.go new file mode 100644 index 000000000000..76bd163bd0a8 --- /dev/null +++ b/store/v2/internal/encoding/changeset_test.go @@ -0,0 +1,95 @@ +package encoding + +import ( + "testing" + + "github.com/stretchr/testify/require" + + corestore "cosmossdk.io/core/store" +) + +func TestChangesetMarshal(t *testing.T) { + testcases := []struct { + name string + changeset *corestore.Changeset + encodedSize int + encodedBytes []byte + }{ + { + name: "empty", + changeset: corestore.NewChangeset(1), + encodedSize: 1, + encodedBytes: []byte{0x0}, + }, + { + name: "one store", + changeset: &corestore.Changeset{Changes: []corestore.StateChanges{ + { + Actor: []byte("storekey"), + StateChanges: corestore.KVPairs{ + {Key: []byte("key"), Value: []byte("value"), Remove: false}, + }, + }, + }}, + encodedSize: 1 + 1 + 8 + 1 + 1 + 3 + 1 + 1 + 5, + encodedBytes: []byte{0x1, 0x8, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x6b, 0x65, 0x79, 0x1, 0x3, 0x6b, 0x65, 0x79, 0x0, 0x5, 0x76, 0x61, 0x6c, 0x75, 0x65}, + }, + { + name: "one remove store", + changeset: &corestore.Changeset{Changes: []corestore.StateChanges{ + { + Actor: []byte("storekey"), + StateChanges: corestore.KVPairs{ + {Key: []byte("key"), Remove: true}, + }, + }, + }}, + encodedSize: 1 + 1 + 8 + 1 + 1 + 3 + 1, + encodedBytes: []byte{0x1, 0x8, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x6b, 0x65, 0x79, 0x1, 0x3, 0x6b, 0x65, 0x79, 0x1}, + }, + { + name: "two stores", + changeset: &corestore.Changeset{Changes: []corestore.StateChanges{ + { + Actor: []byte("storekey1"), + StateChanges: corestore.KVPairs{ + {Key: []byte("key1"), Value: []byte("value1"), Remove: false}, + }, + }, + { + Actor: []byte("storekey2"), + StateChanges: corestore.KVPairs{ + {Key: []byte("key2"), Value: []byte("value2"), Remove: false}, + {Key: []byte("key1"), Remove: true}, + }, + }, + }}, + encodedSize: 2 + 1 + 9 + 1 + 1 + 4 + 1 + 6 + 1 + 9 + 1 + 1 + 4 + 1 + 1 + 6 + 1 + 4 + 1, + // encodedBytes: it is not deterministic, + }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + // check the encoded size + require.Equal(t, encodedSize(tc.changeset), tc.encodedSize, "encoded size mismatch") + // check the encoded bytes + encodedBytes, err := MarshalChangeset(tc.changeset) + require.NoError(t, err, "marshal error") + if len(tc.encodedBytes) != 0 { + require.Equal(t, encodedBytes, tc.encodedBytes, "encoded bytes mismatch") + } + // check the unmarshaled changeset + cs := corestore.NewChangeset(1) + require.NoError(t, UnmarshalChangeset(cs, encodedBytes), "unmarshal error") + require.Equal(t, len(tc.changeset.Changes), len(cs.Changes), "unmarshaled changeset store size mismatch") + for i, changes := range tc.changeset.Changes { + require.Equal(t, changes.Actor, cs.Changes[i].Actor, "unmarshaled changeset store key mismatch") + require.Equal(t, len(changes.StateChanges), len(cs.Changes[i].StateChanges), "unmarshaled changeset StateChanges size mismatch") + for j, pair := range changes.StateChanges { + require.Equal(t, pair, cs.Changes[i].StateChanges[j], "unmarshaled changeset pair mismatch") + } + } + }) + } +} diff --git a/store/v2/migration/manager.go b/store/v2/migration/manager.go new file mode 100644 index 000000000000..d5118a6313e8 --- /dev/null +++ b/store/v2/migration/manager.go @@ -0,0 +1,278 @@ +package migration + +import ( + "encoding/binary" + "errors" + "fmt" + "io" + "sync" + "time" + + "golang.org/x/sync/errgroup" + + "cosmossdk.io/core/log" + corestore "cosmossdk.io/core/store" + "cosmossdk.io/store/v2/commitment" + "cosmossdk.io/store/v2/internal/encoding" + "cosmossdk.io/store/v2/snapshots" + snapshotstypes "cosmossdk.io/store/v2/snapshots/types" + "cosmossdk.io/store/v2/storage" +) + +const ( + // defaultChannelBufferSize is the default buffer size for the migration stream. + defaultChannelBufferSize = 1024 + // defaultStorageBufferSize is the default buffer size for the storage snapshotter. + defaultStorageBufferSize = 1024 + + migrateChangesetKeyFmt = "m/cs_%x" // m/cs_ +) + +// VersionedChangeset is a pair of version and Changeset. +type VersionedChangeset struct { + Version uint64 + Changeset *corestore.Changeset +} + +// Manager manages the migration of the whole state from store/v1 to store/v2. +type Manager struct { + logger log.Logger + snapshotsManager *snapshots.Manager + + stateStorage *storage.StorageStore + stateCommitment *commitment.CommitStore + + db corestore.KVStoreWithBatch + mtx sync.Mutex // mutex for migratedVersion + migratedVersion uint64 + + chChangeset <-chan *VersionedChangeset + chDone <-chan struct{} +} + +// NewManager returns a new Manager. +// +// NOTE: `sc` can be `nil` if don't want to migrate the commitment. +func NewManager(db corestore.KVStoreWithBatch, sm *snapshots.Manager, ss *storage.StorageStore, sc *commitment.CommitStore, logger log.Logger) *Manager { + return &Manager{ + logger: logger, + snapshotsManager: sm, + stateStorage: ss, + stateCommitment: sc, + db: db, + } +} + +// Start starts the whole migration process. +// It migrates the whole state at the given version to the new store/v2 (both SC and SS). +// It also catches up the Changesets which are committed while the migration is in progress. +// `chChangeset` is the channel to receive the committed Changesets from the RootStore. +// `chDone` is the channel to receive the done signal from the RootStore. +// NOTE: It should be called by the RootStore, running in the background. +func (m *Manager) Start(version uint64, chChangeset <-chan *VersionedChangeset, chDone <-chan struct{}) error { + m.chChangeset = chChangeset + m.chDone = chDone + + go func() { + if err := m.writeChangeset(); err != nil { + m.logger.Error("failed to write changeset", "err", err) + } + }() + + if err := m.Migrate(version); err != nil { + return fmt.Errorf("failed to migrate state: %w", err) + } + + return m.Sync() +} + +// GetStateCommitment returns the state commitment. +func (m *Manager) GetStateCommitment() *commitment.CommitStore { + return m.stateCommitment +} + +// Migrate migrates the whole state at the given height to the new store/v2. +func (m *Manager) Migrate(height uint64) error { + // create the migration stream and snapshot, + // which acts as protoio.Reader and snapshots.WriteCloser. + ms := NewMigrationStream(defaultChannelBufferSize) + + if err := m.snapshotsManager.CreateMigration(height, ms); err != nil { + return err + } + + // restore the snapshot + chStorage := make(chan *corestore.StateChanges, defaultStorageBufferSize) + + eg := new(errgroup.Group) + eg.Go(func() error { + return m.stateStorage.Restore(height, chStorage) + }) + eg.Go(func() error { + defer close(chStorage) + if m.stateCommitment != nil { + if _, err := m.stateCommitment.Restore(height, 0, ms, chStorage); err != nil { + return err + } + } else { // there is no commitment migration, just consume the stream to restore the state storage + var storeKey []byte + loop: + for { + snapshotItem := snapshotstypes.SnapshotItem{} + err := ms.ReadMsg(&snapshotItem) + if errors.Is(err, io.EOF) { + break + } + if err != nil { + return fmt.Errorf("failed to read snapshot item: %w", err) + } + switch item := snapshotItem.Item.(type) { + case *snapshotstypes.SnapshotItem_Store: + storeKey = []byte(item.Store.Name) + case *snapshotstypes.SnapshotItem_IAVL: + if item.IAVL.Height == 0 { // only restore the leaf nodes + key := item.IAVL.Key + if key == nil { + key = []byte{} + } + value := item.IAVL.Value + if value == nil { + value = []byte{} + } + chStorage <- &corestore.StateChanges{ + Actor: storeKey, + StateChanges: []corestore.KVPair{ + { + Key: key, + Value: value, + }, + }, + } + } + default: + break loop + } + } + } + return nil + }) + + if err := eg.Wait(); err != nil { + return err + } + + m.mtx.Lock() + m.migratedVersion = height + m.mtx.Unlock() + + return nil +} + +// writeChangeset writes the Changeset to the db. +func (m *Manager) writeChangeset() error { + for vc := range m.chChangeset { + cs := vc.Changeset + buf := make([]byte, 8) + binary.BigEndian.PutUint64(buf, vc.Version) + csKey := []byte(fmt.Sprintf(migrateChangesetKeyFmt, buf)) + csBytes, err := encoding.MarshalChangeset(cs) + if err != nil { + return fmt.Errorf("failed to marshal changeset: %w", err) + } + + batch := m.db.NewBatch() + // Invoking this code in a closure so that defer is called immediately on return + // yet not in the for-loop which can leave resource lingering. + err = func() (err error) { + defer func() { + err = errors.Join(err, batch.Close()) + }() + + if err := batch.Set(csKey, csBytes); err != nil { + return fmt.Errorf("failed to write changeset to db.Batch: %w", err) + } + if err := batch.Write(); err != nil { + return fmt.Errorf("failed to write changeset to db: %w", err) + } + return nil + }() + if err != nil { + return err + } + } + + return nil +} + +// GetMigratedVersion returns the migrated version. +// It is used to check the migrated version in the RootStore. +func (m *Manager) GetMigratedVersion() uint64 { + m.mtx.Lock() + defer m.mtx.Unlock() + return m.migratedVersion +} + +// Sync catches up the Changesets which are committed while the migration is in progress. +// It should be called after the migration is done. +func (m *Manager) Sync() error { + version := m.GetMigratedVersion() + if version == 0 { + return errors.New("migration is not done yet") + } + version += 1 + + for { + select { + case <-m.chDone: + return nil + default: + buf := make([]byte, 8) + binary.BigEndian.PutUint64(buf, version) + csKey := []byte(fmt.Sprintf(migrateChangesetKeyFmt, buf)) + csBytes, err := m.db.Get(csKey) + if err != nil { + return fmt.Errorf("failed to get changeset from db: %w", err) + } + if csBytes == nil { + // wait for the next changeset + time.Sleep(100 * time.Millisecond) + continue + } + + cs := corestore.NewChangeset(version) + if err := encoding.UnmarshalChangeset(cs, csBytes); err != nil { + return fmt.Errorf("failed to unmarshal changeset: %w", err) + } + if m.stateCommitment != nil { + if err := m.stateCommitment.WriteChangeset(cs); err != nil { + return fmt.Errorf("failed to write changeset to commitment: %w", err) + } + if _, err := m.stateCommitment.Commit(version); err != nil { + return fmt.Errorf("failed to commit changeset to commitment: %w", err) + } + } + if err := m.stateStorage.ApplyChangeset(cs); err != nil { + return fmt.Errorf("failed to write changeset to storage: %w", err) + } + + m.mtx.Lock() + m.migratedVersion = version + m.mtx.Unlock() + + version += 1 + } + } +} + +// Close closes the manager. It should be called after the migration is done. +// It will close the db and notify the snapshotsManager that the migration is done. +func (m *Manager) Close() error { + if err := m.db.Close(); err != nil { + return fmt.Errorf("failed to close db: %w", err) + } + if m.stateCommitment != nil { + m.snapshotsManager.EndMigration(m.stateCommitment) + } + + return nil +} diff --git a/store/v2/migration/manager_test.go b/store/v2/migration/manager_test.go new file mode 100644 index 000000000000..07a5b15b8350 --- /dev/null +++ b/store/v2/migration/manager_test.go @@ -0,0 +1,220 @@ +package migration + +import ( + "encoding/binary" + "fmt" + "testing" + + "github.com/stretchr/testify/require" + + corestore "cosmossdk.io/core/store" + coretesting "cosmossdk.io/core/testing" + "cosmossdk.io/store/v2/commitment" + "cosmossdk.io/store/v2/commitment/iavl" + dbm "cosmossdk.io/store/v2/db" + "cosmossdk.io/store/v2/snapshots" + "cosmossdk.io/store/v2/storage" + "cosmossdk.io/store/v2/storage/pebbledb" +) + +var storeKeys = []string{"store1", "store2"} + +func setupMigrationManager(t *testing.T, noCommitStore bool) (*Manager, *commitment.CommitStore) { + t.Helper() + + db := dbm.NewMemDB() + multiTrees := make(map[string]commitment.Tree) + for _, storeKey := range storeKeys { + prefixDB := dbm.NewPrefixDB(db, []byte(storeKey)) + multiTrees[storeKey] = iavl.NewIavlTree(prefixDB, coretesting.NewNopLogger(), iavl.DefaultConfig()) + } + + commitStore, err := commitment.NewCommitStore(multiTrees, nil, db, coretesting.NewNopLogger()) + require.NoError(t, err) + + snapshotsStore, err := snapshots.NewStore(t.TempDir()) + require.NoError(t, err) + + snapshotsManager := snapshots.NewManager(snapshotsStore, snapshots.NewSnapshotOptions(1500, 2), commitStore, nil, nil, coretesting.NewNopLogger()) + + storageDB, err := pebbledb.New(t.TempDir()) + require.NoError(t, err) + newStorageStore := storage.NewStorageStore(storageDB, coretesting.NewNopLogger()) // for store/v2 + + db1 := dbm.NewMemDB() + multiTrees1 := make(map[string]commitment.Tree) + for _, storeKey := range storeKeys { + prefixDB := dbm.NewPrefixDB(db1, []byte(storeKey)) + multiTrees1[storeKey] = iavl.NewIavlTree(prefixDB, coretesting.NewNopLogger(), iavl.DefaultConfig()) + } + + newCommitStore, err := commitment.NewCommitStore(multiTrees1, nil, db1, coretesting.NewNopLogger()) // for store/v2 + require.NoError(t, err) + if noCommitStore { + newCommitStore = nil + } + + return NewManager(db, snapshotsManager, newStorageStore, newCommitStore, coretesting.NewNopLogger()), commitStore +} + +func TestMigrateState(t *testing.T) { + for _, noCommitStore := range []bool{false, true} { + t.Run(fmt.Sprintf("Migrate noCommitStore=%v", noCommitStore), func(t *testing.T) { + m, orgCommitStore := setupMigrationManager(t, noCommitStore) + + // apply changeset + toVersion := uint64(100) + keyCount := 10 + for version := uint64(1); version <= toVersion; version++ { + cs := corestore.NewChangeset(version) + for _, storeKey := range storeKeys { + for i := 0; i < keyCount; i++ { + cs.Add([]byte(storeKey), []byte(fmt.Sprintf("key-%d-%d", version, i)), []byte(fmt.Sprintf("value-%d-%d", version, i)), false) + } + } + require.NoError(t, orgCommitStore.WriteChangeset(cs)) + _, err := orgCommitStore.Commit(version) + require.NoError(t, err) + } + + err := m.Migrate(toVersion - 1) + require.NoError(t, err) + + // expecting error for conflicting process, since Migrate trigger snapshotter create migration, + // which start a snapshot process already. + _, err = m.snapshotsManager.Create(toVersion - 1) + require.Error(t, err) + + if m.stateCommitment != nil { + // check the migrated state + for version := uint64(1); version < toVersion; version++ { + for _, storeKey := range storeKeys { + for i := 0; i < keyCount; i++ { + val, err := m.stateCommitment.Get([]byte(storeKey), toVersion-1, []byte(fmt.Sprintf("key-%d-%d", version, i))) + require.NoError(t, err) + require.Equal(t, []byte(fmt.Sprintf("value-%d-%d", version, i)), val) + } + } + } + // check the latest state + val, err := m.stateCommitment.Get([]byte("store1"), toVersion-1, []byte("key-100-1")) + require.NoError(t, err) + require.Nil(t, val) + val, err = m.stateCommitment.Get([]byte("store2"), toVersion-1, []byte("key-100-0")) + require.NoError(t, err) + require.Nil(t, val) + } + + // check the storage + for version := uint64(1); version < toVersion; version++ { + for _, storeKey := range storeKeys { + for i := 0; i < keyCount; i++ { + val, err := m.stateStorage.Get([]byte(storeKey), toVersion-1, []byte(fmt.Sprintf("key-%d-%d", version, i))) + require.NoError(t, err) + require.Equal(t, []byte(fmt.Sprintf("value-%d-%d", version, i)), val) + } + } + } + }) + } +} + +func TestStartMigrateState(t *testing.T) { + for _, noCommitStore := range []bool{false, true} { + t.Run(fmt.Sprintf("Migrate noCommitStore=%v", noCommitStore), func(t *testing.T) { + m, orgCommitStore := setupMigrationManager(t, noCommitStore) + + chDone := make(chan struct{}) + chChangeset := make(chan *VersionedChangeset, 1) + + // apply changeset + toVersion := uint64(10) + keyCount := 5 + changesets := []corestore.Changeset{} + + for version := uint64(1); version <= toVersion; version++ { + cs := corestore.NewChangeset(version) + for _, storeKey := range storeKeys { + for i := 0; i < keyCount; i++ { + cs.Add([]byte(storeKey), []byte(fmt.Sprintf("key-%d-%d", version, i)), []byte(fmt.Sprintf("value-%d-%d", version, i)), false) + } + } + changesets = append(changesets, *cs) + require.NoError(t, orgCommitStore.WriteChangeset(cs)) + _, err := orgCommitStore.Commit(version) + require.NoError(t, err) + } + + // feed changesets to channel + go func() { + for version := uint64(1); version <= toVersion; version++ { + chChangeset <- &VersionedChangeset{ + Version: version, + Changeset: &changesets[version-1], + } + } + }() + + // check if migrate process complete + go func() { + for { + migrateVersion := m.GetMigratedVersion() + if migrateVersion == toVersion-1 { + break + } + } + + chDone <- struct{}{} + }() + + err := m.Start(toVersion-1, chChangeset, chDone) + require.NoError(t, err) + + // expecting error for conflicting process, since Migrate trigger snapshotter create migration, + // which start a snapshot process already. + _, err = m.snapshotsManager.Create(toVersion - 1) + require.Error(t, err) + + if m.stateCommitment != nil { + // check the migrated state + for version := uint64(1); version < toVersion; version++ { + for _, storeKey := range storeKeys { + for i := 0; i < keyCount; i++ { + val, err := m.stateCommitment.Get([]byte(storeKey), toVersion-1, []byte(fmt.Sprintf("key-%d-%d", version, i))) + require.NoError(t, err) + require.Equal(t, []byte(fmt.Sprintf("value-%d-%d", version, i)), val) + } + } + } + // check the latest state + val, err := m.stateCommitment.Get([]byte("store1"), toVersion-1, []byte("key-100-1")) + require.NoError(t, err) + require.Nil(t, val) + val, err = m.stateCommitment.Get([]byte("store2"), toVersion-1, []byte("key-100-0")) + require.NoError(t, err) + require.Nil(t, val) + } + + // check the storage + for version := uint64(1); version < toVersion; version++ { + for _, storeKey := range storeKeys { + for i := 0; i < keyCount; i++ { + val, err := m.stateStorage.Get([]byte(storeKey), toVersion-1, []byte(fmt.Sprintf("key-%d-%d", version, i))) + require.NoError(t, err) + require.Equal(t, []byte(fmt.Sprintf("value-%d-%d", version, i)), val) + } + } + } + + // check if migration db write change set to storage + for version := uint64(1); version < toVersion; version++ { + buf := make([]byte, 8) + binary.BigEndian.PutUint64(buf, version) + csKey := []byte(fmt.Sprintf(migrateChangesetKeyFmt, buf)) + csVal, err := m.db.Get(csKey) + require.NoError(t, err) + require.NotEmpty(t, csVal) + } + }) + } +} diff --git a/store/v2/mock/db_mock.go b/store/v2/mock/db_mock.go new file mode 100644 index 000000000000..9b962affb102 --- /dev/null +++ b/store/v2/mock/db_mock.go @@ -0,0 +1,409 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: ./types.go +// +// Generated by this command: +// +// mockgen -package mock -destination ./db_mock.go -source ./types.go +// + +// Package mock is a generated GoMock package. +package mock + +import ( + reflect "reflect" + + store "cosmossdk.io/core/store" + proof "cosmossdk.io/store/v2/proof" + gomock "go.uber.org/mock/gomock" +) + +// MockStateCommitter is a mock of StateCommitter interface. +type MockStateCommitter struct { + ctrl *gomock.Controller + recorder *MockStateCommitterMockRecorder + isgomock struct{} +} + +// MockStateCommitterMockRecorder is the mock recorder for MockStateCommitter. +type MockStateCommitterMockRecorder struct { + mock *MockStateCommitter +} + +// NewMockStateCommitter creates a new mock instance. +func NewMockStateCommitter(ctrl *gomock.Controller) *MockStateCommitter { + mock := &MockStateCommitter{ctrl: ctrl} + mock.recorder = &MockStateCommitterMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockStateCommitter) EXPECT() *MockStateCommitterMockRecorder { + return m.recorder +} + +// Close mocks base method. +func (m *MockStateCommitter) Close() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Close") + ret0, _ := ret[0].(error) + return ret0 +} + +// Close indicates an expected call of Close. +func (mr *MockStateCommitterMockRecorder) Close() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockStateCommitter)(nil).Close)) +} + +// Commit mocks base method. +func (m *MockStateCommitter) Commit(version uint64) (*proof.CommitInfo, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Commit", version) + ret0, _ := ret[0].(*proof.CommitInfo) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Commit indicates an expected call of Commit. +func (mr *MockStateCommitterMockRecorder) Commit(version any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Commit", reflect.TypeOf((*MockStateCommitter)(nil).Commit), version) +} + +// Get mocks base method. +func (m *MockStateCommitter) Get(storeKey []byte, version uint64, key []byte) ([]byte, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Get", storeKey, version, key) + ret0, _ := ret[0].([]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Get indicates an expected call of Get. +func (mr *MockStateCommitterMockRecorder) Get(storeKey, version, key any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockStateCommitter)(nil).Get), storeKey, version, key) +} + +// GetCommitInfo mocks base method. +func (m *MockStateCommitter) GetCommitInfo(version uint64) (*proof.CommitInfo, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetCommitInfo", version) + ret0, _ := ret[0].(*proof.CommitInfo) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetCommitInfo indicates an expected call of GetCommitInfo. +func (mr *MockStateCommitterMockRecorder) GetCommitInfo(version any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCommitInfo", reflect.TypeOf((*MockStateCommitter)(nil).GetCommitInfo), version) +} + +// GetLatestVersion mocks base method. +func (m *MockStateCommitter) GetLatestVersion() (uint64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetLatestVersion") + ret0, _ := ret[0].(uint64) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetLatestVersion indicates an expected call of GetLatestVersion. +func (mr *MockStateCommitterMockRecorder) GetLatestVersion() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLatestVersion", reflect.TypeOf((*MockStateCommitter)(nil).GetLatestVersion)) +} + +// GetProof mocks base method. +func (m *MockStateCommitter) GetProof(storeKey []byte, version uint64, key []byte) ([]proof.CommitmentOp, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetProof", storeKey, version, key) + ret0, _ := ret[0].([]proof.CommitmentOp) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetProof indicates an expected call of GetProof. +func (mr *MockStateCommitterMockRecorder) GetProof(storeKey, version, key any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetProof", reflect.TypeOf((*MockStateCommitter)(nil).GetProof), storeKey, version, key) +} + +// LoadVersion mocks base method. +func (m *MockStateCommitter) LoadVersion(targetVersion uint64) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "LoadVersion", targetVersion) + ret0, _ := ret[0].(error) + return ret0 +} + +// LoadVersion indicates an expected call of LoadVersion. +func (mr *MockStateCommitterMockRecorder) LoadVersion(targetVersion any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadVersion", reflect.TypeOf((*MockStateCommitter)(nil).LoadVersion), targetVersion) +} + +// LoadVersionAndUpgrade mocks base method. +func (m *MockStateCommitter) LoadVersionAndUpgrade(version uint64, upgrades *store.StoreUpgrades) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "LoadVersionAndUpgrade", version, upgrades) + ret0, _ := ret[0].(error) + return ret0 +} + +// LoadVersionAndUpgrade indicates an expected call of LoadVersionAndUpgrade. +func (mr *MockStateCommitterMockRecorder) LoadVersionAndUpgrade(version, upgrades any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadVersionAndUpgrade", reflect.TypeOf((*MockStateCommitter)(nil).LoadVersionAndUpgrade), version, upgrades) +} + +// PausePruning mocks base method. +func (m *MockStateCommitter) PausePruning(pause bool) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "PausePruning", pause) +} + +// PausePruning indicates an expected call of PausePruning. +func (mr *MockStateCommitterMockRecorder) PausePruning(pause any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PausePruning", reflect.TypeOf((*MockStateCommitter)(nil).PausePruning), pause) +} + +// Prune mocks base method. +func (m *MockStateCommitter) Prune(version uint64) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Prune", version) + ret0, _ := ret[0].(error) + return ret0 +} + +// Prune indicates an expected call of Prune. +func (mr *MockStateCommitterMockRecorder) Prune(version any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Prune", reflect.TypeOf((*MockStateCommitter)(nil).Prune), version) +} + +// SetInitialVersion mocks base method. +func (m *MockStateCommitter) SetInitialVersion(version uint64) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetInitialVersion", version) + ret0, _ := ret[0].(error) + return ret0 +} + +// SetInitialVersion indicates an expected call of SetInitialVersion. +func (mr *MockStateCommitterMockRecorder) SetInitialVersion(version any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetInitialVersion", reflect.TypeOf((*MockStateCommitter)(nil).SetInitialVersion), version) +} + +// WriteChangeset mocks base method. +func (m *MockStateCommitter) WriteChangeset(cs *store.Changeset) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "WriteChangeset", cs) + ret0, _ := ret[0].(error) + return ret0 +} + +// WriteChangeset indicates an expected call of WriteChangeset. +func (mr *MockStateCommitterMockRecorder) WriteChangeset(cs any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WriteChangeset", reflect.TypeOf((*MockStateCommitter)(nil).WriteChangeset), cs) +} + +// MockStateStorage is a mock of StateStorage interface. +type MockStateStorage struct { + ctrl *gomock.Controller + recorder *MockStateStorageMockRecorder + isgomock struct{} +} + +// MockStateStorageMockRecorder is the mock recorder for MockStateStorage. +type MockStateStorageMockRecorder struct { + mock *MockStateStorage +} + +// NewMockStateStorage creates a new mock instance. +func NewMockStateStorage(ctrl *gomock.Controller) *MockStateStorage { + mock := &MockStateStorage{ctrl: ctrl} + mock.recorder = &MockStateStorageMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockStateStorage) EXPECT() *MockStateStorageMockRecorder { + return m.recorder +} + +// ApplyChangeset mocks base method. +func (m *MockStateStorage) ApplyChangeset(cs *store.Changeset) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ApplyChangeset", cs) + ret0, _ := ret[0].(error) + return ret0 +} + +// ApplyChangeset indicates an expected call of ApplyChangeset. +func (mr *MockStateStorageMockRecorder) ApplyChangeset(cs any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ApplyChangeset", reflect.TypeOf((*MockStateStorage)(nil).ApplyChangeset), cs) +} + +// Close mocks base method. +func (m *MockStateStorage) Close() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Close") + ret0, _ := ret[0].(error) + return ret0 +} + +// Close indicates an expected call of Close. +func (mr *MockStateStorageMockRecorder) Close() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockStateStorage)(nil).Close)) +} + +// Get mocks base method. +func (m *MockStateStorage) Get(storeKey []byte, version uint64, key []byte) ([]byte, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Get", storeKey, version, key) + ret0, _ := ret[0].([]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Get indicates an expected call of Get. +func (mr *MockStateStorageMockRecorder) Get(storeKey, version, key any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockStateStorage)(nil).Get), storeKey, version, key) +} + +// GetLatestVersion mocks base method. +func (m *MockStateStorage) GetLatestVersion() (uint64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetLatestVersion") + ret0, _ := ret[0].(uint64) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetLatestVersion indicates an expected call of GetLatestVersion. +func (mr *MockStateStorageMockRecorder) GetLatestVersion() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLatestVersion", reflect.TypeOf((*MockStateStorage)(nil).GetLatestVersion)) +} + +// Has mocks base method. +func (m *MockStateStorage) Has(storeKey []byte, version uint64, key []byte) (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Has", storeKey, version, key) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Has indicates an expected call of Has. +func (mr *MockStateStorageMockRecorder) Has(storeKey, version, key any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Has", reflect.TypeOf((*MockStateStorage)(nil).Has), storeKey, version, key) +} + +// Iterator mocks base method. +func (m *MockStateStorage) Iterator(storeKey []byte, version uint64, start, end []byte) (store.Iterator, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Iterator", storeKey, version, start, end) + ret0, _ := ret[0].(store.Iterator) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Iterator indicates an expected call of Iterator. +func (mr *MockStateStorageMockRecorder) Iterator(storeKey, version, start, end any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Iterator", reflect.TypeOf((*MockStateStorage)(nil).Iterator), storeKey, version, start, end) +} + +// PausePruning mocks base method. +func (m *MockStateStorage) PausePruning(pause bool) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "PausePruning", pause) +} + +// PausePruning indicates an expected call of PausePruning. +func (mr *MockStateStorageMockRecorder) PausePruning(pause any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PausePruning", reflect.TypeOf((*MockStateStorage)(nil).PausePruning), pause) +} + +// Prune mocks base method. +func (m *MockStateStorage) Prune(version uint64) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Prune", version) + ret0, _ := ret[0].(error) + return ret0 +} + +// Prune indicates an expected call of Prune. +func (mr *MockStateStorageMockRecorder) Prune(version any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Prune", reflect.TypeOf((*MockStateStorage)(nil).Prune), version) +} + +// PruneStoreKeys mocks base method. +func (m *MockStateStorage) PruneStoreKeys(storeKeys []string, version uint64) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "PruneStoreKeys", storeKeys, version) + ret0, _ := ret[0].(error) + return ret0 +} + +// PruneStoreKeys indicates an expected call of PruneStoreKeys. +func (mr *MockStateStorageMockRecorder) PruneStoreKeys(storeKeys, version any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PruneStoreKeys", reflect.TypeOf((*MockStateStorage)(nil).PruneStoreKeys), storeKeys, version) +} + +// ReverseIterator mocks base method. +func (m *MockStateStorage) ReverseIterator(storeKey []byte, version uint64, start, end []byte) (store.Iterator, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ReverseIterator", storeKey, version, start, end) + ret0, _ := ret[0].(store.Iterator) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ReverseIterator indicates an expected call of ReverseIterator. +func (mr *MockStateStorageMockRecorder) ReverseIterator(storeKey, version, start, end any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReverseIterator", reflect.TypeOf((*MockStateStorage)(nil).ReverseIterator), storeKey, version, start, end) +} + +// SetLatestVersion mocks base method. +func (m *MockStateStorage) SetLatestVersion(version uint64) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetLatestVersion", version) + ret0, _ := ret[0].(error) + return ret0 +} + +// SetLatestVersion indicates an expected call of SetLatestVersion. +func (mr *MockStateStorageMockRecorder) SetLatestVersion(version any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetLatestVersion", reflect.TypeOf((*MockStateStorage)(nil).SetLatestVersion), version) +} + +// VersionExists mocks base method. +func (m *MockStateStorage) VersionExists(v uint64) (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "VersionExists", v) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// VersionExists indicates an expected call of VersionExists. +func (mr *MockStateStorageMockRecorder) VersionExists(v any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VersionExists", reflect.TypeOf((*MockStateStorage)(nil).VersionExists), v) +} diff --git a/store/v2/pruning/manager.go b/store/v2/pruning/manager.go new file mode 100644 index 000000000000..4e61a7459d08 --- /dev/null +++ b/store/v2/pruning/manager.go @@ -0,0 +1,70 @@ +package pruning + +import ( + "cosmossdk.io/store/v2" +) + +// Manager is a struct that manages the pruning of old versions of the SC and SS. +type Manager struct { + // scPruner is the pruner for the SC. + scPruner store.Pruner + // scPruningOption are the pruning options for the SC. + scPruningOption *store.PruningOption + // ssPruner is the pruner for the SS. + ssPruner store.Pruner + // ssPruningOption are the pruning options for the SS. + ssPruningOption *store.PruningOption +} + +// NewManager creates a new Pruning Manager. +func NewManager(scPruner, ssPruner store.Pruner, scPruningOption, ssPruningOption *store.PruningOption) *Manager { + return &Manager{ + scPruner: scPruner, + scPruningOption: scPruningOption, + ssPruner: ssPruner, + ssPruningOption: ssPruningOption, + } +} + +// Prune prunes the SC and SS to the provided version. +// +// NOTE: It can be called outside the store manually. +func (m *Manager) Prune(version uint64) error { + // Prune the SC. + if m.scPruningOption != nil { + if prune, pruneTo := m.scPruningOption.ShouldPrune(version); prune { + if err := m.scPruner.Prune(pruneTo); err != nil { + return err + } + } + } + + // Prune the SS. + if m.ssPruningOption != nil { + if prune, pruneTo := m.ssPruningOption.ShouldPrune(version); prune { + if err := m.ssPruner.Prune(pruneTo); err != nil { + return err + } + } + } + + return nil +} + +func (m *Manager) signalPruning(pause bool) { + if scPausablePruner, ok := m.scPruner.(store.PausablePruner); ok { + scPausablePruner.PausePruning(pause) + } + if ssPausablePruner, ok := m.ssPruner.(store.PausablePruner); ok { + ssPausablePruner.PausePruning(pause) + } +} + +func (m *Manager) PausePruning() { + m.signalPruning(true) +} + +func (m *Manager) ResumePruning(version uint64) error { + m.signalPruning(false) + return m.Prune(version) +} diff --git a/store/v2/pruning/manager_test.go b/store/v2/pruning/manager_test.go new file mode 100644 index 000000000000..0a0333451ae5 --- /dev/null +++ b/store/v2/pruning/manager_test.go @@ -0,0 +1,259 @@ +package pruning + +import ( + "fmt" + "testing" + "time" + + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + + corestore "cosmossdk.io/core/store" + coretesting "cosmossdk.io/core/testing" + "cosmossdk.io/store/v2" + "cosmossdk.io/store/v2/commitment" + "cosmossdk.io/store/v2/commitment/iavl" + dbm "cosmossdk.io/store/v2/db" + "cosmossdk.io/store/v2/storage" + "cosmossdk.io/store/v2/storage/sqlite" +) + +var storeKeys = []string{"store1", "store2", "store3"} + +type PruningManagerTestSuite struct { + suite.Suite + + manager *Manager + sc *commitment.CommitStore + ss *storage.StorageStore +} + +func TestPruningManagerTestSuite(t *testing.T) { + suite.Run(t, &PruningManagerTestSuite{}) +} + +func (s *PruningManagerTestSuite) SetupTest() { + nopLog := coretesting.NewNopLogger() + var err error + + mdb := dbm.NewMemDB() + multiTrees := make(map[string]commitment.Tree) + for _, storeKey := range storeKeys { + prefixDB := dbm.NewPrefixDB(mdb, []byte(storeKey)) + multiTrees[storeKey] = iavl.NewIavlTree(prefixDB, nopLog, iavl.DefaultConfig()) + } + s.sc, err = commitment.NewCommitStore(multiTrees, nil, mdb, nopLog) + s.Require().NoError(err) + + sqliteDB, err := sqlite.New(s.T().TempDir()) + s.Require().NoError(err) + s.ss = storage.NewStorageStore(sqliteDB, nopLog) + scPruningOption := store.NewPruningOptionWithCustom(0, 1) // prune all + ssPruningOption := store.NewPruningOptionWithCustom(5, 10) // prune some + s.manager = NewManager(s.sc, s.ss, scPruningOption, ssPruningOption) +} + +func (s *PruningManagerTestSuite) TestPrune() { + // commit changesets with pruning + toVersion := uint64(100) + keyCount := 10 + for version := uint64(1); version <= toVersion; version++ { + cs := corestore.NewChangeset(version) + for _, storeKey := range storeKeys { + for i := 0; i < keyCount; i++ { + cs.Add([]byte(storeKey), []byte(fmt.Sprintf("key-%d-%d", version, i)), []byte(fmt.Sprintf("value-%d-%d", version, i)), false) + } + } + s.Require().NoError(s.sc.WriteChangeset(cs)) + _, err := s.sc.Commit(version) + s.Require().NoError(err) + + s.Require().NoError(s.ss.ApplyChangeset(cs)) + + s.Require().NoError(s.manager.Prune(version)) + } + + // wait for the pruning to finish in the commitment store + checkSCPrune := func() bool { + count := 0 + for _, storeKey := range storeKeys { + _, err := s.sc.GetProof([]byte(storeKey), toVersion-1, []byte(fmt.Sprintf("key-%d-%d", toVersion-1, 0))) + if err != nil { + count++ + } + } + + return count == len(storeKeys) + } + s.Require().Eventually(checkSCPrune, 10*time.Second, 1*time.Second) + + // check the storage store + _, pruneVersion := s.manager.ssPruningOption.ShouldPrune(toVersion) + for version := uint64(1); version <= toVersion; version++ { + for _, storeKey := range storeKeys { + for i := 0; i < keyCount; i++ { + key := []byte(fmt.Sprintf("key-%d-%d", version, i)) + value, err := s.ss.Get([]byte(storeKey), version, key) + if version <= pruneVersion { + s.Require().Nil(value) + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().Equal([]byte(fmt.Sprintf("value-%d-%d", version, i)), value) + } + } + } + } +} + +func TestPruningOption(t *testing.T) { + testCases := []struct { + name string + options *store.PruningOption + version uint64 + pruning bool + pruneVersion uint64 + }{ + { + name: "no pruning", + options: store.NewPruningOptionWithCustom(100, 0), + version: 100, + pruning: false, + pruneVersion: 0, + }, + { + name: "prune all", + options: store.NewPruningOptionWithCustom(0, 1), + version: 19, + pruning: true, + pruneVersion: 18, + }, + { + name: "prune none", + options: store.NewPruningOptionWithCustom(100, 10), + version: 19, + pruning: false, + pruneVersion: 0, + }, + { + name: "prune some", + options: store.NewPruningOptionWithCustom(10, 50), + version: 100, + pruning: true, + pruneVersion: 89, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + pruning, pruneVersion := tc.options.ShouldPrune(tc.version) + require.Equal(t, tc.pruning, pruning) + require.Equal(t, tc.pruneVersion, pruneVersion) + }) + } +} + +func (s *PruningManagerTestSuite) TestSignalCommit() { + // commit version 1 + cs := corestore.NewChangeset(1) + for _, storeKey := range storeKeys { + cs.Add([]byte(storeKey), []byte(fmt.Sprintf("key-%d-%d", 1, 0)), []byte(fmt.Sprintf("value-%d-%d", 1, 0)), false) + } + + s.Require().NoError(s.sc.WriteChangeset(cs)) + _, err := s.sc.Commit(1) + s.Require().NoError(err) + + s.Require().NoError(s.ss.ApplyChangeset(cs)) + + // commit version 2 + for _, storeKey := range storeKeys { + cs.Add([]byte(storeKey), []byte(fmt.Sprintf("key-%d-%d", 2, 0)), []byte(fmt.Sprintf("value-%d-%d", 2, 0)), false) + } + cs.Version = 2 + + // signaling commit has started + s.manager.PausePruning() + + s.Require().NoError(s.sc.WriteChangeset(cs)) + _, err = s.sc.Commit(2) + s.Require().NoError(err) + + s.Require().NoError(s.ss.ApplyChangeset(cs)) + + // try prune before signaling commit has finished + s.Require().NoError(s.manager.Prune(2)) + + // proof is removed no matter SignalCommit has not yet inform that commit process has finish + // since commitInfo is remove async with tree data + checkSCPrune := func() bool { + count := 0 + for _, storeKey := range storeKeys { + _, err := s.sc.GetProof([]byte(storeKey), 1, []byte(fmt.Sprintf("key-%d-%d", 1, 0))) + if err != nil { + count++ + } + } + + return count == len(storeKeys) + } + s.Require().Eventually(checkSCPrune, 10*time.Second, 1*time.Second) + + // data from state commitment should not be pruned since we haven't signal the commit process has finished + val, err := s.sc.Get([]byte(storeKeys[0]), 1, []byte(fmt.Sprintf("key-%d-%d", 1, 0))) + s.Require().NoError(err) + s.Require().Equal(val, []byte(fmt.Sprintf("value-%d-%d", 1, 0))) + + // signaling commit has finished, version 1 should be pruned + err = s.manager.ResumePruning(2) + s.Require().NoError(err) + + checkSCPrune = func() bool { + count := 0 + for _, storeKey := range storeKeys { + _, err := s.sc.GetProof([]byte(storeKey), 1, []byte(fmt.Sprintf("key-%d-%d", 1, 0))) + if err != nil { + count++ + } + } + + return count == len(storeKeys) + } + s.Require().Eventually(checkSCPrune, 10*time.Second, 1*time.Second) + + // try with signal commit start and finish accordingly + // commit changesets with pruning + toVersion := uint64(100) + keyCount := 10 + for version := uint64(3); version <= toVersion; version++ { + cs := corestore.NewChangeset(version) + for _, storeKey := range storeKeys { + for i := 0; i < keyCount; i++ { + cs.Add([]byte(storeKey), []byte(fmt.Sprintf("key-%d-%d", version, i)), []byte(fmt.Sprintf("value-%d-%d", version, i)), false) + } + } + s.manager.PausePruning() + + s.Require().NoError(s.sc.WriteChangeset(cs)) + _, err := s.sc.Commit(version) + s.Require().NoError(err) + + s.Require().NoError(s.ss.ApplyChangeset(cs)) + err = s.manager.ResumePruning(version) + s.Require().NoError(err) + } + + // wait for the pruning to finish in the commitment store + checkSCPrune = func() bool { + count := 0 + for _, storeKey := range storeKeys { + _, err := s.sc.GetProof([]byte(storeKey), toVersion-1, []byte(fmt.Sprintf("key-%d-%d", toVersion-1, 0))) + if err != nil { + count++ + } + } + + return count == len(storeKeys) + } + s.Require().Eventually(checkSCPrune, 10*time.Second, 1*time.Second) +} diff --git a/store/v2/root/builder.go b/store/v2/root/builder.go new file mode 100644 index 000000000000..199184f8e761 --- /dev/null +++ b/store/v2/root/builder.go @@ -0,0 +1,95 @@ +package root + +import ( + "fmt" + "path/filepath" + + "cosmossdk.io/log" + "cosmossdk.io/store/v2" + "cosmossdk.io/store/v2/db" +) + +// Builder is the interface for a store/v2 RootStore builder. +// RootStores built by the Cosmos SDK typically involve a 2 phase initialization: +// 1. Namespace registration +// 2. Configuration and loading +// +// The Builder interface is used to facilitate this pattern. Namespaces (store keys) are registered +// by calling RegisterKey before Build is called. Build is then called with a Config +// object and a RootStore is returned. Calls to Get may return the `RootStore` if Build +// was successful, but that's left up to the implementation. +type Builder interface { + // Build creates a new store/v2 RootStore from the given Config. + Build(log.Logger, *Config) (store.RootStore, error) + // RegisterKey registers a store key (namespace) to be used when building the RootStore. + RegisterKey(string) + // Get returns the Store. Build should be called before calling Get or the result will be nil. + Get() store.RootStore +} + +var _ Builder = (*builder)(nil) + +// builder is the default builder for a store/v2 RootStore satisfying the Store interface. +// Tangibly it combines store key registration and a top-level Config to create a RootStore by calling +// the CreateRootStore factory function. +type builder struct { + // input + storeKeys map[string]struct{} + + // output + store store.RootStore +} + +func NewBuilder() Builder { + return &builder{storeKeys: make(map[string]struct{})} +} + +// Build creates a new store/v2 RootStore. +func (sb *builder) Build( + logger log.Logger, + config *Config, +) (store.RootStore, error) { + if sb.store != nil { + return sb.store, nil + } + if config.Home == "" { + return nil, fmt.Errorf("home directory is required") + } + scRawDb, err := db.NewDB( + db.DBType(config.AppDBBackend), + "application", + filepath.Join(config.Home, "data"), + nil, + ) + if err != nil { + return nil, fmt.Errorf("failed to create SCRawDB: %w", err) + } + + var storeKeys []string + for key := range sb.storeKeys { + storeKeys = append(storeKeys, key) + } + + factoryOptions := &FactoryOptions{ + Logger: logger.With("module", "store"), + RootDir: config.Home, + Options: config.Options, + StoreKeys: storeKeys, + SCRawDB: scRawDb, + } + + rs, err := CreateRootStore(factoryOptions) + if err != nil { + return nil, fmt.Errorf("failed to create root store: %w", err) + } + sb.store = rs + return sb.store, nil +} + +func (sb *builder) Get() store.RootStore { + return sb.store +} + +func (sb *builder) RegisterKey(key string) { + sb.storeKeys[key] = struct{}{} +} diff --git a/store/v2/root/migrate_test.go b/store/v2/root/migrate_test.go new file mode 100644 index 000000000000..2d83038b812e --- /dev/null +++ b/store/v2/root/migrate_test.go @@ -0,0 +1,163 @@ +package root + +import ( + "fmt" + "testing" + "time" + + "github.com/stretchr/testify/suite" + + corestore "cosmossdk.io/core/store" + coretesting "cosmossdk.io/core/testing" + "cosmossdk.io/log" + "cosmossdk.io/store/v2" + "cosmossdk.io/store/v2/commitment" + "cosmossdk.io/store/v2/commitment/iavl" + dbm "cosmossdk.io/store/v2/db" + "cosmossdk.io/store/v2/migration" + "cosmossdk.io/store/v2/pruning" + "cosmossdk.io/store/v2/snapshots" + "cosmossdk.io/store/v2/storage" + "cosmossdk.io/store/v2/storage/sqlite" +) + +var storeKeys = []string{"store1", "store2", "store3"} + +type MigrateStoreTestSuite struct { + suite.Suite + + rootStore store.RootStore +} + +func TestMigrateStoreTestSuite(t *testing.T) { + suite.Run(t, &MigrateStoreTestSuite{}) +} + +func (s *MigrateStoreTestSuite) SetupTest() { + testLog := log.NewTestLogger(s.T()) + nopLog := coretesting.NewNopLogger() + + mdb := dbm.NewMemDB() + multiTrees := make(map[string]commitment.Tree) + for _, storeKey := range storeKeys { + prefixDB := dbm.NewPrefixDB(mdb, []byte(storeKey)) + multiTrees[storeKey] = iavl.NewIavlTree(prefixDB, nopLog, iavl.DefaultConfig()) + } + orgSC, err := commitment.NewCommitStore(multiTrees, nil, mdb, testLog) + s.Require().NoError(err) + + // apply changeset against the original store + toVersion := uint64(200) + keyCount := 10 + for version := uint64(1); version <= toVersion; version++ { + cs := corestore.NewChangeset(version) + for _, storeKey := range storeKeys { + for i := 0; i < keyCount; i++ { + cs.Add([]byte(storeKey), []byte(fmt.Sprintf("key-%d-%d", version, i)), []byte(fmt.Sprintf("value-%d-%d", version, i)), false) + } + } + s.Require().NoError(orgSC.WriteChangeset(cs)) + _, err = orgSC.Commit(version) + s.Require().NoError(err) + } + + // create a new storage and commitment stores + sqliteDB, err := sqlite.New(s.T().TempDir()) + s.Require().NoError(err) + ss := storage.NewStorageStore(sqliteDB, testLog) + + multiTrees1 := make(map[string]commitment.Tree) + for _, storeKey := range storeKeys { + multiTrees1[storeKey] = iavl.NewIavlTree(dbm.NewMemDB(), nopLog, iavl.DefaultConfig()) + } + sc, err := commitment.NewCommitStore(multiTrees1, nil, dbm.NewMemDB(), testLog) + s.Require().NoError(err) + + snapshotsStore, err := snapshots.NewStore(s.T().TempDir()) + s.Require().NoError(err) + snapshotManager := snapshots.NewManager(snapshotsStore, snapshots.NewSnapshotOptions(1500, 2), orgSC, nil, nil, testLog) + migrationManager := migration.NewManager(dbm.NewMemDB(), snapshotManager, ss, sc, testLog) + pm := pruning.NewManager(sc, ss, nil, nil) + + // assume no storage store, simulate the migration process + s.rootStore, err = New(dbm.NewMemDB(), testLog, ss, orgSC, pm, migrationManager, nil) + s.Require().NoError(err) +} + +func (s *MigrateStoreTestSuite) TestMigrateState() { + err := s.rootStore.LoadLatestVersion() + s.Require().NoError(err) + originalLatestVersion, err := s.rootStore.GetLatestVersion() + s.Require().NoError(err) + + // check if the Query fallback to the original SC + for version := uint64(1); version <= originalLatestVersion; version++ { + for _, storeKey := range storeKeys { + for i := 0; i < 10; i++ { + res, err := s.rootStore.Query([]byte(storeKey), version, []byte(fmt.Sprintf("key-%d-%d", version, i)), true) + s.Require().NoError(err) + s.Require().Equal([]byte(fmt.Sprintf("value-%d-%d", version, i)), res.Value) + } + } + } + + // continue to apply changeset against the original store + latestVersion := originalLatestVersion + 1 + keyCount := 10 + for ; latestVersion < 2*originalLatestVersion; latestVersion++ { + cs := corestore.NewChangeset(latestVersion) + for _, storeKey := range storeKeys { + for i := 0; i < keyCount; i++ { + cs.Add([]byte(storeKey), []byte(fmt.Sprintf("key-%d-%d", latestVersion, i)), []byte(fmt.Sprintf("value-%d-%d", latestVersion, i)), false) + } + } + _, err = s.rootStore.Commit(cs) + s.Require().NoError(err) + + // check if the migration is completed + ver, err := s.rootStore.GetStateStorage().GetLatestVersion() + s.Require().NoError(err) + if ver == latestVersion { + break + } + + // add some delay to simulate the consensus process + time.Sleep(100 * time.Millisecond) + } + + // check if the migration is successful + version, err := s.rootStore.GetLatestVersion() + s.Require().NoError(err) + s.Require().Equal(latestVersion, version) + + // query against the migrated store + for version := uint64(1); version <= latestVersion; version++ { + for _, storeKey := range storeKeys { + for i := 0; i < keyCount; i++ { + targetVersion := version + if version < originalLatestVersion { + targetVersion = originalLatestVersion + } + res, err := s.rootStore.Query([]byte(storeKey), targetVersion, []byte(fmt.Sprintf("key-%d-%d", version, i)), true) + s.Require().NoError(err) + s.Require().Equal([]byte(fmt.Sprintf("value-%d-%d", version, i)), res.Value) + } + } + } + + // apply changeset against the migrated store + for version := latestVersion + 1; version <= latestVersion+10; version++ { + cs := corestore.NewChangeset(version) + for _, storeKey := range storeKeys { + for i := 0; i < keyCount; i++ { + cs.Add([]byte(storeKey), []byte(fmt.Sprintf("key-%d-%d", version, i)), []byte(fmt.Sprintf("value-%d-%d", version, i)), false) + } + } + _, err = s.rootStore.Commit(cs) + s.Require().NoError(err) + } + + version, err = s.rootStore.GetLatestVersion() + s.Require().NoError(err) + s.Require().Equal(latestVersion+10, version) +} diff --git a/store/v2/root/store.go b/store/v2/root/store.go new file mode 100644 index 000000000000..59363e2fb35b --- /dev/null +++ b/store/v2/root/store.go @@ -0,0 +1,453 @@ +package root + +import ( + "crypto/sha256" + "errors" + "fmt" + "io" + "sync" + "time" + + "golang.org/x/sync/errgroup" + + corelog "cosmossdk.io/core/log" + corestore "cosmossdk.io/core/store" + "cosmossdk.io/store/v2" + "cosmossdk.io/store/v2/metrics" + "cosmossdk.io/store/v2/migration" + "cosmossdk.io/store/v2/proof" + "cosmossdk.io/store/v2/pruning" +) + +var ( + _ store.RootStore = (*Store)(nil) + _ store.UpgradeableStore = (*Store)(nil) +) + +// Store defines the SDK's default RootStore implementation. It contains a single +// State Storage (SS) backend and a single State Commitment (SC) backend. The SC +// backend may or may not support multiple store keys and is implementation +// dependent. +type Store struct { + logger corelog.Logger + + // holds the db instance for closing it + dbCloser io.Closer + + // stateStorage reflects the state storage backend + stateStorage store.VersionedWriter + + // stateCommitment reflects the state commitment (SC) backend + stateCommitment store.Committer + + // lastCommitInfo reflects the last version/hash that has been committed + lastCommitInfo *proof.CommitInfo + + // telemetry reflects a telemetry agent responsible for emitting metrics (if any) + telemetry metrics.StoreMetrics + + // pruningManager reflects the pruning manager used to prune state of the SS and SC backends + pruningManager *pruning.Manager + + // Migration related fields + // migrationManager reflects the migration manager used to migrate state from v1 to v2 + migrationManager *migration.Manager + // chChangeset reflects the channel used to send the changeset to the migration manager + chChangeset chan *migration.VersionedChangeset + // chDone reflects the channel used to signal the migration manager that the migration + // is done + chDone chan struct{} + // isMigrating reflects whether the store is currently migrating + isMigrating bool +} + +// New creates a new root Store instance. +// +// NOTE: The migration manager is optional and can be nil if no migration is required. +func New( + dbCloser io.Closer, + logger corelog.Logger, + ss store.VersionedWriter, + sc store.Committer, + pm *pruning.Manager, + mm *migration.Manager, + m metrics.StoreMetrics, +) (store.RootStore, error) { + return &Store{ + dbCloser: dbCloser, + logger: logger, + stateStorage: ss, + stateCommitment: sc, + pruningManager: pm, + migrationManager: mm, + telemetry: m, + isMigrating: mm != nil, + }, nil +} + +// Close closes the store and resets all internal fields. Note, Close() is NOT +// idempotent and should only be called once. +func (s *Store) Close() (err error) { + err = errors.Join(err, s.stateStorage.Close()) + err = errors.Join(err, s.stateCommitment.Close()) + err = errors.Join(err, s.dbCloser.Close()) + + s.stateStorage = nil + s.stateCommitment = nil + s.lastCommitInfo = nil + + return err +} + +func (s *Store) SetMetrics(m metrics.Metrics) { + s.telemetry = m +} + +func (s *Store) SetInitialVersion(v uint64) error { + return s.stateCommitment.SetInitialVersion(v) +} + +// getVersionedReader returns a VersionedReader based on the given version. If the +// version exists in the state storage, it returns the state storage. +// If not, it checks if the state commitment implements the VersionedReader interface +// and the version exists in the state commitment, since the state storage will be +// synced during migration. +func (s *Store) getVersionedReader(version uint64) (store.VersionedReader, error) { + isExist, err := s.stateStorage.VersionExists(version) + if err != nil { + return nil, err + } + if isExist { + return s.stateStorage, nil + } + + if vReader, ok := s.stateCommitment.(store.VersionedReader); ok { + isExist, err := vReader.VersionExists(version) + if err != nil { + return nil, err + } + if isExist { + return vReader, nil + } + } + + return nil, fmt.Errorf("version %d does not exist", version) +} + +func (s *Store) StateLatest() (uint64, corestore.ReaderMap, error) { + v, err := s.GetLatestVersion() + if err != nil { + return 0, nil, err + } + + vReader, err := s.getVersionedReader(v) + if err != nil { + return 0, nil, err + } + + return v, NewReaderMap(v, vReader), nil +} + +// StateAt returns a read-only view of the state at a given version. +func (s *Store) StateAt(v uint64) (corestore.ReaderMap, error) { + vReader, err := s.getVersionedReader(v) + return NewReaderMap(v, vReader), err +} + +func (s *Store) GetStateStorage() store.VersionedWriter { + return s.stateStorage +} + +func (s *Store) GetStateCommitment() store.Committer { + return s.stateCommitment +} + +// LastCommitID returns a CommitID based off of the latest internal CommitInfo. +// If an internal CommitInfo is not set, a new one will be returned with only the +// latest version set, which is based off of the SC view. +func (s *Store) LastCommitID() (proof.CommitID, error) { + if s.lastCommitInfo != nil { + return s.lastCommitInfo.CommitID(), nil + } + + latestVersion, err := s.stateCommitment.GetLatestVersion() + if err != nil { + return proof.CommitID{}, err + } + // if the latest version is 0, we return a CommitID with version 0 and a hash of an empty byte slice + bz := sha256.Sum256([]byte{}) + + return proof.CommitID{Version: latestVersion, Hash: bz[:]}, nil +} + +// GetLatestVersion returns the latest version based on the latest internal +// CommitInfo. An error is returned if the latest CommitInfo or version cannot +// be retrieved. +func (s *Store) GetLatestVersion() (uint64, error) { + lastCommitID, err := s.LastCommitID() + if err != nil { + return 0, err + } + + return lastCommitID.Version, nil +} + +func (s *Store) Query(storeKey []byte, version uint64, key []byte, prove bool) (store.QueryResult, error) { + if s.telemetry != nil { + now := time.Now() + defer s.telemetry.MeasureSince(now, "root_store", "query") + } + + var val []byte + var err error + if s.isMigrating { // if we're migrating, we need to query the SC backend + val, err = s.stateCommitment.Get(storeKey, version, key) + if err != nil { + return store.QueryResult{}, fmt.Errorf("failed to query SC store: %w", err) + } + } else { + val, err = s.stateStorage.Get(storeKey, version, key) + if err != nil { + return store.QueryResult{}, fmt.Errorf("failed to query SS store: %w", err) + } + if val == nil { + // fallback to querying SC backend if not found in SS backend + // + // Note, this should only used during migration, i.e. while SS and IAVL v2 + // are being asynchronously synced. + bz, scErr := s.stateCommitment.Get(storeKey, version, key) + if scErr != nil { + return store.QueryResult{}, fmt.Errorf("failed to query SC store: %w", scErr) + } + val = bz + } + } + + result := store.QueryResult{ + Key: key, + Value: val, + Version: version, + } + + if prove { + result.ProofOps, err = s.stateCommitment.GetProof(storeKey, version, key) + if err != nil { + return store.QueryResult{}, fmt.Errorf("failed to get SC store proof: %w", err) + } + } + + return result, nil +} + +func (s *Store) LoadLatestVersion() error { + if s.telemetry != nil { + now := time.Now() + defer s.telemetry.MeasureSince(now, "root_store", "load_latest_version") + } + + lv, err := s.GetLatestVersion() + if err != nil { + return err + } + + return s.loadVersion(lv, nil) +} + +func (s *Store) LoadVersion(version uint64) error { + if s.telemetry != nil { + now := time.Now() + defer s.telemetry.MeasureSince(now, "root_store", "load_version") + } + + return s.loadVersion(version, nil) +} + +// LoadVersionAndUpgrade implements the UpgradeableStore interface. +// +// NOTE: It cannot be called while the store is migrating. +func (s *Store) LoadVersionAndUpgrade(version uint64, upgrades *corestore.StoreUpgrades) error { + if upgrades == nil { + return errors.New("upgrades cannot be nil") + } + + if s.telemetry != nil { + defer s.telemetry.MeasureSince(time.Now(), "root_store", "load_version_and_upgrade") + } + + if s.isMigrating { + return errors.New("cannot upgrade while migrating") + } + + if err := s.loadVersion(version, upgrades); err != nil { + return err + } + + // if the state storage implements the UpgradableDatabase interface, prune the + // deleted store keys + upgradableDatabase, ok := s.stateStorage.(store.UpgradableDatabase) + if ok { + if err := upgradableDatabase.PruneStoreKeys(upgrades.Deleted, version); err != nil { + return fmt.Errorf("failed to prune store keys %v: %w", upgrades.Deleted, err) + } + } + + return nil +} + +func (s *Store) loadVersion(v uint64, upgrades *corestore.StoreUpgrades) error { + s.logger.Debug("loading version", "version", v) + + if upgrades == nil { + if err := s.stateCommitment.LoadVersion(v); err != nil { + return fmt.Errorf("failed to load SC version %d: %w", v, err) + } + } else { + // if upgrades are provided, we need to load the version and apply the upgrades + upgradeableStore, ok := s.stateCommitment.(store.UpgradeableStore) + if !ok { + return errors.New("SC store does not support upgrades") + } + if err := upgradeableStore.LoadVersionAndUpgrade(v, upgrades); err != nil { + return fmt.Errorf("failed to load SS version with upgrades %d: %w", v, err) + } + } + + // set lastCommitInfo explicitly s.t. Commit commits the correct version, i.e. v+1 + var err error + s.lastCommitInfo, err = s.stateCommitment.GetCommitInfo(v) + if err != nil { + return fmt.Errorf("failed to get commit info for version %d: %w", v, err) + } + + // if we're migrating, we need to start the migration process + if s.isMigrating { + s.startMigration() + } + + return nil +} + +// Commit commits all state changes to the underlying SS and SC backends. It +// writes a batch of the changeset to the SC tree, and retrieves the CommitInfo +// from the SC tree. Finally, it commits the SC tree and returns the hash of +// the CommitInfo. +func (s *Store) Commit(cs *corestore.Changeset) ([]byte, error) { + if s.telemetry != nil { + now := time.Now() + defer s.telemetry.MeasureSince(now, "root_store", "commit") + } + + if err := s.handleMigration(cs); err != nil { + return nil, err + } + + // signal to the pruning manager that a new version is about to be committed + // this may be required if the SS and SC backends implementation have the + // background pruning process (iavl v1 for example) which must be paused during the commit + s.pruningManager.PausePruning() + + eg := new(errgroup.Group) + + // if migrating the changeset will be sent to migration manager to fill SS + // otherwise commit to SS async here + if !s.isMigrating { + eg.Go(func() error { + if err := s.stateStorage.ApplyChangeset(cs); err != nil { + return fmt.Errorf("failed to commit SS: %w", err) + } + + return nil + }) + } + + // commit SC async + var cInfo *proof.CommitInfo + eg.Go(func() error { + if err := s.stateCommitment.WriteChangeset(cs); err != nil { + return fmt.Errorf("failed to write batch to SC store: %w", err) + } + var scErr error + cInfo, scErr = s.stateCommitment.Commit(cs.Version) + if scErr != nil { + return fmt.Errorf("failed to commit SC store: %w", scErr) + } + return nil + }) + + if err := eg.Wait(); err != nil { + return nil, err + } + + if cInfo.Version != cs.Version { + return nil, fmt.Errorf("commit version mismatch: got %d, expected %d", cInfo.Version, cs.Version) + } + s.lastCommitInfo = cInfo + + // signal to the pruning manager that the commit is done + if err := s.pruningManager.ResumePruning(s.lastCommitInfo.Version); err != nil { + s.logger.Error("failed to signal commit done to pruning manager", "err", err) + } + + return s.lastCommitInfo.Hash(), nil +} + +// startMigration starts a migration process to migrate the RootStore/v1 to the +// SS and SC backends of store/v2 and initializes the channels. +// It runs in a separate goroutine and replaces the current RootStore with the +// migrated new backends once the migration is complete. +// +// NOTE: This method should only be called once after loadVersion. +func (s *Store) startMigration() { + // buffer at most 1 changeset, if the receiver is behind attempting to buffer + // more than 1 will block. + s.chChangeset = make(chan *migration.VersionedChangeset, 1) + // it is used to signal the migration manager that the migration is done + s.chDone = make(chan struct{}) + + mtx := sync.Mutex{} + mtx.Lock() + go func() { + version := s.lastCommitInfo.Version + s.logger.Info("starting migration", "version", version) + mtx.Unlock() + if err := s.migrationManager.Start(version, s.chChangeset, s.chDone); err != nil { + s.logger.Error("failed to start migration", "err", err) + } + }() + + // wait for the migration manager to start + mtx.Lock() + defer mtx.Unlock() +} + +func (s *Store) handleMigration(cs *corestore.Changeset) error { + if s.isMigrating { + // if the migration manager has already migrated to the version, close the + // channels and replace the state commitment + if s.migrationManager.GetMigratedVersion() == s.lastCommitInfo.Version { + close(s.chDone) + close(s.chChangeset) + s.isMigrating = false + // close the old state commitment and replace it with the new one + if err := s.stateCommitment.Close(); err != nil { + return fmt.Errorf("failed to close the old SC store: %w", err) + } + newStateCommitment := s.migrationManager.GetStateCommitment() + if newStateCommitment != nil { + s.stateCommitment = newStateCommitment + } + if err := s.migrationManager.Close(); err != nil { + return fmt.Errorf("failed to close migration manager: %w", err) + } + s.logger.Info("migration completed", "version", s.lastCommitInfo.Version) + } else { + // queue the next changeset to the migration manager + s.chChangeset <- &migration.VersionedChangeset{Version: s.lastCommitInfo.Version + 1, Changeset: cs} + } + } + return nil +} + +func (s *Store) Prune(version uint64) error { + return s.pruningManager.Prune(version) +} diff --git a/store/v2/root/store_mock_test.go b/store/v2/root/store_mock_test.go new file mode 100644 index 000000000000..4b43d52f7f7e --- /dev/null +++ b/store/v2/root/store_mock_test.go @@ -0,0 +1,120 @@ +package root + +import ( + "errors" + "testing" + + "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" + + corestore "cosmossdk.io/core/store" + coretesting "cosmossdk.io/core/testing" + "cosmossdk.io/store/v2" + "cosmossdk.io/store/v2/metrics" + "cosmossdk.io/store/v2/mock" + "cosmossdk.io/store/v2/pruning" +) + +func newTestRootStore(ss store.VersionedWriter, sc store.Committer) *Store { + noopLog := coretesting.NewNopLogger() + pm := pruning.NewManager(sc.(store.Pruner), ss.(store.Pruner), nil, nil) + return &Store{ + logger: noopLog, + telemetry: metrics.Metrics{}, + stateStorage: ss, + stateCommitment: sc, + pruningManager: pm, + isMigrating: false, + } +} + +func TestGetLatestState(t *testing.T) { + ctrl := gomock.NewController(t) + ss := mock.NewMockStateStorage(ctrl) + sc := mock.NewMockStateCommitter(ctrl) + rs := newTestRootStore(ss, sc) + + // Get the latest version + sc.EXPECT().GetLatestVersion().Return(uint64(0), errors.New("error")) + _, err := rs.GetLatestVersion() + require.Error(t, err) + sc.EXPECT().GetLatestVersion().Return(uint64(1), nil) + v, err := rs.GetLatestVersion() + require.NoError(t, err) + require.Equal(t, uint64(1), v) +} + +func TestQuery(t *testing.T) { + ctrl := gomock.NewController(t) + ss := mock.NewMockStateStorage(ctrl) + sc := mock.NewMockStateCommitter(ctrl) + rs := newTestRootStore(ss, sc) + + // Query without Proof + ss.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, errors.New("error")) + _, err := rs.Query(nil, 0, nil, false) + require.Error(t, err) + ss.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil) + sc.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, errors.New("error")) + _, err = rs.Query(nil, 0, nil, false) + require.Error(t, err) + ss.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil) + sc.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Return([]byte("value"), nil) + v, err := rs.Query(nil, 0, nil, false) + require.NoError(t, err) + require.Equal(t, []byte("value"), v.Value) + ss.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Return([]byte("value"), nil) + v, err = rs.Query(nil, 0, nil, false) + require.NoError(t, err) + require.Equal(t, []byte("value"), v.Value) + + // Query with Proof + ss.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Return([]byte("value"), nil) + sc.EXPECT().GetProof(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, errors.New("error")) + v, err = rs.Query(nil, 0, nil, true) + require.Error(t, err) + + // Query with Migration + rs.isMigrating = true + sc.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, errors.New("error")) + _, err = rs.Query(nil, 0, nil, false) + require.Error(t, err) +} + +func TestLoadVersion(t *testing.T) { + ctrl := gomock.NewController(t) + ss := mock.NewMockStateStorage(ctrl) + sc := mock.NewMockStateCommitter(ctrl) + rs := newTestRootStore(ss, sc) + + // LoadLatestVersion + sc.EXPECT().GetLatestVersion().Return(uint64(0), errors.New("error")) + err := rs.LoadLatestVersion() + require.Error(t, err) + sc.EXPECT().GetLatestVersion().Return(uint64(1), nil) + sc.EXPECT().LoadVersion(uint64(1)).Return(errors.New("error")) + err = rs.LoadLatestVersion() + require.Error(t, err) + + // LoadVersion + sc.EXPECT().LoadVersion(gomock.Any()).Return(nil) + sc.EXPECT().GetCommitInfo(uint64(2)).Return(nil, errors.New("error")) + err = rs.LoadVersion(uint64(2)) + require.Error(t, err) + + // LoadVersionUpgrade + v := &corestore.StoreUpgrades{} + sc.EXPECT().LoadVersionAndUpgrade(uint64(2), v).Return(errors.New("error")) + err = rs.LoadVersionAndUpgrade(uint64(2), v) + require.Error(t, err) + sc.EXPECT().LoadVersionAndUpgrade(uint64(2), v).Return(nil) + sc.EXPECT().GetCommitInfo(uint64(2)).Return(nil, nil) + ss.EXPECT().PruneStoreKeys(gomock.Any(), uint64(2)).Return(errors.New("error")) + err = rs.LoadVersionAndUpgrade(uint64(2), v) + require.Error(t, err) + + // LoadVersionUpgrade with Migration + rs.isMigrating = true + err = rs.LoadVersionAndUpgrade(uint64(2), v) + require.Error(t, err) +} diff --git a/store/v2/root/store_test.go b/store/v2/root/store_test.go new file mode 100644 index 000000000000..10a3a049d3c0 --- /dev/null +++ b/store/v2/root/store_test.go @@ -0,0 +1,789 @@ +package root + +import ( + "crypto/sha256" + "fmt" + "testing" + "time" + + "github.com/stretchr/testify/suite" + + corestore "cosmossdk.io/core/store" + coretesting "cosmossdk.io/core/testing" + "cosmossdk.io/store/v2" + "cosmossdk.io/store/v2/commitment" + "cosmossdk.io/store/v2/commitment/iavl" + dbm "cosmossdk.io/store/v2/db" + "cosmossdk.io/store/v2/proof" + "cosmossdk.io/store/v2/pruning" + "cosmossdk.io/store/v2/storage" + "cosmossdk.io/store/v2/storage/sqlite" +) + +const ( + testStoreKey = "test_store_key" + testStoreKey2 = "test_store_key2" + testStoreKey3 = "test_store_key3" +) + +var testStoreKeys = []string{testStoreKey, testStoreKey2, testStoreKey3} + +var ( + testStoreKeyBytes = []byte(testStoreKey) + testStoreKey2Bytes = []byte(testStoreKey2) + testStoreKey3Bytes = []byte(testStoreKey3) +) + +type RootStoreTestSuite struct { + suite.Suite + + rootStore store.RootStore +} + +func TestStorageTestSuite(t *testing.T) { + suite.Run(t, &RootStoreTestSuite{}) +} + +func (s *RootStoreTestSuite) SetupTest() { + noopLog := coretesting.NewNopLogger() + + sqliteDB, err := sqlite.New(s.T().TempDir()) + s.Require().NoError(err) + ss := storage.NewStorageStore(sqliteDB, noopLog) + + tree := iavl.NewIavlTree(dbm.NewMemDB(), noopLog, iavl.DefaultConfig()) + tree2 := iavl.NewIavlTree(dbm.NewMemDB(), noopLog, iavl.DefaultConfig()) + tree3 := iavl.NewIavlTree(dbm.NewMemDB(), noopLog, iavl.DefaultConfig()) + sc, err := commitment.NewCommitStore(map[string]commitment.Tree{testStoreKey: tree, testStoreKey2: tree2, testStoreKey3: tree3}, nil, dbm.NewMemDB(), noopLog) + s.Require().NoError(err) + + pm := pruning.NewManager(sc, ss, nil, nil) + rs, err := New(dbm.NewMemDB(), noopLog, ss, sc, pm, nil, nil) + s.Require().NoError(err) + + s.rootStore = rs +} + +func (s *RootStoreTestSuite) newStoreWithPruneConfig(config *store.PruningOption) { + noopLog := coretesting.NewNopLogger() + + sqliteDB, err := sqlite.New(s.T().TempDir()) + s.Require().NoError(err) + ss := storage.NewStorageStore(sqliteDB, noopLog) + + mdb := dbm.NewMemDB() + multiTrees := make(map[string]commitment.Tree) + for _, storeKey := range testStoreKeys { + prefixDB := dbm.NewPrefixDB(mdb, []byte(storeKey)) + multiTrees[storeKey] = iavl.NewIavlTree(prefixDB, noopLog, iavl.DefaultConfig()) + } + + sc, err := commitment.NewCommitStore(multiTrees, nil, dbm.NewMemDB(), noopLog) + s.Require().NoError(err) + + pm := pruning.NewManager(sc, ss, config, config) + + rs, err := New(dbm.NewMemDB(), noopLog, ss, sc, pm, nil, nil) + s.Require().NoError(err) + + s.rootStore = rs +} + +func (s *RootStoreTestSuite) newStoreWithBackendMount(ss store.VersionedWriter, sc store.Committer, pm *pruning.Manager) { + noopLog := coretesting.NewNopLogger() + + rs, err := New(dbm.NewMemDB(), noopLog, ss, sc, pm, nil, nil) + s.Require().NoError(err) + + s.rootStore = rs +} + +func (s *RootStoreTestSuite) TearDownTest() { + err := s.rootStore.Close() + s.Require().NoError(err) +} + +func (s *RootStoreTestSuite) TestGetStateCommitment() { + s.Require().Equal(s.rootStore.GetStateCommitment(), s.rootStore.(*Store).stateCommitment) +} + +func (s *RootStoreTestSuite) TestGetStateStorage() { + s.Require().Equal(s.rootStore.GetStateStorage(), s.rootStore.(*Store).stateStorage) +} + +func (s *RootStoreTestSuite) TestSetInitialVersion() { + initialVersion := uint64(5) + s.Require().NoError(s.rootStore.SetInitialVersion(initialVersion)) + + // perform an initial, empty commit + cs := corestore.NewChangeset(initialVersion) + cs.Add(testStoreKeyBytes, []byte("foo"), []byte("bar"), false) + _, err := s.rootStore.Commit(corestore.NewChangeset(initialVersion)) + s.Require().NoError(err) + + // check the latest version + lVersion, err := s.rootStore.GetLatestVersion() + s.Require().NoError(err) + s.Require().Equal(initialVersion, lVersion) + + // set the initial version again + rInitialVersion := uint64(100) + s.Require().NoError(s.rootStore.SetInitialVersion(rInitialVersion)) + + // TODO fix version munging here + // perform the commit + cs = corestore.NewChangeset(initialVersion + 1) + cs.Add(testStoreKey2Bytes, []byte("foo"), []byte("bar"), false) + _, err = s.rootStore.Commit(cs) + s.Require().NoError(err) + lVersion, err = s.rootStore.GetLatestVersion() + s.Require().NoError(err) + // SetInitialVersion only works once + s.Require().NotEqual(rInitialVersion, lVersion) + s.Require().Equal(initialVersion+1, lVersion) +} + +func (s *RootStoreTestSuite) TestQuery() { + _, err := s.rootStore.Query([]byte{}, 1, []byte("foo"), true) + s.Require().Error(err) + + // write and commit a changeset + cs := corestore.NewChangeset(1) + cs.Add(testStoreKeyBytes, []byte("foo"), []byte("bar"), false) + + commitHash, err := s.rootStore.Commit(cs) + s.Require().NoError(err) + s.Require().NotNil(commitHash) + + // ensure the proof is non-nil for the corresponding version + result, err := s.rootStore.Query([]byte(testStoreKey), 1, []byte("foo"), true) + s.Require().NoError(err) + s.Require().NotNil(result.ProofOps) + s.Require().Equal([]byte("foo"), result.ProofOps[0].Key) +} + +func (s *RootStoreTestSuite) TestGetFallback() { + sc := s.rootStore.GetStateCommitment() + + // create a changeset and commit it to SC ONLY + cs := corestore.NewChangeset(1) + cs.Add(testStoreKeyBytes, []byte("foo"), []byte("bar"), false) + + err := sc.WriteChangeset(cs) + s.Require().NoError(err) + + _, err = sc.Commit(cs.Version) + s.Require().NoError(err) + + // ensure we can query for the key, which should fallback to SC + qResult, err := s.rootStore.Query(testStoreKeyBytes, 1, []byte("foo"), false) + s.Require().NoError(err) + s.Require().Equal([]byte("bar"), qResult.Value) + + // non-existent key + qResult, err = s.rootStore.Query(testStoreKeyBytes, 1, []byte("non_existent_key"), false) + s.Require().NoError(err) + s.Require().Nil(qResult.Value) +} + +func (s *RootStoreTestSuite) TestQueryProof() { + cs := corestore.NewChangeset(1) + // testStoreKey + cs.Add(testStoreKeyBytes, []byte("key1"), []byte("value1"), false) + cs.Add(testStoreKeyBytes, []byte("key2"), []byte("value2"), false) + // testStoreKey2 + cs.Add(testStoreKey2Bytes, []byte("key3"), []byte("value3"), false) + // testStoreKey3 + cs.Add(testStoreKey3Bytes, []byte("key4"), []byte("value4"), false) + + // commit + _, err := s.rootStore.Commit(cs) + s.Require().NoError(err) + + // query proof for testStoreKey + result, err := s.rootStore.Query(testStoreKeyBytes, 1, []byte("key1"), true) + s.Require().NoError(err) + s.Require().NotNil(result.ProofOps) + cInfo, err := s.rootStore.GetStateCommitment().GetCommitInfo(1) + s.Require().NoError(err) + storeHash := cInfo.GetStoreCommitID(testStoreKeyBytes).Hash + treeRoots, err := result.ProofOps[0].Run([][]byte{[]byte("value1")}) + s.Require().NoError(err) + s.Require().Equal(treeRoots[0], storeHash) + expRoots, err := result.ProofOps[1].Run([][]byte{storeHash}) + s.Require().NoError(err) + s.Require().Equal(expRoots[0], cInfo.Hash()) +} + +func (s *RootStoreTestSuite) TestLoadVersion() { + // write and commit a few changesets + for v := uint64(1); v <= 5; v++ { + val := fmt.Sprintf("val%03d", v) // val001, val002, ..., val005 + + cs := corestore.NewChangeset(v) + cs.Add(testStoreKeyBytes, []byte("key"), []byte(val), false) + + commitHash, err := s.rootStore.Commit(cs) + s.Require().NoError(err) + s.Require().NotNil(commitHash) + } + + // ensure the latest version is correct + latest, err := s.rootStore.GetLatestVersion() + s.Require().NoError(err) + s.Require().Equal(uint64(5), latest) + + // attempt to load a non-existent version + err = s.rootStore.LoadVersion(6) + s.Require().Error(err) + + // attempt to load a previously committed version + err = s.rootStore.LoadVersion(3) + s.Require().NoError(err) + + // ensure the latest version is correct + latest, err = s.rootStore.GetLatestVersion() + s.Require().NoError(err) + s.Require().Equal(uint64(3), latest) + + // query state and ensure values returned are based on the loaded version + _, ro, err := s.rootStore.StateLatest() + s.Require().NoError(err) + + reader, err := ro.GetReader(testStoreKeyBytes) + s.Require().NoError(err) + val, err := reader.Get([]byte("key")) + s.Require().NoError(err) + s.Require().Equal([]byte("val003"), val) + + // attempt to write and commit a few changesets + for v := 4; v <= 5; v++ { + val := fmt.Sprintf("overwritten_val%03d", v) // overwritten_val004, overwritten_val005 + + cs := corestore.NewChangeset(uint64(v)) + cs.Add(testStoreKeyBytes, []byte("key"), []byte(val), false) + + commitHash, err := s.rootStore.Commit(cs) + s.Require().NoError(err) + s.Require().NotNil(commitHash) + } + + // ensure the latest version is correct + latest, err = s.rootStore.GetLatestVersion() + s.Require().NoError(err) + s.Require().Equal(uint64(5), latest) + + // query state and ensure values returned are based on the loaded version + _, ro, err = s.rootStore.StateLatest() + s.Require().NoError(err) + + reader, err = ro.GetReader(testStoreKeyBytes) + s.Require().NoError(err) + val, err = reader.Get([]byte("key")) + s.Require().NoError(err) + s.Require().Equal([]byte("overwritten_val005"), val) +} + +func (s *RootStoreTestSuite) TestCommit() { + lv, err := s.rootStore.GetLatestVersion() + s.Require().NoError(err) + s.Require().Zero(lv) + + // perform changes + cs := corestore.NewChangeset(1) + for i := 0; i < 100; i++ { + key := fmt.Sprintf("key%03d", i) // key000, key001, ..., key099 + val := fmt.Sprintf("val%03d", i) // val000, val001, ..., val099 + + cs.Add(testStoreKeyBytes, []byte(key), []byte(val), false) + } + + cHash, err := s.rootStore.Commit(cs) + s.Require().NoError(err) + s.Require().NotNil(cHash) + + // ensure latest version is updated + lv, err = s.rootStore.GetLatestVersion() + s.Require().NoError(err) + s.Require().Equal(uint64(1), lv) + + // perform reads on the updated root store + _, ro, err := s.rootStore.StateLatest() + s.Require().NoError(err) + + for i := 0; i < 100; i++ { + key := fmt.Sprintf("key%03d", i) // key000, key001, ..., key099 + val := fmt.Sprintf("val%03d", i) // val000, val001, ..., val099 + + reader, err := ro.GetReader(testStoreKeyBytes) + s.Require().NoError(err) + result, err := reader.Get([]byte(key)) + s.Require().NoError(err) + + s.Require().Equal([]byte(val), result) + } +} + +func (s *RootStoreTestSuite) TestStateAt() { + // write keys over multiple versions + for v := uint64(1); v <= 5; v++ { + // perform changes + cs := corestore.NewChangeset(v) + for i := 0; i < 100; i++ { + key := fmt.Sprintf("key%03d", i) // key000, key001, ..., key099 + val := fmt.Sprintf("val%03d_%03d", i, v) // val000_1, val001_1, ..., val099_1 + + cs.Add(testStoreKeyBytes, []byte(key), []byte(val), false) + } + + // execute Commit + cHash, err := s.rootStore.Commit(cs) + s.Require().NoError(err) + s.Require().NotNil(cHash) + } + + lv, err := s.rootStore.GetLatestVersion() + s.Require().NoError(err) + s.Require().Equal(uint64(5), lv) + + // ensure we can read state correctly at each version + for v := uint64(1); v <= 5; v++ { + ro, err := s.rootStore.StateAt(v) + s.Require().NoError(err) + + for i := 0; i < 100; i++ { + key := fmt.Sprintf("key%03d", i) // key000, key001, ..., key099 + val := fmt.Sprintf("val%03d_%03d", i, v) // val000_1, val001_1, ..., val099_1 + + reader, err := ro.GetReader(testStoreKeyBytes) + s.Require().NoError(err) + isExist, err := reader.Has([]byte(key)) + s.Require().NoError(err) + s.Require().True(isExist) + result, err := reader.Get([]byte(key)) + s.Require().NoError(err) + s.Require().Equal([]byte(val), result) + } + + // non-existent key + reader, err := ro.GetReader(testStoreKey2Bytes) + s.Require().NoError(err) + isExist, err := reader.Has([]byte("key")) + s.Require().NoError(err) + s.Require().False(isExist) + v, err := reader.Get([]byte("key")) + s.Require().NoError(err) + s.Require().Nil(v) + } +} + +func (s *RootStoreTestSuite) TestPrune() { + // perform changes + cs := corestore.NewChangeset(1) + for i := 0; i < 10; i++ { + key := fmt.Sprintf("key%03d", i) // key000, key001, ..., key099 + val := fmt.Sprintf("val%03d", i) // val000, val001, ..., val099 + + cs.Add(testStoreKeyBytes, []byte(key), []byte(val), false) + } + + testCases := []struct { + name string + numVersions int64 + po store.PruningOption + deleted []uint64 + saved []uint64 + }{ + {"prune nothing", 10, store.PruningOption{ + KeepRecent: 0, + Interval: 0, + }, nil, []uint64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}}, + {"prune everything", 12, store.PruningOption{ + KeepRecent: 1, + Interval: 10, + }, []uint64{1, 2, 3, 4, 5, 6, 7, 8}, []uint64{9, 10, 11, 12}}, + {"prune some; no batch", 10, store.PruningOption{ + KeepRecent: 2, + Interval: 1, + }, []uint64{1, 2, 3, 4, 6, 5, 7}, []uint64{8, 9, 10}}, + {"prune some; small batch", 10, store.PruningOption{ + KeepRecent: 2, + Interval: 3, + }, []uint64{1, 2, 3, 4, 5, 6}, []uint64{7, 8, 9, 10}}, + {"prune some; large batch", 10, store.PruningOption{ + KeepRecent: 2, + Interval: 11, + }, nil, []uint64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}}, + } + + for _, tc := range testCases { + + s.newStoreWithPruneConfig(&tc.po) + + // write keys over multiple versions + for i := int64(0); i < tc.numVersions; i++ { + // execute Commit + cs.Version = uint64(i + 1) + cHash, err := s.rootStore.Commit(cs) + s.Require().NoError(err) + s.Require().NotNil(cHash) + } + + for _, v := range tc.saved { + ro, err := s.rootStore.StateAt(v) + s.Require().NoError(err, "expected no error when loading height %d at test %s", v, tc.name) + + for i := 0; i < 10; i++ { + key := fmt.Sprintf("key%03d", i) // key000, key001, ..., key099 + val := fmt.Sprintf("val%03d", i) // val000, val001, ..., val099 + + reader, err := ro.GetReader(testStoreKeyBytes) + s.Require().NoError(err) + result, err := reader.Get([]byte(key)) + s.Require().NoError(err) + s.Require().Equal([]byte(val), result, "value should be equal for test: %s", tc.name) + } + } + + for _, v := range tc.deleted { + var err error + checkErr := func() bool { + if _, err = s.rootStore.StateAt(v); err != nil { + return true + } + return false + } + // wait for async pruning process to finish + s.Require().Eventually(checkErr, 2*time.Second, 100*time.Millisecond) + s.Require().Error(err, "expected error when loading height %d at test %s", v, tc.name) + } + } +} + +func (s *RootStoreTestSuite) TestMultiStore_Pruning_SameHeightsTwice() { + // perform changes + cs := corestore.NewChangeset(1) + cs.Add(testStoreKeyBytes, []byte("key"), []byte("val"), false) + + const ( + numVersions uint64 = 10 + keepRecent uint64 = 1 + interval uint64 = 10 + ) + + s.newStoreWithPruneConfig(&store.PruningOption{ + KeepRecent: keepRecent, + Interval: interval, + }) + s.Require().NoError(s.rootStore.LoadLatestVersion()) + + for i := uint64(0); i < numVersions; i++ { + // execute Commit + cs.Version = i + 1 + cHash, err := s.rootStore.Commit(cs) + s.Require().NoError(err) + s.Require().NotNil(cHash) + } + + latestVer, err := s.rootStore.GetLatestVersion() + s.Require().NoError(err) + s.Require().Equal(numVersions, latestVer) + + for v := uint64(1); v < numVersions-keepRecent; v++ { + var err error + checkErr := func() bool { + if _, err = s.rootStore.StateAt(v); err != nil { + return true + } + return false + } + // wait for async pruning process to finish + s.Require().Eventually(checkErr, 2*time.Second, 100*time.Millisecond, "expected no error when loading height: %d", v) + } + + for v := (numVersions - keepRecent); v < numVersions; v++ { + _, err := s.rootStore.StateAt(v) + s.Require().NoError(err, "expected no error when loading height: %d", v) + } + + // Get latest + err = s.rootStore.LoadVersion(numVersions) + s.Require().NoError(err) + + // Test pruning the same heights again + cs.Version++ + _, err = s.rootStore.Commit(cs) + s.Require().NoError(err) + + // Ensure that can commit one more height with no panic + cs.Version++ + _, err = s.rootStore.Commit(cs) + s.Require().NoError(err) +} + +func (s *RootStoreTestSuite) TestMultiStore_PruningRestart() { + // perform changes + cs := corestore.NewChangeset(1) + cs.Add(testStoreKeyBytes, []byte("key"), []byte("val"), false) + + pruneOpt := &store.PruningOption{ + KeepRecent: 2, + Interval: 11, + } + + noopLog := coretesting.NewNopLogger() + + mdb1 := dbm.NewMemDB() + mdb2 := dbm.NewMemDB() + sqliteDB, err := sqlite.New(s.T().TempDir()) + s.Require().NoError(err) + ss := storage.NewStorageStore(sqliteDB, noopLog) + + tree := iavl.NewIavlTree(mdb1, noopLog, iavl.DefaultConfig()) + sc, err := commitment.NewCommitStore(map[string]commitment.Tree{testStoreKey: tree}, nil, mdb2, noopLog) + s.Require().NoError(err) + + pm := pruning.NewManager(sc, ss, pruneOpt, pruneOpt) + + s.newStoreWithBackendMount(ss, sc, pm) + s.Require().NoError(s.rootStore.LoadLatestVersion()) + + // Commit enough to build up heights to prune, where on the next block we should + // batch delete. + for i := uint64(1); i <= 10; i++ { + // execute Commit + cs.Version = i + cHash, err := s.rootStore.Commit(cs) + s.Require().NoError(err) + s.Require().NotNil(cHash) + } + + latestVer, err := s.rootStore.GetLatestVersion() + s.Require().NoError(err) + + ok, actualHeightToPrune := pruneOpt.ShouldPrune(latestVer) + s.Require().False(ok) + s.Require().Equal(uint64(0), actualHeightToPrune) + + // "restart" + sqliteDB, err = sqlite.New(s.T().TempDir()) + s.Require().NoError(err) + ss = storage.NewStorageStore(sqliteDB, noopLog) + + tree = iavl.NewIavlTree(mdb1, noopLog, iavl.DefaultConfig()) + sc, err = commitment.NewCommitStore(map[string]commitment.Tree{testStoreKey: tree}, nil, mdb2, noopLog) + s.Require().NoError(err) + + pm = pruning.NewManager(sc, ss, pruneOpt, pruneOpt) + + s.newStoreWithBackendMount(ss, sc, pm) + err = s.rootStore.LoadLatestVersion() + s.Require().NoError(err) + + latestVer, err = s.rootStore.GetLatestVersion() + s.Require().NoError(err) + + ok, actualHeightToPrune = pruneOpt.ShouldPrune(latestVer) + s.Require().False(ok) + s.Require().Equal(uint64(0), actualHeightToPrune) + + // commit one more block and ensure the heights have been pruned + // execute Commit + cs.Version++ + cHash, err := s.rootStore.Commit(cs) + s.Require().NoError(err) + s.Require().NotNil(cHash) + + latestVer, err = s.rootStore.GetLatestVersion() + s.Require().NoError(err) + + ok, actualHeightToPrune = pruneOpt.ShouldPrune(latestVer) + s.Require().True(ok) + s.Require().Equal(uint64(8), actualHeightToPrune) + + for v := uint64(1); v <= actualHeightToPrune; v++ { + checkErr := func() bool { + if _, err = s.rootStore.StateAt(v); err != nil { + return true + } + return false + } + // wait for async pruning process to finish + s.Require().Eventually(checkErr, 10*time.Second, 1*time.Second, "expected error when loading height: %d", v) + } +} + +func (s *RootStoreTestSuite) TestMultiStoreRestart() { + noopLog := coretesting.NewNopLogger() + + sqliteDB, err := sqlite.New(s.T().TempDir()) + s.Require().NoError(err) + + ss := storage.NewStorageStore(sqliteDB, noopLog) + + mdb1 := dbm.NewMemDB() + mdb2 := dbm.NewMemDB() + multiTrees := make(map[string]commitment.Tree) + for _, storeKey := range testStoreKeys { + prefixDB := dbm.NewPrefixDB(mdb1, []byte(storeKey)) + multiTrees[storeKey] = iavl.NewIavlTree(prefixDB, noopLog, iavl.DefaultConfig()) + } + + sc, err := commitment.NewCommitStore(multiTrees, nil, mdb2, noopLog) + s.Require().NoError(err) + + pm := pruning.NewManager(sc, ss, nil, nil) + + s.newStoreWithBackendMount(ss, sc, pm) + s.Require().NoError(s.rootStore.LoadLatestVersion()) + + // perform changes + for i := 1; i < 3; i++ { + cs := corestore.NewChangeset(uint64(i)) + key := fmt.Sprintf("key%03d", i) // key000, key001, ..., key099 + val := fmt.Sprintf("val%03d_%03d", i, 1) // val000_1, val001_1, ..., val099_1 + + cs.Add(testStoreKeyBytes, []byte(key), []byte(val), false) + + key = fmt.Sprintf("key%03d", i) // key000, key001, ..., key099 + val = fmt.Sprintf("val%03d_%03d", i, 2) // val000_1, val001_1, ..., val099_1 + + cs.Add(testStoreKey2Bytes, []byte(key), []byte(val), false) + + key = fmt.Sprintf("key%03d", i) // key000, key001, ..., key099 + val = fmt.Sprintf("val%03d_%03d", i, 3) // val000_1, val001_1, ..., val099_1 + + cs.Add(testStoreKey3Bytes, []byte(key), []byte(val), false) + + // execute Commit + cHash, err := s.rootStore.Commit(cs) + s.Require().NoError(err) + s.Require().NotNil(cHash) + + latestVer, err := s.rootStore.GetLatestVersion() + s.Require().NoError(err) + s.Require().Equal(uint64(i), latestVer) + } + + // more changes + cs1 := corestore.NewChangeset(3) + key := fmt.Sprintf("key%03d", 3) // key000, key001, ..., key099 + val := fmt.Sprintf("val%03d_%03d", 3, 1) // val000_1, val001_1, ..., val099_1 + + cs1.Add(testStoreKeyBytes, []byte(key), []byte(val), false) + + key = fmt.Sprintf("key%03d", 3) // key000, key001, ..., key099 + val = fmt.Sprintf("val%03d_%03d", 3, 2) // val000_1, val001_1, ..., val099_1 + + cs1.Add(testStoreKey2Bytes, []byte(key), []byte(val), false) + + // execute Commit + cHash, err := s.rootStore.Commit(cs1) + s.Require().NoError(err) + s.Require().NotNil(cHash) + + latestVer, err := s.rootStore.GetLatestVersion() + s.Require().NoError(err) + s.Require().Equal(uint64(3), latestVer) + + cs2 := corestore.NewChangeset(4) + key = fmt.Sprintf("key%03d", 4) // key000, key001, ..., key099 + val = fmt.Sprintf("val%03d_%03d", 4, 3) // val000_1, val001_1, ..., val099_1 + + cs2.Add(testStoreKey3Bytes, []byte(key), []byte(val), false) + + // execute Commit + cHash, err = s.rootStore.Commit(cs2) + s.Require().NoError(err) + s.Require().NotNil(cHash) + + latestVer, err = s.rootStore.GetLatestVersion() + s.Require().NoError(err) + s.Require().Equal(uint64(4), latestVer) + + _, ro1, err := s.rootStore.StateLatest() + s.Require().Nil(err) + reader1, err := ro1.GetReader(testStoreKeyBytes) + s.Require().NoError(err) + result1, err := reader1.Get([]byte(fmt.Sprintf("key%03d", 3))) + s.Require().NoError(err) + s.Require().Equal([]byte(fmt.Sprintf("val%03d_%03d", 3, 1)), result1, "value should be equal") + + // "restart" + multiTrees = make(map[string]commitment.Tree) + for _, storeKey := range testStoreKeys { + prefixDB := dbm.NewPrefixDB(mdb1, []byte(storeKey)) + multiTrees[storeKey] = iavl.NewIavlTree(prefixDB, noopLog, iavl.DefaultConfig()) + } + + sc, err = commitment.NewCommitStore(multiTrees, nil, mdb2, noopLog) + s.Require().NoError(err) + + pm = pruning.NewManager(sc, ss, nil, nil) + + s.newStoreWithBackendMount(ss, sc, pm) + err = s.rootStore.LoadLatestVersion() + s.Require().Nil(err) + + latestVer, ro, err := s.rootStore.StateLatest() + s.Require().Nil(err) + s.Require().Equal(uint64(4), latestVer) + reader, err := ro.GetReader(testStoreKeyBytes) + s.Require().NoError(err) + result, err := reader.Get([]byte(fmt.Sprintf("key%03d", 3))) + s.Require().NoError(err) + s.Require().Equal([]byte(fmt.Sprintf("val%03d_%03d", 3, 1)), result, "value should be equal") + + reader, err = ro.GetReader(testStoreKey2Bytes) + s.Require().NoError(err) + result, err = reader.Get([]byte(fmt.Sprintf("key%03d", 2))) + s.Require().NoError(err) + s.Require().Equal([]byte(fmt.Sprintf("val%03d_%03d", 2, 2)), result, "value should be equal") + + reader, err = ro.GetReader(testStoreKey3Bytes) + s.Require().NoError(err) + result, err = reader.Get([]byte(fmt.Sprintf("key%03d", 4))) + s.Require().NoError(err) + s.Require().Equal([]byte(fmt.Sprintf("val%03d_%03d", 4, 3)), result, "value should be equal") +} + +func (s *RootStoreTestSuite) TestHashStableWithEmptyCommitAndRestart() { + err := s.rootStore.LoadLatestVersion() + s.Require().NoError(err) + + emptyHash := sha256.Sum256([]byte{}) + appHash := emptyHash[:] + commitID := proof.CommitID{Hash: appHash} + lastCommitID, err := s.rootStore.LastCommitID() + s.Require().Nil(err) + + // the hash of a store with no commits is the root hash of a tree with empty hashes as leaves. + // it should not be equal an empty hash. + s.Require().NotEqual(commitID, lastCommitID) + + cs := corestore.NewChangeset(1) + cs.Add(testStoreKeyBytes, []byte("key"), []byte("val"), false) + + cHash, err := s.rootStore.Commit(cs) + s.Require().Nil(err) + s.Require().NotNil(cHash) + latestVersion, err := s.rootStore.GetLatestVersion() + hash := cHash + s.Require().Nil(err) + s.Require().Equal(uint64(1), latestVersion) + + // make an empty commit, it should update version, but not affect hash + cHash, err = s.rootStore.Commit(corestore.NewChangeset(2)) + s.Require().Nil(err) + s.Require().NotNil(cHash) + latestVersion, err = s.rootStore.GetLatestVersion() + s.Require().Nil(err) + s.Require().Equal(uint64(2), latestVersion) + s.Require().Equal(hash, cHash) + + // reload the store + s.Require().NoError(s.rootStore.LoadLatestVersion()) + lastCommitID, err = s.rootStore.LastCommitID() + s.Require().NoError(err) + s.Require().Equal(lastCommitID.Hash, hash) +} diff --git a/store/v2/root/upgrade_test.go b/store/v2/root/upgrade_test.go new file mode 100644 index 000000000000..400ddb2c4d65 --- /dev/null +++ b/store/v2/root/upgrade_test.go @@ -0,0 +1,157 @@ +package root + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/suite" + + corestore "cosmossdk.io/core/store" + coretesting "cosmossdk.io/core/testing" + "cosmossdk.io/log" + "cosmossdk.io/store/v2" + "cosmossdk.io/store/v2/commitment" + "cosmossdk.io/store/v2/commitment/iavl" + dbm "cosmossdk.io/store/v2/db" + "cosmossdk.io/store/v2/pruning" + "cosmossdk.io/store/v2/storage" + "cosmossdk.io/store/v2/storage/sqlite" +) + +type UpgradeStoreTestSuite struct { + suite.Suite + + commitDB corestore.KVStoreWithBatch + rootStore store.RootStore +} + +func TestUpgradeStoreTestSuite(t *testing.T) { + suite.Run(t, &UpgradeStoreTestSuite{}) +} + +func (s *UpgradeStoreTestSuite) SetupTest() { + testLog := log.NewTestLogger(s.T()) + nopLog := coretesting.NewNopLogger() + + s.commitDB = dbm.NewMemDB() + multiTrees := make(map[string]commitment.Tree) + newTreeFn := func(storeKey string) (commitment.Tree, error) { + prefixDB := dbm.NewPrefixDB(s.commitDB, []byte(storeKey)) + return iavl.NewIavlTree(prefixDB, nopLog, iavl.DefaultConfig()), nil + } + for _, storeKey := range storeKeys { + multiTrees[storeKey], _ = newTreeFn(storeKey) + } + + // create storage and commitment stores + sqliteDB, err := sqlite.New(s.T().TempDir()) + s.Require().NoError(err) + ss := storage.NewStorageStore(sqliteDB, testLog) + sc, err := commitment.NewCommitStore(multiTrees, nil, s.commitDB, testLog) + s.Require().NoError(err) + pm := pruning.NewManager(sc, ss, nil, nil) + s.rootStore, err = New(s.commitDB, testLog, ss, sc, pm, nil, nil) + s.Require().NoError(err) + + // commit changeset + toVersion := uint64(20) + keyCount := 10 + for version := uint64(1); version <= toVersion; version++ { + cs := corestore.NewChangeset(version) + for _, storeKey := range storeKeys { + for i := 0; i < keyCount; i++ { + cs.Add([]byte(storeKey), []byte(fmt.Sprintf("key-%d-%d", version, i)), []byte(fmt.Sprintf("value-%d-%d", version, i)), false) + } + } + _, err = s.rootStore.Commit(cs) + s.Require().NoError(err) + } +} + +func (s *UpgradeStoreTestSuite) loadWithUpgrades(upgrades *corestore.StoreUpgrades) { + testLog := log.NewTestLogger(s.T()) + nopLog := coretesting.NewNopLogger() + + // create a new commitment store + multiTrees := make(map[string]commitment.Tree) + oldTrees := make(map[string]commitment.Tree) + newTreeFn := func(storeKey string) (commitment.Tree, error) { + prefixDB := dbm.NewPrefixDB(s.commitDB, []byte(storeKey)) + return iavl.NewIavlTree(prefixDB, nopLog, iavl.DefaultConfig()), nil + } + for _, storeKey := range storeKeys { + multiTrees[storeKey], _ = newTreeFn(storeKey) + } + for _, added := range upgrades.Added { + multiTrees[added], _ = newTreeFn(added) + } + for _, deleted := range upgrades.Deleted { + oldTrees[deleted], _ = newTreeFn(deleted) + } + + sc, err := commitment.NewCommitStore(multiTrees, oldTrees, s.commitDB, testLog) + s.Require().NoError(err) + pm := pruning.NewManager(sc, s.rootStore.GetStateStorage().(store.Pruner), nil, nil) + s.rootStore, err = New(s.commitDB, testLog, s.rootStore.GetStateStorage(), sc, pm, nil, nil) + s.Require().NoError(err) +} + +func (s *UpgradeStoreTestSuite) TestLoadVersionAndUpgrade() { + // upgrade store keys + upgrades := &corestore.StoreUpgrades{ + Added: []string{"newStore1", "newStore2"}, + Deleted: []string{"store3"}, + } + s.loadWithUpgrades(upgrades) + + // load the store with the upgrades + v, err := s.rootStore.GetLatestVersion() + s.Require().NoError(err) + err = s.rootStore.(store.UpgradeableStore).LoadVersionAndUpgrade(v, upgrades) + s.Require().NoError(err) + + keyCount := 10 + // check old store keys are queryable + oldStoreKeys := []string{"store1", "store3"} + for _, storeKey := range oldStoreKeys { + for version := uint64(1); version <= v; version++ { + for i := 0; i < keyCount; i++ { + proof, err := s.rootStore.Query([]byte(storeKey), version, []byte(fmt.Sprintf("key-%d-%d", version, i)), true) + s.Require().NoError(err) + s.Require().NotNil(proof) + } + } + } + + // commit changeset + newStoreKeys := []string{"newStore1", "newStore2"} + toVersion := uint64(40) + for version := v + 1; version <= toVersion; version++ { + cs := corestore.NewChangeset(version) + for _, storeKey := range newStoreKeys { + for i := 0; i < keyCount; i++ { + cs.Add([]byte(storeKey), []byte(fmt.Sprintf("key-%d-%d", version, i)), []byte(fmt.Sprintf("value-%d-%d", version, i)), false) + } + } + _, err = s.rootStore.Commit(cs) + s.Require().NoError(err) + } + + // check new store keys are queryable + for _, storeKey := range newStoreKeys { + for version := v + 1; version <= toVersion; version++ { + for i := 0; i < keyCount; i++ { + _, err := s.rootStore.Query([]byte(storeKey), version, []byte(fmt.Sprintf("key-%d-%d", version, i)), true) + s.Require().NoError(err) + } + } + } + + // check the original store key is queryable + for version := uint64(1); version <= toVersion; version++ { + for i := 0; i < keyCount; i++ { + _, err := s.rootStore.Query([]byte("store2"), version, []byte(fmt.Sprintf("key-%d-%d", version, i)), true) + s.Require().NoError(err) + } + } +} diff --git a/store/v2/storage/storage_bench_test.go b/store/v2/storage/storage_bench_test.go new file mode 100644 index 000000000000..2e2030bec6bb --- /dev/null +++ b/store/v2/storage/storage_bench_test.go @@ -0,0 +1,187 @@ +//go:build rocksdb +// +build rocksdb + +package storage_test + +import ( + "bytes" + "fmt" + "math/rand" + "sort" + "testing" + + "github.com/stretchr/testify/require" + + corestore "cosmossdk.io/core/store" + coretesting "cosmossdk.io/core/testing" + "cosmossdk.io/store/v2" + "cosmossdk.io/store/v2/storage" + "cosmossdk.io/store/v2/storage/pebbledb" + "cosmossdk.io/store/v2/storage/rocksdb" + "cosmossdk.io/store/v2/storage/sqlite" +) + +var storeKey1 = []byte("store1") + +var ( + backends = map[string]func(dataDir string) (store.VersionedWriter, error){ + "rocksdb_versiondb_opts": func(dataDir string) (store.VersionedWriter, error) { + db, err := rocksdb.New(dataDir) + return storage.NewStorageStore(db, coretesting.NewNopLogger()), err + }, + "pebbledb_default_opts": func(dataDir string) (store.VersionedWriter, error) { + db, err := pebbledb.New(dataDir) + if err == nil && db != nil { + db.SetSync(false) + } + + return storage.NewStorageStore(db, coretesting.NewNopLogger()), err + }, + "btree_sqlite": func(dataDir string) (store.VersionedWriter, error) { + db, err := sqlite.New(dataDir) + return storage.NewStorageStore(db, coretesting.NewNopLogger()), err + }, + } + rng = rand.New(rand.NewSource(567320)) +) + +func BenchmarkGet(b *testing.B) { + numKeyVals := 1_000_000 + keys := make([][]byte, numKeyVals) + vals := make([][]byte, numKeyVals) + for i := 0; i < numKeyVals; i++ { + key := make([]byte, 128) + val := make([]byte, 128) + + _, err := rng.Read(key) + require.NoError(b, err) + _, err = rng.Read(val) + require.NoError(b, err) + + keys[i] = key + vals[i] = val + } + + for ty, fn := range backends { + db, err := fn(b.TempDir()) + require.NoError(b, err) + defer func() { + _ = db.Close() + }() + + cs := corestore.NewChangesetWithPairs(1, map[string]corestore.KVPairs{string(storeKey1): {}}) + for i := 0; i < numKeyVals; i++ { + cs.AddKVPair(storeKey1, corestore.KVPair{Key: keys[i], Value: vals[i]}) + } + + require.NoError(b, db.ApplyChangeset(cs)) + + b.Run(fmt.Sprintf("backend_%s", ty), func(b *testing.B) { + b.ResetTimer() + + for i := 0; i < b.N; i++ { + b.StopTimer() + key := keys[rng.Intn(len(keys))] + + b.StartTimer() + _, err = db.Get(storeKey1, 1, key) + require.NoError(b, err) + } + }) + } +} + +func BenchmarkApplyChangeset(b *testing.B) { + for ty, fn := range backends { + db, err := fn(b.TempDir()) + require.NoError(b, err) + defer func() { + _ = db.Close() + }() + + b.Run(fmt.Sprintf("backend_%s", ty), func(b *testing.B) { + b.ResetTimer() + + for i := 0; i < b.N; i++ { + b.StopTimer() + + ver := uint64(b.N + 1) + cs := corestore.NewChangesetWithPairs(ver, map[string]corestore.KVPairs{string(storeKey1): {}}) + for j := 0; j < 1000; j++ { + key := make([]byte, 128) + val := make([]byte, 128) + + _, err = rng.Read(key) + require.NoError(b, err) + _, err = rng.Read(val) + require.NoError(b, err) + + cs.AddKVPair(storeKey1, corestore.KVPair{Key: key, Value: val}) + } + + b.StartTimer() + require.NoError(b, db.ApplyChangeset(cs)) + } + }) + } +} + +func BenchmarkIterate(b *testing.B) { + numKeyVals := 1_000_000 + keys := make([][]byte, numKeyVals) + vals := make([][]byte, numKeyVals) + for i := 0; i < numKeyVals; i++ { + key := make([]byte, 128) + val := make([]byte, 128) + + _, err := rng.Read(key) + require.NoError(b, err) + _, err = rng.Read(val) + require.NoError(b, err) + + keys[i] = key + vals[i] = val + + } + + for ty, fn := range backends { + db, err := fn(b.TempDir()) + require.NoError(b, err) + defer func() { + _ = db.Close() + }() + + b.StopTimer() + + cs := corestore.NewChangesetWithPairs(1, map[string]corestore.KVPairs{string(storeKey1): {}}) + for i := 0; i < numKeyVals; i++ { + cs.AddKVPair(storeKey1, corestore.KVPair{Key: keys[i], Value: vals[i]}) + } + + require.NoError(b, db.ApplyChangeset(cs)) + + sort.Slice(keys, func(i, j int) bool { + return bytes.Compare(keys[i], keys[j]) < 0 + }) + + b.Run(fmt.Sprintf("backend_%s", ty), func(b *testing.B) { + b.ResetTimer() + + for i := 0; i < b.N; i++ { + b.StopTimer() + + itr, err := db.Iterator(storeKey1, 1, keys[0], nil) + require.NoError(b, err) + + b.StartTimer() + + for ; itr.Valid(); itr.Next() { + _ = itr.Key() + _ = itr.Value() + } + + require.NoError(b, itr.Error()) + } + }) + } +} diff --git a/store/v2/storage/storage_test_suite.go b/store/v2/storage/storage_test_suite.go new file mode 100644 index 000000000000..4d38efe7931e --- /dev/null +++ b/store/v2/storage/storage_test_suite.go @@ -0,0 +1,1056 @@ +package storage + +import ( + "fmt" + "slices" + "testing" + + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + + corestore "cosmossdk.io/core/store" + "cosmossdk.io/store/v2" +) + +const ( + storeKey1 = "store1" +) + +var storeKey1Bytes = []byte(storeKey1) + +// StorageTestSuite defines a reusable test suite for all storage backends. +type StorageTestSuite struct { + suite.Suite + + NewDB func(dir string) (*StorageStore, error) + EmptyBatchSize int + SkipTests []string +} + +func (s *StorageTestSuite) TestDatabase_Close() { + db, err := s.NewDB(s.T().TempDir()) + s.Require().NoError(err) + s.Require().NoError(db.Close()) + + // close should not be idempotent + s.Require().Panics(func() { _ = db.Close() }) +} + +func (s *StorageTestSuite) TestDatabase_LatestVersion() { + db, err := s.NewDB(s.T().TempDir()) + s.Require().NoError(err) + defer db.Close() + + lv, err := db.GetLatestVersion() + s.Require().NoError(err) + s.Require().Zero(lv) + + for i := uint64(1); i <= 1001; i++ { + err = db.SetLatestVersion(i) + s.Require().NoError(err) + + lv, err = db.GetLatestVersion() + s.Require().NoError(err) + s.Require().Equal(i, lv) + } +} + +func (s *StorageTestSuite) TestDatabase_VersionedKeys() { + db, err := s.NewDB(s.T().TempDir()) + s.Require().NoError(err) + defer db.Close() + + for i := uint64(1); i <= 100; i++ { + s.Require().NoError(db.ApplyChangeset(corestore.NewChangesetWithPairs( + i, + map[string]corestore.KVPairs{ + storeKey1: {{Key: []byte("key"), Value: []byte(fmt.Sprintf("value%03d", i))}}, + }, + ))) + } + + for i := uint64(1); i <= 100; i++ { + bz, err := db.Get(storeKey1Bytes, i, []byte("key")) + s.Require().NoError(err) + s.Require().Equal(fmt.Sprintf("value%03d", i), string(bz)) + } +} + +func (s *StorageTestSuite) TestDatabase_GetVersionedKey() { + db, err := s.NewDB(s.T().TempDir()) + s.Require().NoError(err) + defer db.Close() + + // store a key at version 1 + s.Require().NoError(db.ApplyChangeset(corestore.NewChangesetWithPairs( + 1, + map[string]corestore.KVPairs{ + storeKey1: {{Key: []byte("key"), Value: []byte("value001")}}, + }, + ))) + + // assume chain progresses to version 10 w/o any changes to key + bz, err := db.Get(storeKey1Bytes, 10, []byte("key")) + s.Require().NoError(err) + s.Require().Equal([]byte("value001"), bz) + + ok, err := db.Has(storeKey1Bytes, 10, []byte("key")) + s.Require().NoError(err) + s.Require().True(ok) + + // chain progresses to version 11 with an update to key + s.Require().NoError(db.ApplyChangeset(corestore.NewChangesetWithPairs( + 11, + map[string]corestore.KVPairs{ + storeKey1: {{Key: []byte("key"), Value: []byte("value011")}}, + }, + ))) + + bz, err = db.Get(storeKey1Bytes, 10, []byte("key")) + s.Require().NoError(err) + s.Require().Equal([]byte("value001"), bz) + + ok, err = db.Has(storeKey1Bytes, 10, []byte("key")) + s.Require().NoError(err) + s.Require().True(ok) + + for i := uint64(11); i <= 14; i++ { + bz, err = db.Get(storeKey1Bytes, i, []byte("key")) + s.Require().NoError(err) + s.Require().Equal([]byte("value011"), bz) + + ok, err = db.Has(storeKey1Bytes, i, []byte("key")) + s.Require().NoError(err) + s.Require().True(ok) + } + + // chain progresses to version 15 with a delete to key + s.Require().NoError(db.ApplyChangeset(corestore.NewChangesetWithPairs( + 15, + map[string]corestore.KVPairs{storeKey1: {{Key: []byte("key"), Remove: true}}}, + ))) + + // all queries up to version 14 should return the latest value + for i := uint64(1); i <= 14; i++ { + bz, err = db.Get(storeKey1Bytes, i, []byte("key")) + s.Require().NoError(err) + s.Require().NotNil(bz) + + ok, err = db.Has(storeKey1Bytes, i, []byte("key")) + s.Require().NoError(err) + s.Require().True(ok) + } + + // all queries after version 15 should return nil + for i := uint64(15); i <= 17; i++ { + bz, err = db.Get(storeKey1Bytes, i, []byte("key")) + s.Require().NoError(err) + s.Require().Nil(bz) + + ok, err = db.Has(storeKey1Bytes, i, []byte("key")) + s.Require().NoError(err) + s.Require().False(ok) + } +} + +func (s *StorageTestSuite) TestDatabase_ApplyChangeset() { + db, err := s.NewDB(s.T().TempDir()) + s.Require().NoError(err) + defer db.Close() + + cs := corestore.NewChangesetWithPairs(1, map[string]corestore.KVPairs{storeKey1: {}}) + for i := 0; i < 100; i++ { + cs.AddKVPair(storeKey1Bytes, corestore.KVPair{Key: []byte(fmt.Sprintf("key%03d", i)), Value: []byte("value")}) + } + + for i := 0; i < 100; i++ { + if i%10 == 0 { + cs.AddKVPair(storeKey1Bytes, corestore.KVPair{Key: []byte(fmt.Sprintf("key%03d", i)), Remove: true}) + } + } + + s.Require().NoError(db.ApplyChangeset(cs)) + + lv, err := db.GetLatestVersion() + s.Require().NoError(err) + s.Require().Equal(uint64(1), lv) + + for i := 0; i < 1; i++ { + ok, err := db.Has(storeKey1Bytes, 1, []byte(fmt.Sprintf("key%03d", i))) + s.Require().NoError(err) + + if i%10 == 0 { + s.Require().False(ok) + } else { + s.Require().True(ok) + } + } +} + +func (s *StorageTestSuite) TestDatabase_IteratorEmptyDomain() { + db, err := s.NewDB(s.T().TempDir()) + s.Require().NoError(err) + defer db.Close() + + iter, err := db.Iterator(storeKey1Bytes, 1, []byte{}, []byte{}) + s.Require().Error(err) + s.Require().Nil(iter) +} + +func (s *StorageTestSuite) TestDatabase_IteratorClose() { + db, err := s.NewDB(s.T().TempDir()) + s.Require().NoError(err) + defer db.Close() + + iter, err := db.Iterator(storeKey1Bytes, 1, []byte("key000"), nil) + s.Require().NoError(err) + iter.Close() + + s.Require().False(iter.Valid()) +} + +func (s *StorageTestSuite) TestDatabase_IteratorDomain() { + db, err := s.NewDB(s.T().TempDir()) + s.Require().NoError(err) + defer db.Close() + + testCases := map[string]struct { + start, end []byte + }{ + "start without end domain": { + start: []byte("key010"), + }, + "start and end domain": { + start: []byte("key010"), + end: []byte("key020"), + }, + } + + for name, tc := range testCases { + s.Run(name, func() { + iter, err := db.Iterator(storeKey1Bytes, 1, tc.start, tc.end) + s.Require().NoError(err) + + defer iter.Close() + + start, end := iter.Domain() + s.Require().Equal(tc.start, start) + s.Require().Equal(tc.end, end) + }) + } +} + +func (s *StorageTestSuite) TestDatabase_Iterator() { + db, err := s.NewDB(s.T().TempDir()) + s.Require().NoError(err) + defer db.Close() + + cs := corestore.NewChangesetWithPairs(1, map[string]corestore.KVPairs{storeKey1: {}}) + for i := 0; i < 100; i++ { + key := fmt.Sprintf("key%03d", i) // key000, key001, ..., key099 + val := fmt.Sprintf("val%03d", i) // val000, val001, ..., val099 + + cs.AddKVPair(storeKey1Bytes, corestore.KVPair{Key: []byte(key), Value: []byte(val), Remove: false}) + } + + s.Require().NoError(db.ApplyChangeset(cs)) + + // iterator without an end key over multiple versions + for v := uint64(1); v < 5; v++ { + itr, err := db.Iterator(storeKey1Bytes, v, []byte("key000"), nil) + s.Require().NoError(err) + + var i, count int + for ; itr.Valid(); itr.Next() { + s.Require().Equal([]byte(fmt.Sprintf("key%03d", i)), itr.Key(), string(itr.Key())) + s.Require().Equal([]byte(fmt.Sprintf("val%03d", i)), itr.Value()) + + i++ + count++ + } + s.Require().NoError(itr.Error()) + s.Require().Equal(100, count) + + // seek past domain, which should make the iterator invalid and produce an error + s.Require().False(itr.Valid()) + + err = itr.Close() + s.Require().NoError(err, "Failed to close iterator") + } + + // iterator with a start and end domain over multiple versions + for v := uint64(1); v < 5; v++ { + itr2, err := db.Iterator(storeKey1Bytes, v, []byte("key010"), []byte("key019")) + s.Require().NoError(err) + + i, count := 10, 0 + for ; itr2.Valid(); itr2.Next() { + s.Require().Equal([]byte(fmt.Sprintf("key%03d", i)), itr2.Key()) + s.Require().Equal([]byte(fmt.Sprintf("val%03d", i)), itr2.Value()) + + i++ + count++ + } + s.Require().Equal(9, count) + s.Require().NoError(itr2.Error()) + + // seek past domain, which should make the iterator invalid and produce an error + s.Require().False(itr2.Valid()) + + err = itr2.Close() + if err != nil { + return + } + } + + // start must be <= end + iter3, err := db.Iterator(storeKey1Bytes, 1, []byte("key020"), []byte("key019")) + s.Require().Error(err) + s.Require().Nil(iter3) +} + +func (s *StorageTestSuite) TestDatabase_Iterator_RangedDeletes() { + db, err := s.NewDB(s.T().TempDir()) + s.Require().NoError(err) + defer db.Close() + + s.Require().NoError(db.ApplyChangeset(corestore.NewChangesetWithPairs( + 1, + map[string]corestore.KVPairs{ + storeKey1: { + {Key: []byte("key001"), Value: []byte("value001"), Remove: false}, + {Key: []byte("key002"), Value: []byte("value001"), Remove: false}, + }, + }, + ))) + + s.Require().NoError(db.ApplyChangeset(corestore.NewChangesetWithPairs( + 5, + map[string]corestore.KVPairs{ + storeKey1: {{Key: []byte("key002"), Value: []byte("value002"), Remove: false}}, + }, + ))) + + s.Require().NoError(db.ApplyChangeset(corestore.NewChangesetWithPairs( + 10, + map[string]corestore.KVPairs{ + storeKey1: {{Key: []byte("key002"), Remove: true}}, + }, + ))) + + itr, err := db.Iterator(storeKey1Bytes, 11, []byte("key001"), nil) + s.Require().NoError(err) + + defer itr.Close() + + // there should only be one valid key in the iterator -- key001 + var count int + for ; itr.Valid(); itr.Next() { + s.Require().Equal([]byte("key001"), itr.Key()) + count++ + } + s.Require().Equal(1, count) + s.Require().NoError(itr.Error()) +} + +func (s *StorageTestSuite) TestDatabase_IteratorMultiVersion() { + db, err := s.NewDB(s.T().TempDir()) + s.Require().NoError(err) + defer db.Close() + + // for versions 1-49, set all 10 keys + for v := uint64(1); v < 50; v++ { + cs := corestore.NewChangesetWithPairs(v, map[string]corestore.KVPairs{storeKey1: {}}) + for i := 0; i < 10; i++ { + key := fmt.Sprintf("key%03d", i) + val := fmt.Sprintf("val%03d-%03d", i, v) + + cs.AddKVPair(storeKey1Bytes, corestore.KVPair{Key: []byte(key), Value: []byte(val)}) + } + + s.Require().NoError(db.ApplyChangeset(cs)) + } + + // for versions 50-100, only update even keys + for v := uint64(50); v <= 100; v++ { + cs := corestore.NewChangesetWithPairs(v, map[string]corestore.KVPairs{storeKey1: {}}) + for i := 0; i < 10; i++ { + if i%2 == 0 { + key := fmt.Sprintf("key%03d", i) + val := fmt.Sprintf("val%03d-%03d", i, v) + + cs.AddKVPair(storeKey1Bytes, corestore.KVPair{Key: []byte(key), Value: []byte(val), Remove: false}) + } + } + + s.Require().NoError(db.ApplyChangeset(cs)) + } + + itr, err := db.Iterator(storeKey1Bytes, 69, []byte("key000"), nil) + s.Require().NoError(err) + + defer itr.Close() + + // All keys should be present; All odd keys should have a value that reflects + // version 49, and all even keys should have a value that reflects the desired + // version, 69. + var i, count int + for ; itr.Valid(); itr.Next() { + s.Require().Equal([]byte(fmt.Sprintf("key%03d", i)), itr.Key(), string(itr.Key())) + + if i%2 == 0 { + s.Require().Equal([]byte(fmt.Sprintf("val%03d-%03d", i, 69)), itr.Value()) + } else { + s.Require().Equal([]byte(fmt.Sprintf("val%03d-%03d", i, 49)), itr.Value()) + } + + i = (i + 1) % 10 + count++ + } + + s.Require().NoError(itr.Error()) + s.Require().Equal(10, count) +} + +func (s *StorageTestSuite) TestDatabaseIterator_SkipVersion() { + db, err := s.NewDB(s.T().TempDir()) + s.Require().NoError(err) + + defer db.Close() + + dbApplyChangeset(s.T(), db, 58827506, storeKey1, [][]byte{[]byte("keyC")}, [][]byte{[]byte("value003")}) + dbApplyChangeset(s.T(), db, 58827506, storeKey1, [][]byte{[]byte("keyE")}, [][]byte{[]byte("value000")}) + dbApplyChangeset(s.T(), db, 58827506, storeKey1, [][]byte{[]byte("keyF")}, [][]byte{[]byte("value000")}) + dbApplyChangeset(s.T(), db, 58833605, storeKey1, [][]byte{[]byte("keyC")}, [][]byte{[]byte("value004")}) + dbApplyChangeset(s.T(), db, 58833606, storeKey1, [][]byte{[]byte("keyD")}, [][]byte{[]byte("value006")}) + + itr, err := db.Iterator(storeKey1Bytes, 58831525, []byte("key"), nil) + s.Require().NoError(err) + defer itr.Close() + + count := make(map[string]struct{}) + for ; itr.Valid(); itr.Next() { + count[string(itr.Key())] = struct{}{} + } + + s.Require().NoError(itr.Error()) + s.Require().Equal(3, len(count)) +} + +func (s *StorageTestSuite) TestDatabaseIterator_ForwardIteration() { + db, err := s.NewDB(s.T().TempDir()) + s.Require().NoError(err) + defer db.Close() + + dbApplyChangeset(s.T(), db, 8, storeKey1, [][]byte{[]byte("keyA")}, [][]byte{[]byte("value001")}) + dbApplyChangeset(s.T(), db, 9, storeKey1, [][]byte{[]byte("keyB")}, [][]byte{[]byte("value002")}) + dbApplyChangeset(s.T(), db, 10, storeKey1, [][]byte{[]byte("keyC")}, [][]byte{[]byte("value003")}) + dbApplyChangeset(s.T(), db, 11, storeKey1, [][]byte{[]byte("keyD")}, [][]byte{[]byte("value004")}) + + dbApplyChangeset(s.T(), db, 2, storeKey1, [][]byte{[]byte("keyD")}, [][]byte{[]byte("value007")}) + dbApplyChangeset(s.T(), db, 3, storeKey1, [][]byte{[]byte("keyE")}, [][]byte{[]byte("value008")}) + dbApplyChangeset(s.T(), db, 4, storeKey1, [][]byte{[]byte("keyF")}, [][]byte{[]byte("value009")}) + dbApplyChangeset(s.T(), db, 5, storeKey1, [][]byte{[]byte("keyH")}, [][]byte{[]byte("value010")}) + + itr, err := db.Iterator(storeKey1Bytes, 6, nil, []byte("keyZ")) + s.Require().NoError(err) + + defer itr.Close() + count := 0 + for ; itr.Valid(); itr.Next() { + count++ + } + + s.Require().NoError(itr.Error()) + s.Require().Equal(4, count) +} + +func (s *StorageTestSuite) TestDatabaseIterator_ForwardIterationHigher() { + db, err := s.NewDB(s.T().TempDir()) + s.Require().NoError(err) + defer db.Close() + + dbApplyChangeset(s.T(), db, 9, storeKey1, [][]byte{[]byte("keyB")}, [][]byte{[]byte("value002")}) + dbApplyChangeset(s.T(), db, 10, storeKey1, [][]byte{[]byte("keyC")}, [][]byte{[]byte("value003")}) + dbApplyChangeset(s.T(), db, 11, storeKey1, [][]byte{[]byte("keyD")}, [][]byte{[]byte("value004")}) + + dbApplyChangeset(s.T(), db, 12, storeKey1, [][]byte{[]byte("keyD")}, [][]byte{[]byte("value007")}) + dbApplyChangeset(s.T(), db, 13, storeKey1, [][]byte{[]byte("keyE")}, [][]byte{[]byte("value008")}) + dbApplyChangeset(s.T(), db, 14, storeKey1, [][]byte{[]byte("keyF")}, [][]byte{[]byte("value009")}) + dbApplyChangeset(s.T(), db, 15, storeKey1, [][]byte{[]byte("keyH")}, [][]byte{[]byte("value010")}) + + itr, err := db.Iterator(storeKey1Bytes, 6, nil, []byte("keyZ")) + s.Require().NoError(err) + + defer itr.Close() + + count := 0 + for ; itr.Valid(); itr.Next() { + count++ + } + + s.Require().Equal(0, count) +} + +func (s *StorageTestSuite) TestDatabaseIterator_WithDelete() { + db, err := s.NewDB(s.T().TempDir()) + s.Require().NoError(err) + defer db.Close() + + dbApplyChangeset(s.T(), db, 1, storeKey1, [][]byte{[]byte("keyA")}, [][]byte{[]byte("value001")}) + dbApplyChangeset(s.T(), db, 2, storeKey1, [][]byte{[]byte("keyA")}, [][]byte{nil}) // delete + + itr, err := db.Iterator(storeKey1Bytes, 1, nil, nil) + s.Require().NoError(err) + + count := 0 + for ; itr.Valid(); itr.Next() { + count++ + } + s.Require().Equal(1, count) + + itr, err = db.Iterator(storeKey1Bytes, 2, nil, nil) + s.Require().NoError(err) + + count = 0 + for ; itr.Valid(); itr.Next() { + count++ + } + s.Require().Equal(0, count) +} + +func (s *StorageTestSuite) TestDatabase_IteratorNoDomain() { + db, err := s.NewDB(s.T().TempDir()) + s.Require().NoError(err) + defer db.Close() + + // for versions 1-50, set all 10 keys + for v := uint64(1); v <= 50; v++ { + cs := corestore.NewChangesetWithPairs(v, map[string]corestore.KVPairs{storeKey1: {}}) + for i := 0; i < 10; i++ { + key := fmt.Sprintf("key%03d", i) + val := fmt.Sprintf("val%03d-%03d", i, v) + + cs.AddKVPair(storeKey1Bytes, corestore.KVPair{Key: []byte(key), Value: []byte(val), Remove: false}) + } + + s.Require().NoError(db.ApplyChangeset(cs)) + } + + // create an iterator over the entire domain + itr, err := db.Iterator(storeKey1Bytes, 50, nil, nil) + s.Require().NoError(err) + + defer itr.Close() + + var i, count int + for ; itr.Valid(); itr.Next() { + s.Require().Equal([]byte(fmt.Sprintf("key%03d", i)), itr.Key(), string(itr.Key())) + s.Require().Equal([]byte(fmt.Sprintf("val%03d-%03d", i, 50)), itr.Value()) + + i++ + count++ + } + s.Require().NoError(itr.Error()) + s.Require().Equal(10, count) +} + +func (s *StorageTestSuite) TestDatabase_Prune() { + if slices.Contains(s.SkipTests, s.T().Name()) { + s.T().SkipNow() + } + + db, err := s.NewDB(s.T().TempDir()) + s.Require().NoError(err) + defer db.Close() + + // for versions 1-50, set 10 keys + for v := uint64(1); v <= 50; v++ { + cs := corestore.NewChangesetWithPairs(v, map[string]corestore.KVPairs{storeKey1: {}}) + for i := 0; i < 10; i++ { + key := fmt.Sprintf("key%03d", i) + val := fmt.Sprintf("val%03d-%03d", i, v) + + cs.AddKVPair(storeKey1Bytes, corestore.KVPair{Key: []byte(key), Value: []byte(val)}) + } + + s.Require().NoError(db.ApplyChangeset(cs)) + } + + // prune the first 25 versions + s.Require().NoError(db.Prune(25)) + + latestVersion, err := db.GetLatestVersion() + s.Require().NoError(err) + s.Require().Equal(uint64(50), latestVersion) + + // Ensure all keys are no longer present up to and including version 25 and + // all keys are present after version 25. + for v := uint64(1); v <= 50; v++ { + for i := 0; i < 10; i++ { + key := fmt.Sprintf("key%03d", i) + val := fmt.Sprintf("val%03d-%03d", i, v) + + bz, err := db.Get(storeKey1Bytes, v, []byte(key)) + if v <= 25 { + s.Require().Error(err) + s.Require().Nil(bz) + } else { + s.Require().NoError(err) + s.Require().Equal([]byte(val), bz) + } + } + } + + itr, err := db.Iterator(storeKey1Bytes, 25, []byte("key000"), nil) + s.Require().NoError(err) + s.Require().False(itr.Valid()) + + // prune the latest version which should prune the entire dataset + s.Require().NoError(db.Prune(50)) + + for v := uint64(1); v <= 50; v++ { + for i := 0; i < 10; i++ { + key := fmt.Sprintf("key%03d", i) + + bz, err := db.Get(storeKey1Bytes, v, []byte(key)) + s.Require().Error(err) + s.Require().Nil(bz) + } + } +} + +func (s *StorageTestSuite) TestDatabase_Prune_KeepRecent() { + if slices.Contains(s.SkipTests, s.T().Name()) { + s.T().SkipNow() + } + + db, err := s.NewDB(s.T().TempDir()) + s.Require().NoError(err) + defer db.Close() + + key := []byte("key") + + // write a key at three different versions + s.Require().NoError(db.ApplyChangeset(corestore.NewChangesetWithPairs( + 1, + map[string]corestore.KVPairs{storeKey1: {{Key: key, Value: []byte("val001"), Remove: false}}}, + ))) + s.Require().NoError(db.ApplyChangeset(corestore.NewChangesetWithPairs( + 100, + map[string]corestore.KVPairs{storeKey1: {{Key: key, Value: []byte("val100"), Remove: false}}}, + ))) + s.Require().NoError(db.ApplyChangeset(corestore.NewChangesetWithPairs( + 200, + map[string]corestore.KVPairs{storeKey1: {{Key: key, Value: []byte("val200"), Remove: false}}}, + ))) + + // prune version 50 + s.Require().NoError(db.Prune(50)) + + // ensure queries for versions 50 and older return nil + bz, err := db.Get(storeKey1Bytes, 49, key) + s.Require().Error(err) + s.Require().Nil(bz) + + itr, err := db.Iterator(storeKey1Bytes, 49, nil, nil) + s.Require().NoError(err) + s.Require().False(itr.Valid()) + + defer itr.Close() + + // ensure the value previously at version 1 is still there for queries greater than 50 + bz, err = db.Get(storeKey1Bytes, 51, key) + s.Require().NoError(err) + s.Require().Equal([]byte("val001"), bz) + + // ensure the correct value at a greater height + bz, err = db.Get(storeKey1Bytes, 200, key) + s.Require().NoError(err) + s.Require().Equal([]byte("val200"), bz) + + // prune latest height and ensure we have the previous version when querying above it + s.Require().NoError(db.Prune(200)) + + bz, err = db.Get(storeKey1Bytes, 201, key) + s.Require().NoError(err) + s.Require().Equal([]byte("val200"), bz) +} + +func (s *StorageTestSuite) TestDatabase_Restore() { + db, err := s.NewDB(s.T().TempDir()) + s.Require().NoError(err) + defer db.Close() + + toVersion := uint64(10) + keyCount := 10 + + // for versions 1-10, set 10 keys + for v := uint64(1); v <= toVersion; v++ { + cs := corestore.NewChangesetWithPairs(v, map[string]corestore.KVPairs{storeKey1: {}}) + for i := 0; i < keyCount; i++ { + key := fmt.Sprintf("key%03d", i) + val := fmt.Sprintf("val%03d-%03d", i, v) + + cs.AddKVPair(storeKey1Bytes, corestore.KVPair{Key: []byte(key), Value: []byte(val)}) + } + + s.Require().NoError(db.ApplyChangeset(cs)) + } + + latestVersion, err := db.GetLatestVersion() + s.Require().NoError(err) + s.Require().Equal(uint64(10), latestVersion) + + chStorage := make(chan *corestore.StateChanges, 5) + + go func() { + for i := uint64(11); i <= 15; i++ { + kvPairs := []corestore.KVPair{} + for j := 0; j < keyCount; j++ { + key := fmt.Sprintf("key%03d-%03d", j, i) + val := fmt.Sprintf("val%03d-%03d", j, i) + + kvPairs = append(kvPairs, corestore.KVPair{Key: []byte(key), Value: []byte(val)}) + } + chStorage <- &corestore.StateChanges{ + Actor: storeKey1Bytes, + StateChanges: kvPairs, + } + } + close(chStorage) + }() + + // restore with snapshot version smaller than latest version + // should return an error + err = db.Restore(9, chStorage) + s.Require().Error(err) + + // restore + err = db.Restore(11, chStorage) + s.Require().NoError(err) + + // check the storage + for i := uint64(11); i <= 15; i++ { + for j := 0; j < keyCount; j++ { + key := fmt.Sprintf("key%03d-%03d", j, i) + val := fmt.Sprintf("val%03d-%03d", j, i) + + v, err := db.Get(storeKey1Bytes, 11, []byte(key)) + s.Require().NoError(err) + s.Require().Equal([]byte(val), v) + } + } +} + +func (s *StorageTestSuite) TestUpgradable() { + ss, err := s.NewDB(s.T().TempDir()) + s.Require().NoError(err) + defer ss.Close() + + // Ensure the database is upgradable. + if _, ok := ss.db.(store.UpgradableDatabase); !ok { + s.T().Skip("database is not upgradable") + } + + storeKeys := []string{"store1", "store2", "store3"} + uptoVersion := uint64(50) + keyCount := 10 + for _, storeKey := range storeKeys { + for v := uint64(1); v <= uptoVersion; v++ { + keys := make([][]byte, keyCount) + vals := make([][]byte, keyCount) + for i := 0; i < keyCount; i++ { + keys[i] = []byte(fmt.Sprintf("key%03d", i)) + vals[i] = []byte(fmt.Sprintf("val%03d-%03d", i, v)) + } + dbApplyChangeset(s.T(), ss, v, storeKey, keys, vals) + } + } + + // prune storekeys (`store2`, `store3`) + removedStoreKeys := []string{storeKeys[1], storeKeys[2]} + err = ss.PruneStoreKeys(removedStoreKeys, uptoVersion) + s.Require().NoError(err) + // should be able to query before Prune for removed storeKeys + for _, storeKey := range removedStoreKeys { + for v := uint64(1); v <= uptoVersion; v++ { + for i := 0; i < keyCount; i++ { + bz, err := ss.Get([]byte(storeKey), v, []byte(fmt.Sprintf("key%03d", i))) + s.Require().NoError(err) + s.Require().Equal([]byte(fmt.Sprintf("val%03d-%03d", i, v)), bz) + } + } + } + s.Require().NoError(ss.Prune(uptoVersion)) + // should not be able to query after Prune + // skip the test of RocksDB + if !slices.Contains(s.SkipTests, "TestUpgradable_Prune") { + for _, storeKey := range removedStoreKeys { + // it will return error ErrVersionPruned + for v := uint64(1); v <= uptoVersion; v++ { + for i := 0; i < keyCount; i++ { + _, err := ss.Get([]byte(storeKey), v, []byte(fmt.Sprintf("key%03d", i))) + s.Require().Error(err) + } + } + v := uptoVersion + 1 + for i := 0; i < keyCount; i++ { + val, err := ss.Get([]byte(storeKey), v, []byte(fmt.Sprintf("key%03d", i))) + s.Require().NoError(err) + s.Require().Nil(val) + } + } + } +} + +func (s *StorageTestSuite) TestRemovingOldStoreKey() { + ss, err := s.NewDB(s.T().TempDir()) + s.Require().NoError(err) + defer ss.Close() + + // Ensure the database is upgradable. + if _, ok := ss.db.(store.UpgradableDatabase); !ok { + s.T().Skip("database is not upgradable") + } + + storeKeys := []string{"store1", "store2", "store3"} + uptoVersion := uint64(50) + keyCount := 10 + for _, storeKey := range storeKeys { + for v := uint64(1); v <= uptoVersion; v++ { + keys := make([][]byte, keyCount) + vals := make([][]byte, keyCount) + for i := 0; i < keyCount; i++ { + keys[i] = []byte(fmt.Sprintf("key%03d-%03d", i, v)) + vals[i] = []byte(fmt.Sprintf("val%03d-%03d", i, v)) + } + dbApplyChangeset(s.T(), ss, v, storeKey, keys, vals) + } + } + + // remove `store1` and `store3` + removedStoreKeys := []string{storeKeys[0], storeKeys[2]} + err = ss.PruneStoreKeys(removedStoreKeys, uptoVersion) + s.Require().NoError(err) + // should be able to query before Prune for removed storeKeys + for _, storeKey := range removedStoreKeys { + for v := uint64(1); v <= uptoVersion; v++ { + for i := 0; i < keyCount; i++ { + bz, err := ss.Get([]byte(storeKey), v, []byte(fmt.Sprintf("key%03d-%03d", i, v))) + s.Require().NoError(err) + s.Require().Equal([]byte(fmt.Sprintf("val%03d-%03d", i, v)), bz) + } + } + } + // add `store1` back + newStoreKeys := []string{storeKeys[0], storeKeys[1]} + newVersion := uptoVersion + 10 + for _, storeKey := range newStoreKeys { + for v := uptoVersion + 1; v <= newVersion; v++ { + keys := make([][]byte, keyCount) + vals := make([][]byte, keyCount) + for i := 0; i < keyCount; i++ { + keys[i] = []byte(fmt.Sprintf("key%03d-%03d", i, v)) + vals[i] = []byte(fmt.Sprintf("val%03d-%03d", i, v)) + } + dbApplyChangeset(s.T(), ss, v, storeKey, keys, vals) + } + } + + s.Require().NoError(ss.Prune(newVersion)) + // skip the test of RocksDB + if !slices.Contains(s.SkipTests, "TestUpgradable_Prune") { + for _, storeKey := range removedStoreKeys { + queryVersion := newVersion + 1 + // should not be able to query after Prune during 1 ~ uptoVersion + for v := uint64(1); v <= uptoVersion; v++ { + for i := 0; i < keyCount; i++ { + val, err := ss.Get([]byte(storeKey), queryVersion, []byte(fmt.Sprintf("key%03d", i))) + s.Require().NoError(err) + s.Require().Nil(val) + } + } + // should be able to query after Prune during uptoVersion + 1 ~ newVersion + // for `store1` added back + for v := uptoVersion + 1; v <= newVersion; v++ { + for i := 0; i < keyCount; i++ { + val, err := ss.Get([]byte(storeKey), queryVersion, []byte(fmt.Sprintf("key%03d-%03d", i, v))) + s.Require().NoError(err) + if storeKey == storeKeys[0] { + // `store1` is added back + s.Require().Equal([]byte(fmt.Sprintf("val%03d-%03d", i, v)), val) + } else { + // `store3` is removed + s.Require().Nil(val) + } + } + } + } + } +} + +// TestVersionExists tests the VersionExists method of the Database struct. +func (s *StorageTestSuite) TestVersionExists() { + // Define test cases + testCases := []struct { + name string + setup func(t *testing.T, db *StorageStore) + version uint64 + expectedExists bool + expectError bool + }{ + { + name: "Fresh database: version 0 exists", + setup: func(t *testing.T, db *StorageStore) { + t.Helper() + // No setup needed for fresh database + }, + version: 0, + expectedExists: true, + expectError: false, + }, + { + name: "Fresh database: version 1 exists", + setup: func(t *testing.T, db *StorageStore) { + t.Helper() + // No setup needed for fresh database + }, + version: 1, + expectedExists: false, + expectError: false, + }, + { + name: "After setting latest version to 10, version 5 exists", + setup: func(t *testing.T, db *StorageStore) { + t.Helper() + err := db.SetLatestVersion(10) + if err != nil { + t.Fatalf("Setting latest version should not error: %v", err) + } + }, + version: 5, + expectedExists: true, // Since pruning hasn't occurred, earliestVersion is still 0 + expectError: false, + }, + { + name: "After setting latest version to 10 and pruning to 5, version 4 does not exist", + setup: func(t *testing.T, db *StorageStore) { + t.Helper() + err := db.SetLatestVersion(10) + if err != nil { + t.Fatalf("Setting latest version should not error: %v", err) + } + + err = db.Prune(5) + if err != nil { + t.Fatalf("Pruning to version 5 should not error: %v", err) + } + }, + version: 4, + expectedExists: false, + expectError: false, + }, + { + name: "After setting latest version to 10 and pruning to 5, version 5 does not exist", + setup: func(t *testing.T, db *StorageStore) { + t.Helper() + err := db.SetLatestVersion(10) + if err != nil { + t.Fatalf("Setting latest version should not error: %v", err) + } + + err = db.Prune(5) + if err != nil { + t.Fatalf("Pruning to version 5 should not error: %v", err) + } + }, + version: 5, + expectedExists: false, + expectError: false, + }, + { + name: "After setting latest version to 10 and pruning to 5, version 6 exists", + setup: func(t *testing.T, db *StorageStore) { + t.Helper() + err := db.SetLatestVersion(10) + if err != nil { + t.Fatalf("Setting latest version should not error: %v", err) + } + + err = db.Prune(5) + if err != nil { + t.Fatalf("Pruning to version 5 should not error: %v", err) + } + }, + version: 6, + expectedExists: true, + expectError: false, + }, + { + name: "After pruning to 0, all versions >=1 exist", + setup: func(t *testing.T, db *StorageStore) { + t.Helper() + err := db.SetLatestVersion(10) + if err != nil { + t.Fatalf("Setting latest version should not error: %v", err) + } + // Prune to version 0 + err = db.Prune(0) + if err != nil { + t.Fatalf("Pruning to version 0 should not error: %v", err) + } + }, + version: 1, + expectedExists: true, + expectError: false, + }, + } + + // Iterate over each test case + for _, tc := range testCases { + s.T().Run(tc.name, func(t *testing.T) { + // Initialize the database for each test + db, err := s.NewDB(t.TempDir()) + require.NoError(t, err, "Failed to initialize the database") + defer db.Close() + + // Setup test environment + tc.setup(t, db) + + // Call VersionExists and check the result + exists, err := db.VersionExists(tc.version) + if tc.expectError { + require.Error(t, err, "Expected error but got none") + } else { + require.NoError(t, err, "Did not expect an error but got one") + require.Equal(t, tc.expectedExists, exists, "Version existence mismatch") + } + }) + } +} + +func dbApplyChangeset( + t *testing.T, + db store.VersionedWriter, + version uint64, + storeKey string, + keys, vals [][]byte, +) { + t.Helper() + + require.Greater(t, version, uint64(0)) + require.Equal(t, len(keys), len(vals)) + + cs := corestore.NewChangeset(version) + for i := 0; i < len(keys); i++ { + remove := false + if vals[i] == nil { + remove = true + } + + cs.AddKVPair([]byte(storeKey), corestore.KVPair{Key: keys[i], Value: vals[i], Remove: remove}) + } + + require.NoError(t, db.ApplyChangeset(cs)) +} diff --git a/store/v2/storage/store.go b/store/v2/storage/store.go new file mode 100644 index 000000000000..5ca5a30132b7 --- /dev/null +++ b/store/v2/storage/store.go @@ -0,0 +1,161 @@ +package storage + +import ( + "errors" + "fmt" + + "cosmossdk.io/core/log" + corestore "cosmossdk.io/core/store" + "cosmossdk.io/store/v2" + "cosmossdk.io/store/v2/snapshots" +) + +const ( + // TODO: it is a random number, need to be tuned + defaultBatchBufferSize = 100000 +) + +var ( + _ store.VersionedWriter = (*StorageStore)(nil) + _ snapshots.StorageSnapshotter = (*StorageStore)(nil) + _ store.Pruner = (*StorageStore)(nil) + _ store.UpgradableDatabase = (*StorageStore)(nil) +) + +// StorageStore is a wrapper around the store.VersionedWriter interface. +type StorageStore struct { + logger log.Logger + db Database +} + +// NewStorageStore returns a reference to a new StorageStore. +func NewStorageStore(db Database, logger log.Logger) *StorageStore { + return &StorageStore{ + logger: logger, + db: db, + } +} + +// Has returns true if the key exists in the store. +func (ss *StorageStore) Has(storeKey []byte, version uint64, key []byte) (bool, error) { + return ss.db.Has(storeKey, version, key) +} + +// Get returns the value associated with the given key. +func (ss *StorageStore) Get(storeKey []byte, version uint64, key []byte) ([]byte, error) { + return ss.db.Get(storeKey, version, key) +} + +// ApplyChangeset applies the given changeset to the storage. +func (ss *StorageStore) ApplyChangeset(cs *corestore.Changeset) error { + b, err := ss.db.NewBatch(cs.Version) + if err != nil { + return err + } + + for _, pairs := range cs.Changes { + for _, kvPair := range pairs.StateChanges { + if kvPair.Remove { + if err := b.Delete(pairs.Actor, kvPair.Key); err != nil { + return err + } + } else { + if err := b.Set(pairs.Actor, kvPair.Key, kvPair.Value); err != nil { + return err + } + } + } + } + + if err := b.Write(); err != nil { + return err + } + + return nil +} + +// GetLatestVersion returns the latest version of the store. +func (ss *StorageStore) GetLatestVersion() (uint64, error) { + return ss.db.GetLatestVersion() +} + +// SetLatestVersion sets the latest version of the store. +func (ss *StorageStore) SetLatestVersion(version uint64) error { + return ss.db.SetLatestVersion(version) +} + +// VersionExists returns true if the given version exists in the store. +func (ss *StorageStore) VersionExists(version uint64) (bool, error) { + return ss.db.VersionExists(version) +} + +// Iterator returns an iterator over the specified domain and prefix. +func (ss *StorageStore) Iterator(storeKey []byte, version uint64, start, end []byte) (corestore.Iterator, error) { + return ss.db.Iterator(storeKey, version, start, end) +} + +// ReverseIterator returns an iterator over the specified domain and prefix in reverse. +func (ss *StorageStore) ReverseIterator(storeKey []byte, version uint64, start, end []byte) (corestore.Iterator, error) { + return ss.db.ReverseIterator(storeKey, version, start, end) +} + +// Prune prunes the store up to the given version. +func (ss *StorageStore) Prune(version uint64) error { + return ss.db.Prune(version) +} + +// Restore restores the store from the given channel. +func (ss *StorageStore) Restore(version uint64, chStorage <-chan *corestore.StateChanges) error { + latestVersion, err := ss.db.GetLatestVersion() + if err != nil { + return fmt.Errorf("failed to get latest version: %w", err) + } + if version <= latestVersion { + return fmt.Errorf("the snapshot version %d is not greater than latest version %d", version, latestVersion) + } + + b, err := ss.db.NewBatch(version) + if err != nil { + return err + } + + for kvPair := range chStorage { + for _, kv := range kvPair.StateChanges { + if err := b.Set(kvPair.Actor, kv.Key, kv.Value); err != nil { + return err + } + if b.Size() > defaultBatchBufferSize { + if err := b.Write(); err != nil { + return err + } + if err := b.Reset(); err != nil { + return err + } + } + } + } + + if b.Size() > 0 { + if err := b.Write(); err != nil { + return err + } + } + + return nil +} + +// PruneStoreKeys prunes the store keys which implements the store.UpgradableDatabase +// interface. +func (ss *StorageStore) PruneStoreKeys(storeKeys []string, version uint64) error { + gdb, ok := ss.db.(store.UpgradableDatabase) + if !ok { + return errors.New("db does not implement UpgradableDatabase interface") + } + + return gdb.PruneStoreKeys(storeKeys, version) +} + +// Close closes the store. +func (ss *StorageStore) Close() error { + return ss.db.Close() +} diff --git a/store/v2/store.go b/store/v2/store.go new file mode 100644 index 000000000000..124d7de579a1 --- /dev/null +++ b/store/v2/store.go @@ -0,0 +1,100 @@ +package store + +import ( + "io" + + corestore "cosmossdk.io/core/store" + "cosmossdk.io/store/v2/metrics" + "cosmossdk.io/store/v2/proof" +) + +// RootStore defines an abstraction layer containing a State Storage (SS) engine +// and one or more State Commitment (SC) engines. +type RootStore interface { + Pruner + Backend + + // StateLatest returns a read-only version of the RootStore at the latest + // height, alongside the associated version. + StateLatest() (uint64, corestore.ReaderMap, error) + + // StateAt is analogous to StateLatest() except it returns a read-only version + // of the RootStore at the provided version. If such a version cannot be found, + // an error must be returned. + StateAt(version uint64) (corestore.ReaderMap, error) + + // Query performs a query on the RootStore for a given store key, version (height), + // and key tuple. Queries should be routed to the underlying SS engine. + Query(storeKey []byte, version uint64, key []byte, prove bool) (QueryResult, error) + + // LoadVersion loads the RootStore to the given version. + LoadVersion(version uint64) error + + // LoadLatestVersion behaves identically to LoadVersion except it loads the + // latest version implicitly. + LoadLatestVersion() error + + // GetLatestVersion returns the latest version, i.e. height, committed. + GetLatestVersion() (uint64, error) + + // SetInitialVersion sets the initial version on the RootStore. + SetInitialVersion(v uint64) error + + // Commit should be responsible for taking the provided changeset and flushing + // it to disk. Note, it will overwrite the changeset if WorkingHash() was called. + // Commit() should ensure the changeset is committed to all SC and SS backends + // and flushed to disk. It must return a hash of the merkle-ized committed state. + Commit(cs *corestore.Changeset) ([]byte, error) + + // LastCommitID returns a CommitID pertaining to the last commitment. + LastCommitID() (proof.CommitID, error) + + // SetMetrics sets the telemetry handler on the RootStore. + SetMetrics(m metrics.Metrics) + + io.Closer +} + +// Backend defines the interface for the RootStore backends. +type Backend interface { + // GetStateStorage returns the SS backend. + GetStateStorage() VersionedWriter + + // GetStateCommitment returns the SC backend. + GetStateCommitment() Committer +} + +// UpgradeableStore defines the interface for upgrading store keys. +type UpgradeableStore interface { + // LoadVersionAndUpgrade behaves identically to LoadVersion except it also + // accepts a StoreUpgrades object that defines a series of transformations to + // apply to store keys (if any). + // + // Note, handling StoreUpgrades is optional depending on the underlying store + // implementation. + LoadVersionAndUpgrade(version uint64, upgrades *corestore.StoreUpgrades) error +} + +// Pruner defines the interface for pruning old versions of the store or database. +type Pruner interface { + // Prune prunes the store to the provided version. + Prune(version uint64) error +} + +// PausablePruner extends the Pruner interface to include the API for pausing +// the pruning process. +type PausablePruner interface { + Pruner + + // PausePruning pauses or resumes the pruning process to avoid the parallel writes + // while committing the state. + PausePruning(pause bool) +} + +// QueryResult defines the response type to performing a query on a RootStore. +type QueryResult struct { + Key []byte + Value []byte + Version uint64 + ProofOps []proof.CommitmentOp +} diff --git a/tests/go.mod b/tests/go.mod index b989216528a8..57eac9921f4b 100644 --- a/tests/go.mod +++ b/tests/go.mod @@ -239,8 +239,20 @@ replace ( // pseudo version lower than the latest tag cosmossdk.io/api => cosmossdk.io/api v0.7.3-0.20240924065902-eb7653cfecdf // main cosmossdk.io/client/v2 => ../client/v2 +<<<<<<< HEAD // pseudo version lower than the latest tag cosmossdk.io/store => cosmossdk.io/store v1.0.0-rc.0.0.20241106093505-9611c5a0e6e3 // main +======= + cosmossdk.io/collections => ../collections + cosmossdk.io/core => ../core + cosmossdk.io/core/testing => ../core/testing + cosmossdk.io/indexer/postgres => ../indexer/postgres + cosmossdk.io/runtime/v2 => ../runtime/v2 + cosmossdk.io/server/v2/appmanager => ../server/v2/appmanager + cosmossdk.io/server/v2/stf => ../server/v2/stf + cosmossdk.io/store => ../store + cosmossdk.io/store/v2 => ../store/v2 +>>>>>>> 43e28b43a (refactor(store/v2): simplify genesis flow (#22435)) cosmossdk.io/x/accounts => ../x/accounts cosmossdk.io/x/accounts/defaults/base => ../x/accounts/defaults/base cosmossdk.io/x/accounts/defaults/lockup => ../x/accounts/defaults/lockup diff --git a/tests/go.sum b/tests/go.sum index 2c329055c639..125b777d75b9 100644 --- a/tests/go.sum +++ b/tests/go.sum @@ -192,6 +192,7 @@ cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xX cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg= 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= +<<<<<<< HEAD cosmossdk.io/api v0.7.3-0.20240924065902-eb7653cfecdf h1:CttA/mEIxGm4E7vwrjUpju7/Iespns08d9bOza70cIc= cosmossdk.io/api v0.7.3-0.20240924065902-eb7653cfecdf/go.mod h1:YMfx2ATpgITsoydD3hIBa8IkDHtyXp/14rmG0d3sEew= cosmossdk.io/collections v0.4.1-0.20241107084833-00f3065e70ee h1:OLqvi9ekfShobmdgr4Q/8pu+LjzYJSrNl9tiadPg2xY= @@ -200,6 +201,8 @@ cosmossdk.io/core v1.0.0-alpha.5 h1:McjYXAQ6XcT20v2uHyH7PhoWH8V+mebzfVFqT3GinsI= cosmossdk.io/core v1.0.0-alpha.5/go.mod h1:3u9cWq1FAVtiiCrDPpo4LhR+9V6k/ycSG4/Y/tREWCY= cosmossdk.io/core/testing v0.0.0-20240923163230-04da382a9f29 h1:NxxUo0GMJUbIuVg0R70e3cbn9eFTEuMr7ev1AFvypdY= cosmossdk.io/core/testing v0.0.0-20240923163230-04da382a9f29/go.mod h1:8s2tPeJtSiQuoyPmr2Ag7meikonISO4Fv4MoO8+ORrs= +======= +>>>>>>> 43e28b43a (refactor(store/v2): simplify genesis flow (#22435)) cosmossdk.io/depinject v1.1.0 h1:wLan7LG35VM7Yo6ov0jId3RHWCGRhe8E8bsuARorl5E= cosmossdk.io/depinject v1.1.0/go.mod h1:kkI5H9jCGHeKeYWXTqYdruogYrEeWvBQCw1Pj4/eCFI= cosmossdk.io/errors v1.0.1 h1:bzu+Kcr0kS/1DuPBtUFdWjzLqyUuCiyHjyJB6srBV/0= diff --git a/tests/integration/v2/app.go b/tests/integration/v2/app.go new file mode 100644 index 000000000000..aa5420d425dd --- /dev/null +++ b/tests/integration/v2/app.go @@ -0,0 +1,411 @@ +package integration + +import ( + "context" + "crypto/sha256" + "errors" + "fmt" + "math/rand" + "testing" + "time" + + cmtproto "github.com/cometbft/cometbft/api/cometbft/types/v1" + cmtjson "github.com/cometbft/cometbft/libs/json" + cmttypes "github.com/cometbft/cometbft/types" + "github.com/stretchr/testify/require" + + "cosmossdk.io/core/comet" + corecontext "cosmossdk.io/core/context" + "cosmossdk.io/core/server" + corestore "cosmossdk.io/core/store" + "cosmossdk.io/core/transaction" + "cosmossdk.io/depinject" + sdkmath "cosmossdk.io/math" + "cosmossdk.io/runtime/v2" + "cosmossdk.io/runtime/v2/services" + "cosmossdk.io/server/v2/stf" + "cosmossdk.io/server/v2/stf/branch" + "cosmossdk.io/store/v2" + "cosmossdk.io/store/v2/root" + bankkeeper "cosmossdk.io/x/bank/keeper" + banktypes "cosmossdk.io/x/bank/types" + consensustypes "cosmossdk.io/x/consensus/types" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/std" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/simulation" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + authsign "github.com/cosmos/cosmos-sdk/x/auth/signing" + "github.com/cosmos/cosmos-sdk/x/auth/tx" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" +) + +const DefaultGenTxGas = 10000000 +const ( + Genesis_COMMIT = iota + Genesis_NOCOMMIT + Genesis_SKIP +) + +type stateMachineTx = transaction.Tx + +// DefaultConsensusParams defines the default CometBFT consensus params used in +// SimApp testing. +var DefaultConsensusParams = &cmtproto.ConsensusParams{ + Version: &cmtproto.VersionParams{ + App: 1, + }, + Block: &cmtproto.BlockParams{ + MaxBytes: 200000, + MaxGas: 100_000_000, + }, + Evidence: &cmtproto.EvidenceParams{ + MaxAgeNumBlocks: 302400, + MaxAgeDuration: 504 * time.Hour, // 3 weeks is the max duration + MaxBytes: 10000, + }, + Validator: &cmtproto.ValidatorParams{ + PubKeyTypes: []string{ + cmttypes.ABCIPubKeyTypeEd25519, + cmttypes.ABCIPubKeyTypeSecp256k1, + }, + }, +} + +// StartupConfig defines the startup configuration of a new test app. +type StartupConfig struct { + // ValidatorSet defines a custom validator set to be validating the app. + ValidatorSet func() (*cmttypes.ValidatorSet, error) + // AppOption defines the additional operations that will be run in the app builder phase. + AppOption runtime.AppBuilderOption[stateMachineTx] + // GenesisBehavior defines the behavior of the app at genesis. + GenesisBehavior int + // GenesisAccounts defines the genesis accounts to be used in the app. + GenesisAccounts []GenesisAccount + // HomeDir defines the home directory of the app where config and data will be stored. + HomeDir string +} + +func DefaultStartUpConfig(t *testing.T) StartupConfig { + t.Helper() + + priv := secp256k1.GenPrivKey() + ba := authtypes.NewBaseAccount( + priv.PubKey().Address().Bytes(), + priv.PubKey(), + 0, + 0, + ) + ga := GenesisAccount{ + ba, + sdk.NewCoins( + sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(100000000000000)), + ), + } + homedir := t.TempDir() + t.Logf("generated integration test app config; HomeDir=%s", homedir) + return StartupConfig{ + ValidatorSet: CreateRandomValidatorSet, + GenesisBehavior: Genesis_COMMIT, + GenesisAccounts: []GenesisAccount{ga}, + HomeDir: homedir, + } +} + +// NewApp initializes a new runtime.App. A Nop logger is set in runtime.App. +// appConfig defines the application configuration (f.e. app_config.go). +// extraOutputs defines the extra outputs to be assigned by the dependency injector (depinject). +func NewApp( + appConfig depinject.Config, + startupConfig StartupConfig, + extraOutputs ...interface{}, +) (*App, error) { + // create the app with depinject + var ( + storeBuilder = root.NewBuilder() + app *runtime.App[stateMachineTx] + appBuilder *runtime.AppBuilder[stateMachineTx] + txConfig client.TxConfig + txConfigOptions tx.ConfigOptions + cometService comet.Service = &cometServiceImpl{} + kvFactory corestore.KVStoreServiceFactory = func(actor []byte) corestore.KVStoreService { + return services.NewGenesisKVService(actor, &storeService{actor, stf.NewKVStoreService(actor)}) + } + cdc codec.Codec + err error + ) + + if err := depinject.Inject( + depinject.Configs( + appConfig, + codec.DefaultProviders, + depinject.Supply( + &root.Config{ + Home: startupConfig.HomeDir, + AppDBBackend: "goleveldb", + Options: root.DefaultStoreOptions(), + }, + runtime.GlobalConfig{ + "server": server.ConfigMap{ + "minimum-gas-prices": "0stake", + }, + }, + services.NewGenesisHeaderService(stf.HeaderService{}), + cometService, + kvFactory, + &eventService{}, + storeBuilder, + ), + depinject.Invoke( + std.RegisterInterfaces, + ), + ), + append(extraOutputs, &appBuilder, &cdc, &txConfigOptions, &txConfig, &storeBuilder)...); err != nil { + return nil, fmt.Errorf("failed to inject dependencies: %w", err) + } + + app, err = appBuilder.Build() + if err != nil { + return nil, fmt.Errorf("failed to build app: %w", err) + } + if err := app.LoadLatest(); err != nil { + return nil, fmt.Errorf("failed to load app: %w", err) + } + + store := storeBuilder.Get() + if store == nil { + return nil, fmt.Errorf("failed to build store: %w", err) + } + err = store.SetInitialVersion(0) + if err != nil { + return nil, fmt.Errorf("failed to set initial version: %w", err) + } + + integrationApp := &App{App: app, Store: store, txConfig: txConfig, lastHeight: 0} + if startupConfig.GenesisBehavior == Genesis_SKIP { + return integrationApp, nil + } + + // create validator set + valSet, err := startupConfig.ValidatorSet() + if err != nil { + return nil, errors.New("failed to create validator set") + } + + var ( + balances []banktypes.Balance + genAccounts []authtypes.GenesisAccount + ) + for _, ga := range startupConfig.GenesisAccounts { + genAccounts = append(genAccounts, ga.GenesisAccount) + balances = append( + balances, + banktypes.Balance{ + Address: ga.GenesisAccount.GetAddress().String(), + Coins: ga.Coins, + }, + ) + } + + genesisJSON, err := genesisStateWithValSet( + cdc, + app.DefaultGenesis(), + valSet, + genAccounts, + balances...) + if err != nil { + return nil, fmt.Errorf("failed to create genesis state: %w", err) + } + + // init chain must be called to stop deliverState from being nil + genesisJSONBytes, err := cmtjson.MarshalIndent(genesisJSON, "", " ") + if err != nil { + return nil, fmt.Errorf( + "failed to marshal default genesis state: %w", + err, + ) + } + + ctx := context.WithValue( + context.Background(), + corecontext.CometParamsInitInfoKey, + &consensustypes.MsgUpdateParams{ + Authority: "consensus", + Block: DefaultConsensusParams.Block, + Evidence: DefaultConsensusParams.Evidence, + Validator: DefaultConsensusParams.Validator, + Abci: DefaultConsensusParams.Abci, + Synchrony: DefaultConsensusParams.Synchrony, + Feature: DefaultConsensusParams.Feature, + }, + ) + + emptyHash := sha256.Sum256(nil) + _, genesisState, err := app.InitGenesis( + ctx, + &server.BlockRequest[stateMachineTx]{ + Height: 1, + Time: time.Now(), + Hash: emptyHash[:], + ChainId: "test-chain", + AppHash: emptyHash[:], + IsGenesis: true, + }, + genesisJSONBytes, + &genesisTxCodec{txConfigOptions}, + ) + if err != nil { + return nil, fmt.Errorf("failed init genesis: %w", err) + } + + if startupConfig.GenesisBehavior == Genesis_NOCOMMIT { + integrationApp.lastHeight = 0 + return integrationApp, nil + } + + _, err = integrationApp.Commit(genesisState) + if err != nil { + return nil, fmt.Errorf("failed to commit initial version: %w", err) + } + + return integrationApp, nil +} + +// App is a wrapper around runtime.App that provides additional testing utilities. +type App struct { + *runtime.App[stateMachineTx] + lastHeight uint64 + Store store.RootStore + txConfig client.TxConfig +} + +// Deliver delivers a block with the given transactions and returns the resulting state. +func (a *App) Deliver( + t *testing.T, ctx context.Context, txs []stateMachineTx, +) (*server.BlockResponse, corestore.WriterMap) { + t.Helper() + req := &server.BlockRequest[stateMachineTx]{ + Height: a.lastHeight + 1, + Txs: txs, + Hash: make([]byte, 32), + AppHash: make([]byte, 32), + } + resp, state, err := a.DeliverBlock(ctx, req) + require.NoError(t, err) + a.lastHeight++ + return resp, state +} + +// StateLatestContext creates returns a new context from context.Background() with the latest state. +func (a *App) StateLatestContext(t *testing.T) context.Context { + t.Helper() + _, state, err := a.Store.StateLatest() + require.NoError(t, err) + writeableState := branch.DefaultNewWriterMap(state) + iCtx := &integrationContext{state: writeableState} + return context.WithValue(context.Background(), contextKey, iCtx) +} + +// Commit commits the given state and returns the new state hash. +func (a *App) Commit(state corestore.WriterMap) ([]byte, error) { + changes, err := state.GetStateChanges() + if err != nil { + return nil, fmt.Errorf("failed to get state changes: %w", err) + } + cs := &corestore.Changeset{Version: a.lastHeight, Changes: changes} + return a.Store.Commit(cs) +} + +// SignCheckDeliver signs and checks the given messages and delivers them. +func (a *App) SignCheckDeliver( + t *testing.T, ctx context.Context, msgs []sdk.Msg, + chainID string, accNums, accSeqs []uint64, privateKeys []cryptotypes.PrivKey, + txErrString string, +) server.TxResult { + t.Helper() + + r := rand.New(rand.NewSource(time.Now().UnixNano())) + sigs := make([]signing.SignatureV2, len(privateKeys)) + + // create a random length memo + memo := simulation.RandStringOfLength(r, simulation.RandIntBetween(r, 0, 100)) + + signMode, err := authsign.APISignModeToInternal(a.txConfig.SignModeHandler().DefaultMode()) + require.NoError(t, err) + + // 1st round: set SignatureV2 with empty signatures, to set correct + // signer infos. + for i, p := range privateKeys { + sigs[i] = signing.SignatureV2{ + PubKey: p.PubKey(), + Data: &signing.SingleSignatureData{ + SignMode: signMode, + }, + Sequence: accSeqs[i], + } + } + + txBuilder := a.txConfig.NewTxBuilder() + err = txBuilder.SetMsgs(msgs...) + require.NoError(t, err) + err = txBuilder.SetSignatures(sigs...) + require.NoError(t, err) + txBuilder.SetMemo(memo) + txBuilder.SetFeeAmount(sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 0)}) + txBuilder.SetGasLimit(DefaultGenTxGas) + + // 2nd round: once all signer infos are set, every signer can sign. + for i, p := range privateKeys { + signerData := authsign.SignerData{ + Address: sdk.AccAddress(p.PubKey().Address()).String(), + ChainID: chainID, + AccountNumber: accNums[i], + Sequence: accSeqs[i], + PubKey: p.PubKey(), + } + + signBytes, err := authsign.GetSignBytesAdapter( + ctx, a.txConfig.SignModeHandler(), signMode, signerData, + // todo why fetch twice? + txBuilder.GetTx()) + require.NoError(t, err) + sig, err := p.Sign(signBytes) + require.NoError(t, err) + sigs[i].Data.(*signing.SingleSignatureData).Signature = sig + } + err = txBuilder.SetSignatures(sigs...) + require.NoError(t, err) + + builtTx := txBuilder.GetTx() + blockResponse, blockState := a.Deliver(t, ctx, []stateMachineTx{builtTx}) + + require.Equal(t, 1, len(blockResponse.TxResults)) + txResult := blockResponse.TxResults[0] + if txErrString != "" { + require.ErrorContains(t, txResult.Error, txErrString) + } else { + require.NoError(t, txResult.Error) + } + + _, err = a.Commit(blockState) + require.NoError(t, err) + + return txResult +} + +// CheckBalance checks the balance of the given address. +func (a *App) CheckBalance( + t *testing.T, ctx context.Context, addr sdk.AccAddress, expected sdk.Coins, keeper bankkeeper.Keeper, +) { + t.Helper() + balances := keeper.GetAllBalances(ctx, addr) + require.Equal(t, expected, balances) +} + +func (a *App) Close() error { + return a.Store.Close() +}