From 8b8bae7b0182980a0ac969130eaa88e7ccb15e26 Mon Sep 17 00:00:00 2001 From: valli0x Date: Wed, 16 Oct 2024 18:13:22 +0300 Subject: [PATCH 01/20] epochs BeginBlock refactor --- x/epochs/keeper/abci.go | 5 +++-- x/epochs/module.go | 8 ++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/x/epochs/keeper/abci.go b/x/epochs/keeper/abci.go index 082ae5b..5273e14 100644 --- a/x/epochs/keeper/abci.go +++ b/x/epochs/keeper/abci.go @@ -17,6 +17,7 @@ package keeper import ( + "context" "strconv" "time" @@ -27,9 +28,9 @@ import ( ) // BeginBlocker of epochs module -func (k Keeper) BeginBlocker(ctx sdk.Context) { +func (k Keeper) BeginBlocker(goCtx context.Context) { defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), telemetry.MetricKeyBeginBlocker) - + ctx := sdk.UnwrapSDKContext(goCtx) logger := k.Logger(ctx) k.IterateEpochInfo(ctx, func(_ int64, epochInfo types.EpochInfo) (stop bool) { diff --git a/x/epochs/module.go b/x/epochs/module.go index 8d92c70..dab9bb8 100644 --- a/x/epochs/module.go +++ b/x/epochs/module.go @@ -45,15 +45,15 @@ import ( var ( _ module.AppModule = AppModule{} - // _ module.BeginBlockAppModule = &AppModule{} - // _ module.EndBlockAppModule = &AppModule{} _ module.AppModuleBasic = AppModuleBasic{} _ module.AppModuleSimulation = AppModule{} + _ appmodule.HasBeginBlocker = (*AppModule)(nil) + _ appmodule.AppModule = (*AppModule)(nil) ) -// ---------------------------------------------------------------------------- +// --------------------------------------------------------------------------- // AppModuleBasic // ---------------------------------------------------------------------------- @@ -179,7 +179,7 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.Raw func (AppModule) ConsensusVersion() uint64 { return 1 } // BeginBlock contains the logic that is automatically triggered at the beginning of each block -func (am *AppModule) BeginBlock(ctx sdk.Context) error { +func (am *AppModule) BeginBlock(ctx context.Context) error { am.keeper.BeginBlocker(ctx) return nil } From ffb9de70f099662cf1a5a91fd65c53022e1698a9 Mon Sep 17 00:00:00 2001 From: valli0x Date: Wed, 16 Oct 2024 18:25:24 +0300 Subject: [PATCH 02/20] init-test-node.sh replace galacticad to build/galacticad --- init-test-node.sh | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/init-test-node.sh b/init-test-node.sh index b9a17f8..d8f03bc 100755 --- a/init-test-node.sh +++ b/init-test-node.sh @@ -43,13 +43,13 @@ HOME_DIR=${HOME}/.galactica/ rm -rf ~/.galactica* # Import keys from mnemonics -echo $VAL_MNEMONIC | ./galacticad keys add $VAL_KEY --recover --keyring-backend test --algo "eth_secp256k1" -echo $USER1_MNEMONIC | ./galacticad keys add $USER1_KEY --recover --keyring-backend test --algo "eth_secp256k1" -echo $USER2_MNEMONIC | ./galacticad keys add $USER2_KEY --recover --keyring-backend test --algo "eth_secp256k1" -echo $USER3_MNEMONIC | ./galacticad keys add $USER3_KEY --recover --keyring-backend test --algo "eth_secp256k1" -echo $USER4_MNEMONIC | ./galacticad keys add $USER4_KEY --recover --keyring-backend test --algo "eth_secp256k1" +echo $VAL_MNEMONIC | build/galacticad keys add $VAL_KEY --recover --keyring-backend test --algo "eth_secp256k1" +echo $USER1_MNEMONIC | build/galacticad keys add $USER1_KEY --recover --keyring-backend test --algo "eth_secp256k1" +echo $USER2_MNEMONIC | build/galacticad keys add $USER2_KEY --recover --keyring-backend test --algo "eth_secp256k1" +echo $USER3_MNEMONIC | build/galacticad keys add $USER3_KEY --recover --keyring-backend test --algo "eth_secp256k1" +echo $USER4_MNEMONIC | build/galacticad keys add $USER4_KEY --recover --keyring-backend test --algo "eth_secp256k1" -./galacticad init $MONIKER --chain-id $CHAINID +build/galacticad init $MONIKER --chain-id $CHAINID # Set gas limit in genesis cat ${HOME_DIR}/config/genesis.json | jq '.consensus["params"]["block"]["max_gas"]="10000000"' > ${HOME_DIR}/config/tmp_genesis.json && mv ${HOME_DIR}/config/tmp_genesis.json ${HOME_DIR}/config/genesis.json @@ -70,22 +70,22 @@ else fi # Allocate genesis accounts (cosmos formatted addresses) -./galacticad add-genesis-account "$(./galacticad keys show $VAL_KEY -a --keyring-backend test)" 1000000000000000000000aphoton,1000000000000000000000agnet,1000000000000000000stake --keyring-backend test -./galacticad add-genesis-account "$(./galacticad keys show $USER1_KEY -a --keyring-backend test)" 1000000000000000000000aphoton,1000000000000000000000agnet,1000000000000000000stake --keyring-backend test -./galacticad add-genesis-account "$(./galacticad keys show $USER2_KEY -a --keyring-backend test)" 1000000000000000000000aphoton,1000000000000000000000agnet,1000000000000000000stake --keyring-backend test -./galacticad add-genesis-account "$(./galacticad keys show $USER3_KEY -a --keyring-backend test)" 1000000000000000000000aphoton,1000000000000000000000agnet,1000000000000000000stake --keyring-backend test -./galacticad add-genesis-account "$(./galacticad keys show $USER4_KEY -a --keyring-backend test)" 1000000000000000000000aphoton,1000000000000000000000agnet,1000000000000000000stake --keyring-backend test +build/galacticad add-genesis-account "$(build/galacticad keys show $VAL_KEY -a --keyring-backend test)" 1000000000000000000000aphoton,1000000000000000000000agnet,1000000000000000000stake --keyring-backend test +build/galacticad add-genesis-account "$(build/galacticad keys show $USER1_KEY -a --keyring-backend test)" 1000000000000000000000aphoton,1000000000000000000000agnet,1000000000000000000stake --keyring-backend test +build/galacticad add-genesis-account "$(build/galacticad keys show $USER2_KEY -a --keyring-backend test)" 1000000000000000000000aphoton,1000000000000000000000agnet,1000000000000000000stake --keyring-backend test +build/galacticad add-genesis-account "$(build/galacticad keys show $USER3_KEY -a --keyring-backend test)" 1000000000000000000000aphoton,1000000000000000000000agnet,1000000000000000000stake --keyring-backend test +build/galacticad add-genesis-account "$(build/galacticad keys show $USER4_KEY -a --keyring-backend test)" 1000000000000000000000aphoton,1000000000000000000000agnet,1000000000000000000stake --keyring-backend test # Sign genesis transaction echo "gentx command" -./galacticad gentx $VAL_KEY 1000000000000000000stake --amount=1000000000000000000000aphoton --chain-id $CHAINID --keyring-backend test +build/galacticad gentx $VAL_KEY 1000000000000000000stake --amount=1000000000000000000000aphoton --chain-id $CHAINID --keyring-backend test # Collect genesis tx -./galacticad collect-gentxs +build/galacticad collect-gentxs # Run this to ensure everything worked and that the genesis file is setup correctly -./galacticad validate-genesis +build/galacticad validate-genesis # Start the node (remove the --pruning=nothing flag if historical queries are not needed) -# ./galacticad start --keyring-backend test +# build/galacticad start --keyring-backend test # --rpc.unsafe --log_level info --json-rpc.api eth,txpool,personal,net,debug,web3 --api.enable From 9e5c0e19a0453c2de7d6f9edaadbc9da5486f4ed Mon Sep 17 00:00:00 2001 From: valli0x Date: Mon, 21 Oct 2024 13:26:49 +0300 Subject: [PATCH 03/20] v1.4.0-rc0 cronos dependencies --- Makefile | 2 +- app/app.go | 5 + cmd/galacticad/cmd/config.go | 17 + cmd/galacticad/cmd/root.go | 19 +- go.mod | 55 +- go.sum | 99 +- memiavl/README.md | 129 +++ memiavl/benchmark_test.go | 235 +++++ memiavl/changeset.pb.go | 597 ++++++++++++ memiavl/commit_info.pb.go | 804 ++++++++++++++++ memiavl/db.go | 1094 +++++++++++++++++++++ memiavl/db_test.go | 508 ++++++++++ memiavl/export.go | 146 +++ memiavl/filelock.go | 28 + memiavl/go.mod | 65 ++ memiavl/go.sum | 250 +++++ memiavl/import.go | 263 +++++ memiavl/iterator.go | 114 +++ memiavl/iterator_test.go | 60 ++ memiavl/kv.pb.go | 558 +++++++++++ memiavl/layout_little_endian.go | 85 ++ memiavl/layout_native.go | 125 +++ memiavl/mem_node.go | 218 +++++ memiavl/mmap.go | 60 ++ memiavl/multitree.go | 462 +++++++++ memiavl/node.go | 201 ++++ memiavl/persisted_node.go | 248 +++++ memiavl/proof.go | 194 ++++ memiavl/proof_test.go | 59 ++ memiavl/snapshot.go | 585 ++++++++++++ memiavl/snapshot_test.go | 193 ++++ memiavl/tree.go | 295 ++++++ memiavl/tree_test.go | 271 ++++++ memiavl/types.go | 34 + memiavl/wal.go | 100 ++ memiavl/wal.pb.go | 1109 ++++++++++++++++++++++ memiavl/wal_test.go | 45 + store/cachemulti/store.go | 37 + store/config/config.go | 32 + store/config/toml.go | 32 + store/go.mod | 170 ++++ store/go.sum | 1002 +++++++++++++++++++ store/memiavlstore/store.go | 213 +++++ store/rootmulti/import.go | 91 ++ store/rootmulti/objstore.go | 33 + store/rootmulti/objstore_placeholder.go | 15 + store/rootmulti/snapshot.go | 70 ++ store/rootmulti/store.go | 662 +++++++++++++ store/rootmulti/store_test.go | 14 + store/setup.go | 71 ++ versiondb/README.md | 109 +++ versiondb/backend_test_utils.go | 276 ++++++ versiondb/client/changeset.go | 363 +++++++ versiondb/client/changeset_test.go | 187 ++++ versiondb/client/cmd.go | 33 + versiondb/client/convert_to_sst.go | 291 ++++++ versiondb/client/convert_to_sst_test.go | 56 ++ versiondb/client/dump.go | 342 +++++++ versiondb/client/flags.go | 30 + versiondb/client/ingest_sst.go | 60 ++ versiondb/client/print.go | 73 ++ versiondb/client/restore.go | 121 +++ versiondb/client/restore_app_db.go | 334 +++++++ versiondb/client/stores.go | 35 + versiondb/client/to_versiondb.go | 46 + versiondb/client/verify.go | 315 ++++++ versiondb/client/wrapreader.go | 46 + versiondb/extsort/delta_encoding.go | 89 ++ versiondb/extsort/delta_encoding_test.go | 51 + versiondb/extsort/merge.go | 110 +++ versiondb/extsort/sort.go | 253 +++++ versiondb/extsort/sort_test.go | 71 ++ versiondb/extsort/types.go | 8 + versiondb/go.mod | 174 ++++ versiondb/go.sum | 1039 ++++++++++++++++++++ versiondb/multistore.go | 159 ++++ versiondb/objstore.go | 11 + versiondb/store.go | 88 ++ versiondb/streaming_service.go | 33 + versiondb/tsrocksdb/comparator.go | 59 ++ versiondb/tsrocksdb/iterator.go | 147 +++ versiondb/tsrocksdb/opts.go | 85 ++ versiondb/tsrocksdb/store.go | 290 ++++++ versiondb/tsrocksdb/store_test.go | 155 +++ versiondb/types.go | 34 + 85 files changed, 17293 insertions(+), 54 deletions(-) create mode 100644 memiavl/README.md create mode 100644 memiavl/benchmark_test.go create mode 100644 memiavl/changeset.pb.go create mode 100644 memiavl/commit_info.pb.go create mode 100644 memiavl/db.go create mode 100644 memiavl/db_test.go create mode 100644 memiavl/export.go create mode 100644 memiavl/filelock.go create mode 100644 memiavl/go.mod create mode 100644 memiavl/go.sum create mode 100644 memiavl/import.go create mode 100644 memiavl/iterator.go create mode 100644 memiavl/iterator_test.go create mode 100644 memiavl/kv.pb.go create mode 100644 memiavl/layout_little_endian.go create mode 100644 memiavl/layout_native.go create mode 100644 memiavl/mem_node.go create mode 100644 memiavl/mmap.go create mode 100644 memiavl/multitree.go create mode 100644 memiavl/node.go create mode 100644 memiavl/persisted_node.go create mode 100644 memiavl/proof.go create mode 100644 memiavl/proof_test.go create mode 100644 memiavl/snapshot.go create mode 100644 memiavl/snapshot_test.go create mode 100644 memiavl/tree.go create mode 100644 memiavl/tree_test.go create mode 100644 memiavl/types.go create mode 100644 memiavl/wal.go create mode 100644 memiavl/wal.pb.go create mode 100644 memiavl/wal_test.go create mode 100644 store/cachemulti/store.go create mode 100644 store/config/config.go create mode 100644 store/config/toml.go create mode 100644 store/go.mod create mode 100644 store/go.sum create mode 100644 store/memiavlstore/store.go create mode 100644 store/rootmulti/import.go create mode 100644 store/rootmulti/objstore.go create mode 100644 store/rootmulti/objstore_placeholder.go create mode 100644 store/rootmulti/snapshot.go create mode 100644 store/rootmulti/store.go create mode 100644 store/rootmulti/store_test.go create mode 100644 store/setup.go create mode 100644 versiondb/README.md create mode 100644 versiondb/backend_test_utils.go create mode 100644 versiondb/client/changeset.go create mode 100644 versiondb/client/changeset_test.go create mode 100644 versiondb/client/cmd.go create mode 100644 versiondb/client/convert_to_sst.go create mode 100644 versiondb/client/convert_to_sst_test.go create mode 100644 versiondb/client/dump.go create mode 100644 versiondb/client/flags.go create mode 100644 versiondb/client/ingest_sst.go create mode 100644 versiondb/client/print.go create mode 100644 versiondb/client/restore.go create mode 100644 versiondb/client/restore_app_db.go create mode 100644 versiondb/client/stores.go create mode 100644 versiondb/client/to_versiondb.go create mode 100644 versiondb/client/verify.go create mode 100644 versiondb/client/wrapreader.go create mode 100644 versiondb/extsort/delta_encoding.go create mode 100644 versiondb/extsort/delta_encoding_test.go create mode 100644 versiondb/extsort/merge.go create mode 100644 versiondb/extsort/sort.go create mode 100644 versiondb/extsort/sort_test.go create mode 100644 versiondb/extsort/types.go create mode 100644 versiondb/go.mod create mode 100644 versiondb/go.sum create mode 100644 versiondb/multistore.go create mode 100644 versiondb/objstore.go create mode 100644 versiondb/store.go create mode 100644 versiondb/streaming_service.go create mode 100644 versiondb/tsrocksdb/comparator.go create mode 100644 versiondb/tsrocksdb/iterator.go create mode 100644 versiondb/tsrocksdb/opts.go create mode 100644 versiondb/tsrocksdb/store.go create mode 100644 versiondb/tsrocksdb/store_test.go create mode 100644 versiondb/types.go diff --git a/Makefile b/Makefile index 70ea05a..373ba8c 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,7 @@ default_target: all # process build tags -build_tags = netgo +build_tags = netgo objstore ifeq ($(LEDGER_ENABLED),true) ifeq ($(OS),Windows_NT) GCCEXE = $(shell where gcc.exe 2> NUL) diff --git a/app/app.go b/app/app.go index e694fc1..8f9bd40 100644 --- a/app/app.go +++ b/app/app.go @@ -136,6 +136,8 @@ import ( capabilitytypes "github.com/cosmos/ibc-go/modules/capability/types" icatypes "github.com/cosmos/ibc-go/v8/modules/apps/27-interchain-accounts/types" + memiavlstore "github.com/crypto-org-chain/cronos/store" + "github.com/cosmos/cosmos-sdk/client/flags" ) const ( @@ -397,6 +399,9 @@ func New( // } // baseAppOptions = append(baseAppOptions, prepareOpt) + homePath := cast.ToString(appOpts.Get(flags.FlagHome)) + baseAppOptions = memiavlstore.SetupMemIAVL(logger, homePath, appOpts, false, false, 0, baseAppOptions) + app.App = appBuilder.Build(db, traceStore, baseAppOptions...) encConfig := MakeEncodingConfig() diff --git a/cmd/galacticad/cmd/config.go b/cmd/galacticad/cmd/config.go index 0ad392f..1dc6d34 100644 --- a/cmd/galacticad/cmd/config.go +++ b/cmd/galacticad/cmd/config.go @@ -119,3 +119,20 @@ func VerifyAddressFormat(bz []byte) error { return nil } + +type VersionDBConfig struct { + // Enable defines if the versiondb should be enabled. + Enable bool `mapstructure:"enable"` +} + +func DefaultVersionDBConfig() VersionDBConfig { + return VersionDBConfig{ + Enable: false, + } +} + +var DefaultVersionDBTemplate = ` +[versiondb] +# Enable defines if the versiondb should be enabled. +enable = {{ .VersionDB.Enable }} +` \ No newline at end of file diff --git a/cmd/galacticad/cmd/root.go b/cmd/galacticad/cmd/root.go index ee9b327..496d484 100644 --- a/cmd/galacticad/cmd/root.go +++ b/cmd/galacticad/cmd/root.go @@ -65,18 +65,20 @@ import ( // ethertypes "github.com/evmos/ethermint/types" - "github.com/Galactica-corp/galactica/app" - authtxconfig "github.com/cosmos/cosmos-sdk/x/auth/tx/config" - txsign "github.com/cosmos/cosmos-sdk/types/tx/signing" - authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" "slices" + + confixcmd "cosmossdk.io/tools/confix/cmd" + "github.com/Galactica-corp/galactica/app" appparams "github.com/Galactica-corp/galactica/app/params" "github.com/Galactica-corp/galactica/cmd/galacticad/cmd/ethkeys" dbm "github.com/cosmos/cosmos-db" "github.com/cosmos/cosmos-sdk/crypto/keyring" simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" + txsign "github.com/cosmos/cosmos-sdk/types/tx/signing" + authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" + authtxconfig "github.com/cosmos/cosmos-sdk/x/auth/tx/config" + memiavlcfg "github.com/crypto-org-chain/cronos/store/config" evmenc "github.com/evmos/ethermint/encoding" - confixcmd "cosmossdk.io/tools/confix/cmd" ) // NewRootCmd creates a new root command for a Cosmos SDK application @@ -431,6 +433,9 @@ func initAppConfig() (string, interface{}) { type CustomAppConfig struct { ethermintconfig.Config + + MemIAVL memiavlcfg.MemIAVLConfig `mapstructure:"memiavl"` + VersionDB VersionDBConfig `mapstructure:"versiondb"` } // Optionally allow the chain developer to overwrite the SDK's default @@ -452,7 +457,9 @@ func initAppConfig() (string, interface{}) { srvCfg.JSONRPC.API = ethermintconfig.GetAPINamespaces() customAppConfig := CustomAppConfig{ - Config: *srvCfg, + Config: *srvCfg, + MemIAVL: memiavlcfg.DefaultMemIAVLConfig(), + VersionDB: DefaultVersionDBConfig(), } customAppTemplate := serverconfig.DefaultConfigTemplate + ethermintconfig.DefaultConfigTemplate diff --git a/go.mod b/go.mod index 68d59d8..8baa31d 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( cosmossdk.io/x/tx v0.13.3 cosmossdk.io/x/upgrade v0.1.1 github.com/bufbuild/buf v1.23.1 - github.com/cometbft/cometbft v0.38.10 + github.com/cometbft/cometbft v0.38.11 github.com/cosmos/cosmos-db v1.0.2 github.com/cosmos/cosmos-proto v1.0.0-beta.5 github.com/cosmos/cosmos-sdk v0.50.6 @@ -26,6 +26,7 @@ require ( github.com/cosmos/ibc-go/modules/capability v1.0.0 github.com/cosmos/ibc-go/v8 v8.2.1 github.com/cosmos/rosetta v0.50.3-1 + github.com/crypto-org-chain/cronos/store v0.0.0-00010101000000-000000000000 github.com/ethereum/go-ethereum v1.10.26 github.com/evmos/ethermint v0.0.0-00010101000000-000000000000 github.com/golang/protobuf v1.5.4 @@ -38,7 +39,7 @@ require ( github.com/stretchr/testify v1.9.0 google.golang.org/genproto/googleapis/api v0.0.0-20240521202816-d264139d666e google.golang.org/grpc v1.64.0 - google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0 + google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0 google.golang.org/protobuf v1.34.1 ) @@ -59,6 +60,7 @@ require ( github.com/Microsoft/go-winio v0.6.2 // indirect github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect github.com/VictoriaMetrics/fastcache v1.6.0 // indirect + github.com/alitto/pond v1.8.3 // indirect github.com/aws/aws-sdk-go v1.53.10 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect @@ -86,14 +88,15 @@ require ( github.com/cosmos/btcutil v1.0.5 // indirect github.com/cosmos/go-bip39 v1.0.0 // indirect github.com/cosmos/gogogateway v1.2.0 // indirect - github.com/cosmos/iavl v1.0.1 // indirect + github.com/cosmos/iavl v1.1.2 // indirect github.com/cosmos/ics23/go v0.10.0 // indirect github.com/cosmos/ledger-cosmos-go v0.13.3 // indirect github.com/cosmos/rosetta-sdk-go v0.10.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect github.com/creachadair/atomicfile v0.3.1 // indirect github.com/creachadair/tomledit v0.0.24 // indirect - github.com/crypto-org-chain/go-block-stm v0.0.0-20240408011717-9f11af197bde // indirect + github.com/crypto-org-chain/cronos/memiavl v0.0.4 // indirect + github.com/crypto-org-chain/go-block-stm v0.0.0-20240912024944-1cd89976aa5e // indirect github.com/danieljoos/wincred v1.2.1 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/deckarep/golang-set/v2 v2.1.0 // indirect @@ -180,6 +183,7 @@ require ( github.com/klauspost/pgzip v1.2.6 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect + github.com/ledgerwatch/erigon-lib v0.0.0-20230210071639-db0e7ed11263 // indirect github.com/lib/pq v1.10.9 // indirect github.com/linxGnu/grocksdb v1.9.1 // indirect github.com/magiconair/properties v1.8.7 // indirect @@ -205,9 +209,9 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/pkg/profile v1.7.0 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/prometheus/client_golang v1.18.0 // indirect + github.com/prometheus/client_golang v1.19.0 // indirect github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/common v0.47.0 // indirect + github.com/prometheus/common v0.52.2 // indirect github.com/prometheus/procfs v0.15.0 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/rivo/uniseg v0.2.0 // indirect @@ -233,11 +237,14 @@ require ( github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.0 // indirect github.com/tidwall/sjson v1.2.5 // indirect + github.com/tidwall/tinylru v1.1.0 // indirect + github.com/tidwall/wal v1.1.7 // indirect github.com/tklauser/go-sysconf v0.3.10 // indirect github.com/tklauser/numcpus v0.4.0 // indirect github.com/tyler-smith/go-bip39 v1.1.0 // indirect github.com/ulikunitz/xz v0.5.12 // indirect github.com/vbatts/tar-split v0.11.3 // indirect + github.com/zbiljic/go-filelock v0.0.0-20170914061330-1dbf7103ab7d // indirect github.com/zondax/hid v0.9.2 // indirect github.com/zondax/ledger-go v0.14.3 // indirect go.etcd.io/bbolt v1.4.0-alpha.0.0.20240404170359-43604f3112c5 // indirect @@ -274,20 +281,38 @@ require ( sigs.k8s.io/yaml v1.4.0 // indirect ) +// release/v0.50.x replace ( - cosmossdk.io/store => github.com/crypto-org-chain/cosmos-sdk/store v0.0.0-20240415105151-0108877a3201 - cosmossdk.io/x/tx => github.com/crypto-org-chain/cosmos-sdk/x/tx v0.0.0-20240415105151-0108877a3201 - // use cosmos fork of keyring - //github.com/99designs/keyring => github.com/cosmos/keyring v1.2.0 + cosmossdk.io/client/v2 => github.com/crypto-org-chain/cosmos-sdk/client/v2 v2.0.0-20240911084450-6870ba130be2 + cosmossdk.io/store => github.com/crypto-org-chain/cosmos-sdk/store v0.0.0-20240911084450-6870ba130be2 + cosmossdk.io/x/tx => github.com/crypto-org-chain/cosmos-sdk/x/tx v0.0.0-20240911084450-6870ba130be2 + github.com/cosmos/cosmos-sdk => github.com/crypto-org-chain/cosmos-sdk v0.50.6-0.20240911084450-6870ba130be2 +) + +replace ( + github.com/crypto-org-chain/cronos/memiavl => ./memiavl + github.com/crypto-org-chain/cronos/store => ./store + github.com/crypto-org-chain/cronos/versiondb => ./versiondb +) + +replace ( + // Use cosmos keyring github.com/99designs/keyring => github.com/cosmos/keyring v1.2.0 + + // for go-ethereum github.com/cockroachdb/pebble => github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811 - // use cometbft github.com/cometbft/cometbft-db => github.com/crypto-org-chain/cometbft-db v0.0.0-20231011055109-57922ac52a63 - github.com/cosmos/cosmos-sdk => github.com/crypto-org-chain/cosmos-sdk v0.0.0-20240415105151-0108877a3201 + // dgrijalva/jwt-go is deprecated and doesn't receive security updates. + // TODO: remove it: https://github.com/cosmos/cosmos-sdk/issues/13134 + github.com/dgrijalva/jwt-go => github.com/golang-jwt/jwt/v4 v4.4.2 github.com/ethereum/go-ethereum => github.com/crypto-org-chain/go-ethereum v1.10.20-0.20240425065928-ebb09502e7a7 - github.com/evmos/ethermint => github.com/crypto-org-chain/ethermint v0.6.1-0.20240502052908-179e436703b3 - // replace broken goleveldb + // develop + github.com/evmos/ethermint => github.com/crypto-org-chain/ethermint v0.6.1-0.20240913100216-dbc7eb41488c + + // Fix upstream GHSA-h395-qcrw-5vmq and GHSA-3vp4-m3rf-835h vulnerabilities. + // TODO Remove it: https://github.com/cosmos/cosmos-sdk/issues/10409 + github.com/gin-gonic/gin => github.com/gin-gonic/gin v1.9.0 + github.com/jhump/protoreflect => github.com/jhump/protoreflect v1.9.0 github.com/syndtr/goleveldb => github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 github.com/tidwall/btree => github.com/crypto-org-chain/btree v0.0.0-20240406140148-2687063b042c - ) diff --git a/go.sum b/go.sum index e0d89c2..db03c64 100644 --- a/go.sum +++ b/go.sum @@ -188,8 +188,6 @@ cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1V cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M= cosmossdk.io/api v0.7.5 h1:eMPTReoNmGUm8DeiQL9DyM8sYDjEhWzL1+nLbI9DqtQ= cosmossdk.io/api v0.7.5/go.mod h1:IcxpYS5fMemZGqyYtErK7OqvdM0C8kdW3dq8Q/XIG38= -cosmossdk.io/client/v2 v2.0.0-beta.1 h1:XkHh1lhrLYIT9zKl7cIOXUXg2hdhtjTPBUfqERNA1/Q= -cosmossdk.io/client/v2 v2.0.0-beta.1/go.mod h1:JEUSu9moNZQ4kU3ir1DKD5eU4bllmAexrGWjmb9k8qU= cosmossdk.io/collections v0.4.0 h1:PFmwj2W8szgpD5nOd8GWH6AbYNi1f2J6akWXJ7P5t9s= cosmossdk.io/collections v0.4.0/go.mod h1:oa5lUING2dP+gdDquow+QjlF45eL1t4TJDypgGd+tv0= cosmossdk.io/core v0.11.0 h1:vtIafqUi+1ZNAE/oxLOQQ7Oek2n4S48SWLG8h/+wdbo= @@ -253,6 +251,8 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/alitto/pond v1.8.3 h1:ydIqygCLVPqIX/USe5EaV/aSRXTRXDEI9JwuDdu+/xs= +github.com/alitto/pond v1.8.3/go.mod h1:CmvIIGd5jKLasGI3D87qDkQxjzChdKMmnXMg3fG6M6Q= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/allegro/bigcache v1.2.1 h1:hg1sY1raCwic3Vnsvje6TT7/pnZba83LeFck5NrFKSc= github.com/allegro/bigcache v1.2.1/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= @@ -317,6 +317,8 @@ github.com/bufbuild/connect-opentelemetry-go v0.3.0 h1:AuZi3asTDKmjGtd2aqpyP4p5Q github.com/bufbuild/connect-opentelemetry-go v0.3.0/go.mod h1:r1ppyTtu1EWeRodk4Q/JbyQhIWtO7eR3GoRDzjeEcNU= github.com/bufbuild/protocompile v0.6.0 h1:Uu7WiSQ6Yj9DbkdnOe7U4mNKp58y9WDMKDn28/ZlunY= github.com/bufbuild/protocompile v0.6.0/go.mod h1:YNP35qEYoYGme7QMtz5SBCoN4kL4g12jTtjuzRNdjpE= +github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= +github.com/bytedance/sonic v1.8.0/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= @@ -332,6 +334,8 @@ github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL 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/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s= +github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM= github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= @@ -370,8 +374,8 @@ github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZ github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/coinbase/rosetta-sdk-go/types v1.0.0 h1:jpVIwLcPoOeCR6o1tU+Xv7r5bMONNbHU7MuEHboiFuA= github.com/coinbase/rosetta-sdk-go/types v1.0.0/go.mod h1:eq7W2TMRH22GTW0N0beDnN931DW0/WOI1R2sdHNHG4c= -github.com/cometbft/cometbft v0.38.10 h1:2ePuglchT+j0Iao+cfmt/nw5U7K2lnGDzXSUPGVdXaU= -github.com/cometbft/cometbft v0.38.10/go.mod h1:jHPx9vQpWzPHEAiYI/7EDKaB1NXhK6o3SArrrY8ExKc= +github.com/cometbft/cometbft v0.38.11 h1:6bNDUB8/xq4uYonYwIfGc9OqK1ZH4NkdaMmR1LZIJqk= +github.com/cometbft/cometbft v0.38.11/go.mod h1:jHPx9vQpWzPHEAiYI/7EDKaB1NXhK6o3SArrrY8ExKc= github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg= github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM= github.com/containerd/stargz-snapshotter/estargz v0.14.3 h1:OqlDCK3ZVUO6C3B/5FSkDwbkEETK84kQgEeFwDC+62k= @@ -395,8 +399,8 @@ github.com/cosmos/gogogateway v1.2.0/go.mod h1:iQpLkGWxYcnCdz5iAdLcRBSw3h7NXeOkZ github.com/cosmos/gogoproto v1.4.2/go.mod h1:cLxOsn1ljAHSV527CHOtaIP91kK6cCrZETRBrkzItWU= github.com/cosmos/gogoproto v1.4.12 h1:vB6Lbe/rtnYGjQuFxkPiPYiCybqFT8QvLipDZP8JpFE= github.com/cosmos/gogoproto v1.4.12/go.mod h1:LnZob1bXRdUoqMMtwYlcR3wjiElmlC+FkjaZRv1/eLY= -github.com/cosmos/iavl v1.0.1 h1:D+mYbcRO2wptYzOM1Hxl9cpmmHU1ZEt9T2Wv5nZTeUw= -github.com/cosmos/iavl v1.0.1/go.mod h1:8xIUkgVvwvVrBu81scdPty+/Dx9GqwHnAvXz4cwF7RY= +github.com/cosmos/iavl v1.1.2 h1:zL9FK7C4L/P4IF1Dm5fIwz0WXCnn7Bp1M2FxH0ayM7Y= +github.com/cosmos/iavl v1.1.2/go.mod h1:jLeUvm6bGT1YutCaL2fIar/8vGUE8cPZvh/gXEWDaDM= github.com/cosmos/ibc-go/modules/capability v1.0.0 h1:r/l++byFtn7jHYa09zlAdSeevo8ci1mVZNO9+V0xsLE= github.com/cosmos/ibc-go/modules/capability v1.0.0/go.mod h1:D81ZxzjZAe0ZO5ambnvn1qedsFQ8lOwtqicG6liLBco= github.com/cosmos/ibc-go/v8 v8.2.1 h1:MTsnZZjxvGD4Fv5pYyx5UkELafSX0rlPt6IfsE2BpTQ= @@ -428,16 +432,18 @@ github.com/crypto-org-chain/btree v0.0.0-20240406140148-2687063b042c h1:MOgfS4+F github.com/crypto-org-chain/btree v0.0.0-20240406140148-2687063b042c/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY= github.com/crypto-org-chain/cometbft-db v0.0.0-20231011055109-57922ac52a63 h1:R1QJ9a3XdYMSKo+1RdFifxb/g3lNypC52L/rpYrWoKo= github.com/crypto-org-chain/cometbft-db v0.0.0-20231011055109-57922ac52a63/go.mod h1:rocwIfnS+kA060x64gkSIRvWB9StSppIkJuo5MWzL24= -github.com/crypto-org-chain/cosmos-sdk v0.0.0-20240415105151-0108877a3201 h1:3R54xSBI4geLX3H5Ljk0FSVDMPRpywq5L5K//aHwo8s= -github.com/crypto-org-chain/cosmos-sdk v0.0.0-20240415105151-0108877a3201/go.mod h1:DkCxCPi3veciROiq36PbpXDhboMjqHAS0Xyv2dEEW04= -github.com/crypto-org-chain/cosmos-sdk/store v0.0.0-20240415105151-0108877a3201 h1:0T8U5tgQLfD8k8kxisez5ks9s7yxU2JSRhi5MUQ0Cp0= -github.com/crypto-org-chain/cosmos-sdk/store v0.0.0-20240415105151-0108877a3201/go.mod h1:lfuLI1f4o+0SGtlHQS4x5qsjRcZZfYqG8bp3k8hM0M8= -github.com/crypto-org-chain/cosmos-sdk/x/tx v0.0.0-20240415105151-0108877a3201 h1:DbCOM19ywdL5K+bOy4h+0MppzcPgI2guHnYCfDNnAcM= -github.com/crypto-org-chain/cosmos-sdk/x/tx v0.0.0-20240415105151-0108877a3201/go.mod h1:CBCU6fsRVz23QGFIQBb1DNX2DztJCf3jWyEkHY2nJQ0= -github.com/crypto-org-chain/ethermint v0.6.1-0.20240502052908-179e436703b3 h1:YYmMJowZyiyioNHYnps5hw3XkV1zcXSC3jy/xzqK2Rg= -github.com/crypto-org-chain/ethermint v0.6.1-0.20240502052908-179e436703b3/go.mod h1:9MVSajfKloRP8h2chP78LhCKx5u9O2pCMBvxrmx6+0s= -github.com/crypto-org-chain/go-block-stm v0.0.0-20240408011717-9f11af197bde h1:sQIHTJfVt5VTrF7po9eZiFkZiPjlHbFvnXtGCOoBjNM= -github.com/crypto-org-chain/go-block-stm v0.0.0-20240408011717-9f11af197bde/go.mod h1:iwQTX9xMX8NV9k3o2BiWXA0SswpsZrDk5q3gA7nWYiE= +github.com/crypto-org-chain/cosmos-sdk v0.50.6-0.20240911084450-6870ba130be2 h1:4SoAvnxDaiIWcgm6XOmPDIdCf4/WNhNYLXGbij1eaA0= +github.com/crypto-org-chain/cosmos-sdk v0.50.6-0.20240911084450-6870ba130be2/go.mod h1:Rb43DdB0i/rKcCN69Tg2X3+zA4WhJ7MC8K3a6Ezh38E= +github.com/crypto-org-chain/cosmos-sdk/client/v2 v2.0.0-20240911084450-6870ba130be2 h1:5oGZtOUcauk9dtv+8BCfj2PEQyXEEEV+K3sP4OSvBmg= +github.com/crypto-org-chain/cosmos-sdk/client/v2 v2.0.0-20240911084450-6870ba130be2/go.mod h1:W5sR4asmVDUhJpEmuXTUBkk/yEefKlXTjVWcNciVSR0= +github.com/crypto-org-chain/cosmos-sdk/store v0.0.0-20240911084450-6870ba130be2 h1:CGh5I0L6IYhe0AJevb4vf5TE3ru+qAgMs437BlWCwo8= +github.com/crypto-org-chain/cosmos-sdk/store v0.0.0-20240911084450-6870ba130be2/go.mod h1:gjE3DZe4t/+VeIk6CmrouyqiuDbZ7QOVDDq3nLqBTpg= +github.com/crypto-org-chain/cosmos-sdk/x/tx v0.0.0-20240911084450-6870ba130be2 h1:mxlOSCru7YgmX055rrlkCSUu0D8lAqJ8Dnhp0yXCBuM= +github.com/crypto-org-chain/cosmos-sdk/x/tx v0.0.0-20240911084450-6870ba130be2/go.mod h1:RTiTs4hkXG6IvYGknvB8p79YgjYJdcbzLUOGJChsPnY= +github.com/crypto-org-chain/ethermint v0.6.1-0.20240913100216-dbc7eb41488c h1:pJJNL+ZganmfcxzEijVNqwNDhzXsTyMk/Of1/lUvxlM= +github.com/crypto-org-chain/ethermint v0.6.1-0.20240913100216-dbc7eb41488c/go.mod h1:D2lnc8ARuVmgc2/2IWla2Ky1o8/pjmyrnIt+d46Clco= +github.com/crypto-org-chain/go-block-stm v0.0.0-20240912024944-1cd89976aa5e h1:FFpE6+Y4o5GxkeGwUcETM6amgohh7msWvWf1MDqueVc= +github.com/crypto-org-chain/go-block-stm v0.0.0-20240912024944-1cd89976aa5e/go.mod h1:iwQTX9xMX8NV9k3o2BiWXA0SswpsZrDk5q3gA7nWYiE= github.com/crypto-org-chain/go-ethereum v1.10.20-0.20240425065928-ebb09502e7a7 h1:V43F3JFcqG4MUThf9W/DytnPblpR6CcaLBw2Wx6zTgE= github.com/crypto-org-chain/go-ethereum v1.10.20-0.20240425065928-ebb09502e7a7/go.mod h1:+a8pUj1tOyJ2RinsNQD4326YS+leSoKGiG/uVVb0x6Y= github.com/danieljoos/wincred v1.2.1 h1:dl9cBrupW8+r5250DYkYxocLeZ1Y4vB1kxgtjxw8GQs= @@ -463,7 +469,6 @@ github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= @@ -539,7 +544,7 @@ github.com/getsentry/sentry-go v0.28.0/go.mod h1:1fQZ+7l7eeJ3wYi82q5Hg8GqAPgefRq github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= +github.com/gin-gonic/gin v1.9.0/go.mod h1:W1Me9+hsUSyj3CePGrd1/QrKJMSJ1Tu/0hFEH89961k= github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0= github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= @@ -567,10 +572,10 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= -github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= -github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= -github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.11.2/go.mod h1:NieE624vt4SCTJtD87arVLvdmjPAeV8BQlHtMnw9D7s= github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU= github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= @@ -580,6 +585,7 @@ github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= +github.com/goccy/go-json v0.10.0/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0= github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -599,6 +605,7 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v4 v4.3.0 h1:kHL1vqdqWNfATmA0FNMdmZNMyZI1U6O31X4rlIPoBog= github.com/golang-jwt/jwt/v4 v4.3.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= +github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.2.1 h1:OptwRhECazUx5ix5TTWC3EZhsZEHWcYWY4FQHTIubm4= github.com/golang/glog v1.2.1/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= @@ -828,8 +835,8 @@ github.com/jdxcode/netrc v0.0.0-20221124155335-4616370d1a84 h1:2uT3aivO7NVpUPGcQ github.com/jdxcode/netrc v0.0.0-20221124155335-4616370d1a84/go.mod h1:Zi/ZFkEqFHTm7qkjyNJjaWH4LQA9LQhGJyF0lTYGpxw= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jhump/protoreflect v1.15.3 h1:6SFRuqU45u9hIZPJAoZ8c28T3nK64BNdp9w6jFonzls= -github.com/jhump/protoreflect v1.15.3/go.mod h1:4ORHmSBmlCW8fh3xHmJMGyul1zNqZK4Elxc8qKP+p1k= +github.com/jhump/protoreflect v1.9.0 h1:npqHz788dryJiR/l6K/RUQAyh2SwV91+d1dnh4RjO9w= +github.com/jhump/protoreflect v1.9.0/go.mod h1:7GcYQDdMU/O/BBrl/cX6PNHpXh6cenjd8pneu5yW7Tg= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= @@ -845,6 +852,7 @@ github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= @@ -861,6 +869,7 @@ github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8 github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU= github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU= github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -877,7 +886,9 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/ledgerwatch/erigon-lib v0.0.0-20230210071639-db0e7ed11263 h1:LGEzZvf33Y1NhuP5+jI/ni9l1TFS6oYPDilgy74NusM= +github.com/ledgerwatch/erigon-lib v0.0.0-20230210071639-db0e7ed11263/go.mod h1:OXgMDuUo2lZ3NpH29ZvMYbk+LxFd5ffDl2Z2mGMuY/I= +github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= @@ -900,6 +911,7 @@ github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNx github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= @@ -933,6 +945,7 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/mtibben/percent v0.2.1 h1:5gssi8Nqo8QU/r2pynCm+hBQHpkB/uNK7BJCFogWdzs= @@ -998,6 +1011,7 @@ github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0Mw github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= 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/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= @@ -1028,8 +1042,8 @@ github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5Fsn github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk= -github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA= +github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU= +github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -1044,8 +1058,8 @@ github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt2 github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= -github.com/prometheus/common v0.47.0 h1:p5Cz0FNHo7SnWOmWmoRozVcjEp0bIVU8cV7OShpjL1k= -github.com/prometheus/common v0.47.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= +github.com/prometheus/common v0.52.2 h1:LW8Vk7BccEdONfrJBDffQGRtpSzi5CQaRZGtboOO2ck= +github.com/prometheus/common v0.52.2/go.mod h1:lrWtQx+iDfn2mbH5GUzlH9TSHyfZpHkSiG1W7y3sF2Q= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= @@ -1063,6 +1077,7 @@ github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6So github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= @@ -1156,6 +1171,7 @@ github.com/test-go/testify v1.1.4 h1:Tf9lntrKUMHiXQ07qBScBTSA0dhYQlu83hswqelv1iE github.com/test-go/testify v1.1.4/go.mod h1:rH7cfJo/47vWGdi4GPj16x3/t1xGOj2YxzmNQzk2ghU= github.com/tetratelabs/wazero v1.2.1 h1:J4X2hrGzJvt+wqltuvcSjHQ7ujQxA9gb6PeMs4qlUWs= github.com/tetratelabs/wazero v1.2.1/go.mod h1:wYx2gNRg8/WihJfSDxA1TIL8H+GkfLYm+bIfbblu9VQ= +github.com/tidwall/gjson v1.10.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM= github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= @@ -1165,17 +1181,21 @@ github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= +github.com/tidwall/tinylru v1.1.0 h1:XY6IUfzVTU9rpwdhKUF6nQdChgCdGjkMfLzbWyiau6I= +github.com/tidwall/tinylru v1.1.0/go.mod h1:3+bX+TJ2baOLMWTnlyNWHh4QMnFyARg2TLTQ6OFbzw8= +github.com/tidwall/wal v1.1.7 h1:emc1TRjIVsdKKSnpwGBAcsAGg0767SvUk8+ygx7Bb+4= +github.com/tidwall/wal v1.1.7/go.mod h1:r6lR1j27W9EPalgHiB7zLJDYu3mzW5BQP5KrzBpYY/E= github.com/tklauser/go-sysconf v0.3.10 h1:IJ1AZGZRWbY8T5Vfk04D9WOA5WSejdflXxP03OUqALw= github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk= github.com/tklauser/numcpus v0.4.0 h1:E53Dm1HjH1/R2/aoCtXtPgzmElmn51aOkhCFSuZq//o= github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= -github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= -github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/ugorji/go/codec v1.2.9/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc= github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= @@ -1197,6 +1217,8 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/zbiljic/go-filelock v0.0.0-20170914061330-1dbf7103ab7d h1:XQyeLr7N9iY9mi+TGgsBFkj54+j3fdoo8e2u6zrGP5A= +github.com/zbiljic/go-filelock v0.0.0-20170914061330-1dbf7103ab7d/go.mod h1:hoMeDjlNXTNqVwrCk8YDyaBS2g5vFfEX2ezMi4vb6CY= github.com/zondax/hid v0.9.2 h1:WCJFnEDMiqGF64nlZz28E9qLVZ0KSJ7xpc5DLEyma2U= github.com/zondax/hid v0.9.2/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= github.com/zondax/ledger-go v0.14.3 h1:wEpJt2CEcBJ428md/5MgSLsXLBos98sBOyxNmCjfUCw= @@ -1251,6 +1273,7 @@ go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -1263,6 +1286,7 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1366,6 +1390,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -1511,6 +1537,8 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220906165534-d0df966e6959/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= @@ -1518,6 +1546,8 @@ golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1529,7 +1559,10 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1826,8 +1859,9 @@ google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCD google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0 h1:M1YKkFIboKNieVO5DLUEVzQfGwJD30Nv2jfUgzb5UcE= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0 h1:TLkBREm4nIsEcexnCjgQd5GQWaHcqMzwQV0TX9pq8S0= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0/go.mod h1:DNq5QpG7LJqD2AamLZ7zvKE0DEpVl2BSEVjFycAAjRY= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 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= @@ -1897,6 +1931,7 @@ nhooyr.io/websocket v1.8.11/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+ pgregory.net/rapid v1.1.0 h1:CMa0sjHSru3puNx+J0MIAuiiEV4N0qj8/cMWGBBCsjw= pgregory.net/rapid v1.1.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= diff --git a/memiavl/README.md b/memiavl/README.md new file mode 100644 index 0000000..3fcf033 --- /dev/null +++ b/memiavl/README.md @@ -0,0 +1,129 @@ +# Alternative IAVL Implementation + +## Changelog + +* 11 Jan 2023: Initial version +* 13 Jan 2023: Change changeset encoding from protobuf to plain one +* 17 Jan 2023: + * Add delete field to change set to support empty value + * Add section about compression on snapshot format +* 27 Jan 2023: + * Update metadata file format + * Encode key length with 4 bytes instead of 2. +* 24 Feb 2023: + * Reduce node size without hash from 32bytes to 16bytes, leverage properties of post-order traversal. + * Merge key-values into single kvs file, build optional MPHF hash table to index it. + + +## The Journey + +It started for an use case of verifying the state change sets, we need to replay the change sets to rebuild IAVL tree and check the final IAVL root hash, compare the root hash with the on-chain hash to verify the integrity of the change sets. + +The first implementation keeps the whole IAVL tree in memory, mutate nodes in-place, and don't update hashes for the intermediate versions, and one insight from the test run is it runs surprisingly fast. For the distribution store in our testnet, it can process from genesis to block `6698242` in 2 minutes, which is around `55818` blocks per second. + +To support incremental replay, we further designed an IAVL snapshot format that's stored on disk, while supporting random access with mmap, which solves the memory usage issue, and reduce the time of replaying. + +## New Design + +So the new idea is we can put the snapshot and change sets together, the change sets is the write-ahead-log for the IAVL tree. + +It also integrates well with versiondb, because versiondb can also be derived from change sets to provide query service. IAVL tree is only used for consensus state machine and merkle proof generations. + +### Advantages + +- Better write amplification, we only need to write the change sets in real time which is much more compact than IAVL nodes, IAVL snapshot can be created in much lower frequency. +- Better read amplification, the IAVL snapshot is a plain file, the nodes are referenced with offset, the read amplification is simply 1. +- Better space amplification, the archived change sets are much more compact than current IAVL tree, in our test case, the ratio could be as large as 1:100. We don't need to keep too old IAVL snapshots, because versiondb will handle the historical key-value queries, IAVL tree only takes care of merkle proof generations for blocks within an unbonding period. In very rare cases that do need IAVL tree of very old version, you can always replay the change sets from the genesis. + +## File Formats + +> NOTICE: the integers are always encoded with little endianness. + +### Change Set File + +``` +version: 8 +size: 8 // size of whole payload +payload: + delete: 1 + keyLen: varint-uint64 + key + [ // if delete is false + valueLen: varint-uint64 + value + ] + +repeat with next version +``` + +- Change set files can be splited with certain block ranges for incremental backup and restoration. + +- Historical files can be compressed with zlib, because it doesn't need to support random access. + +### IAVL Snapshot + +IAVL snapshot is composed by four files: + +- `metadata`, 16bytes: + + ``` + magic: 4 + format: 4 + version: 4 + root node index: 4 + ``` + +- `nodes`, array of fixed size(16+32bytes) nodes, the node format is like this: + + ``` + # branch + height : 1 + _padding : 3 + version : 4 + size : 4 + key node : 4 + hash : [32]byte + + # leaf + height : 1 + _padding : 3 + version : 4 + key offset : 8 + hash : [32]byte + ``` + The node has fixed length, can be indexed directly. The nodes references each other with the node index, nodes are written with post-order depth-first traversal, so the root node is always placed at the end. + + For branch node, the `key node` field reference the smallest leaf node in the right branch, the key slice is fetched from there indirectly, the leaf nodes stores the `offset` into the `kvs` file, where the key and value slices can be built. + + The branch node's left/child node indexes are inferenced from existing information and properties of post-order traversal: + + ``` + right child index = self index - 1 + left child index = key node - 1 + ``` + + The version/size/node indexes are encoded with 4 bytes, should be enough in foreseeable future, but could be changed to more bytes in the future. + + The implementation will read the mmap-ed content in a zero-copy way, won't use extra node cache, it will only rely on the OS page cache. + +- `kvs`, sequence of leaf node key-value pairs, the keys are ordered and no duplication. + + ``` + keyLen: varint-uint64 + key + valueLen: varint-uint64 + value + *repeat* + ``` + +#### Compression + +The items in snapshot reference with each other by file offsets, we can apply some block compression techniques to compress keys and values files while maintain random accessibility by uncompressed file offset, for example zstd's experimental seekable format[^1]. + +### VersionDB + +[VersionDB](../README.md) is to support query and iterating historical versions of key-values pairs, currently implemented with rocksdb's experimental user-defined timestamp feature, support query and iterate key-value pairs by version, it's an alternative way to support grpc query service, and much more compact than IAVL trees, similar in size with the compressed change set files. + +After versiondb is fully integrated, IAVL tree don't need to serve queries at all, it don't need to store the values at all, just store the value hashes would be enough. + +[^1]: https://github.com/facebook/zstd/blob/dev/contrib/seekable_format/zstd_seekable_compression_format.md diff --git a/memiavl/benchmark_test.go b/memiavl/benchmark_test.go new file mode 100644 index 0000000..71255ff --- /dev/null +++ b/memiavl/benchmark_test.go @@ -0,0 +1,235 @@ +package memiavl + +import ( + "bytes" + "encoding/binary" + "math/rand" + "sort" + "testing" + + iavlcache "github.com/cosmos/iavl/cache" + "github.com/stretchr/testify/require" + "github.com/tidwall/btree" +) + +func BenchmarkByteCompare(b *testing.B) { + var x, y [32]byte + for i := 0; i < b.N; i++ { + _ = bytes.Compare(x[:], y[:]) + } +} + +func BenchmarkRandomGet(b *testing.B) { + amount := 1000000 + items := genRandItems(amount) + targetKey := items[500].key + targetValue := items[500].value + targetItem := itemT{key: targetKey} + + tree := New(0) + for _, item := range items { + tree.set(item.key, item.value) + } + + snapshotDir := b.TempDir() + err := tree.WriteSnapshot(snapshotDir) + require.NoError(b, err) + snapshot, err := OpenSnapshot(snapshotDir) + require.NoError(b, err) + defer snapshot.Close() + + b.Run("memiavl", func(b *testing.B) { + require.Equal(b, targetValue, tree.Get(targetKey)) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = tree.Get(targetKey) + } + }) + b.Run("memiavl-disk", func(b *testing.B) { + diskTree := NewFromSnapshot(snapshot, true, 0) + require.Equal(b, targetValue, diskTree.Get(targetKey)) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = diskTree.Get(targetKey) + } + }) + b.Run("memiavl-disk-cache-hit", func(b *testing.B) { + diskTree := NewFromSnapshot(snapshot, true, 1) + require.Equal(b, targetValue, diskTree.Get(targetKey)) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = diskTree.Get(targetKey) + } + }) + b.Run("memiavl-disk-cache-miss", func(b *testing.B) { + diskTree := NewFromSnapshot(snapshot, true, 0) + // enforce an empty cache to emulate cache miss + diskTree.cache = iavlcache.New(0) + require.Equal(b, targetValue, diskTree.Get(targetKey)) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = diskTree.Get(targetKey) + } + }) + b.Run("btree-degree-2", func(b *testing.B) { + bt2 := btree.NewBTreeGOptions(lessG, btree.Options{ + NoLocks: true, + Degree: 2, + }) + for _, item := range items { + bt2.Set(item) + } + v, _ := bt2.Get(targetItem) + require.Equal(b, targetValue, v.value) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, _ = bt2.Get(targetItem) + } + }) + b.Run("btree-degree-32", func(b *testing.B) { + bt32 := btree.NewBTreeGOptions(lessG, btree.Options{ + NoLocks: true, + Degree: 32, + }) + for _, item := range items { + bt32.Set(item) + } + v, _ := bt32.Get(targetItem) + require.Equal(b, targetValue, v.value) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, _ = bt32.Get(targetItem) + } + }) + b.Run("iavl-lru", func(b *testing.B) { + cache := iavlcache.New(amount) + for _, item := range items { + cache.Add(NewIavlCacheNode(item.key, item.value)) + } + v := cache.Get(targetItem.key).(iavlCacheNode).value + require.Equal(b, targetValue, v) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = cache.Get(targetKey).(iavlCacheNode).value + } + }) + b.Run("go-map", func(b *testing.B) { + m := make(map[string][]byte, amount) + for _, item := range items { + m[string(item.key)] = item.value + } + v := m[string(targetItem.key)] + require.Equal(b, targetValue, v) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = m[string(targetKey)] + } + }) + + b.Run("binary-search", func(b *testing.B) { + // the last benchmark sort the items in place + sort.Slice(items, func(i, j int) bool { + return bytes.Compare(items[i].key, items[j].key) < 0 + }) + cmp := func(i int) bool { return bytes.Compare(items[i].key, targetKey) != -1 } + i := sort.Search(len(items), cmp) + require.Equal(b, targetValue, items[i].value) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + n := sort.Search(len(items), cmp) + _ = items[n].value + } + }) +} + +func BenchmarkRandomSet(b *testing.B) { + items := genRandItems(1000000) + b.ResetTimer() + b.Run("memiavl", func(b *testing.B) { + for i := 0; i < b.N; i++ { + tree := New(0) + for _, item := range items { + tree.set(item.key, item.value) + } + } + }) + b.Run("tree2", func(b *testing.B) { + for i := 0; i < b.N; i++ { + bt := btree.NewBTreeGOptions(lessG, btree.Options{ + NoLocks: true, + Degree: 2, + }) + for _, item := range items { + bt.Set(item) + } + } + }) + b.Run("tree32", func(b *testing.B) { + for i := 0; i < b.N; i++ { + bt := btree.NewBTreeGOptions(lessG, btree.Options{ + NoLocks: true, + Degree: 32, + }) + for _, item := range items { + bt.Set(item) + } + } + }) +} + +type itemT struct { + key, value []byte +} + +func lessG(a, b itemT) bool { + return bytes.Compare(a.key, b.key) == -1 +} + +func int64ToItemT(n uint64) itemT { + var key, value [8]byte + binary.BigEndian.PutUint64(key[:], n) + binary.LittleEndian.PutUint64(value[:], n) + return itemT{ + key: key[:], + value: value[:], + } +} + +func genRandItems(n int) []itemT { + r := rand.New(rand.NewSource(0)) + items := make([]itemT, n) + itemsM := make(map[uint64]bool) + for i := 0; i < n; i++ { + for { + key := uint64(r.Int63n(10000000000000000)) + if !itemsM[key] { + itemsM[key] = true + items[i] = int64ToItemT(key) + break + } + } + } + return items +} + +type iavlCacheNode struct { + key []byte + value []byte +} + +func NewIavlCacheNode(key, value []byte) iavlCacheNode { + return iavlCacheNode{key, value} +} + +func (n iavlCacheNode) GetKey() []byte { + return n.key +} diff --git a/memiavl/changeset.pb.go b/memiavl/changeset.pb.go new file mode 100644 index 0000000..dc986df --- /dev/null +++ b/memiavl/changeset.pb.go @@ -0,0 +1,597 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: memiavl/changeset.proto + +package memiavl + +import ( + fmt "fmt" + proto "github.com/cosmos/gogoproto/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// KVPair represents a key-value pair +type KVPair struct { + Delete bool `protobuf:"varint,1,opt,name=delete,proto3" json:"delete,omitempty"` + Key []byte `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"` + Value []byte `protobuf:"bytes,3,opt,name=value,proto3" json:"value,omitempty"` +} + +func (m *KVPair) Reset() { *m = KVPair{} } +func (m *KVPair) String() string { return proto.CompactTextString(m) } +func (*KVPair) ProtoMessage() {} +func (*KVPair) Descriptor() ([]byte, []int) { + return fileDescriptor_54242fa334002fa1, []int{0} +} +func (m *KVPair) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *KVPair) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_KVPair.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *KVPair) XXX_Merge(src proto.Message) { + xxx_messageInfo_KVPair.Merge(m, src) +} +func (m *KVPair) XXX_Size() int { + return m.Size() +} +func (m *KVPair) XXX_DiscardUnknown() { + xxx_messageInfo_KVPair.DiscardUnknown(m) +} + +var xxx_messageInfo_KVPair proto.InternalMessageInfo + +func (m *KVPair) GetDelete() bool { + if m != nil { + return m.Delete + } + return false +} + +func (m *KVPair) GetKey() []byte { + if m != nil { + return m.Key + } + return nil +} + +func (m *KVPair) GetValue() []byte { + if m != nil { + return m.Value + } + return nil +} + +// ChangeSet represents a list of key-value pairs +type ChangeSet struct { + Pairs []*KVPair `protobuf:"bytes,1,rep,name=pairs,proto3" json:"pairs,omitempty"` +} + +func (m *ChangeSet) Reset() { *m = ChangeSet{} } +func (m *ChangeSet) String() string { return proto.CompactTextString(m) } +func (*ChangeSet) ProtoMessage() {} +func (*ChangeSet) Descriptor() ([]byte, []int) { + return fileDescriptor_54242fa334002fa1, []int{1} +} +func (m *ChangeSet) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ChangeSet) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ChangeSet.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ChangeSet) XXX_Merge(src proto.Message) { + xxx_messageInfo_ChangeSet.Merge(m, src) +} +func (m *ChangeSet) XXX_Size() int { + return m.Size() +} +func (m *ChangeSet) XXX_DiscardUnknown() { + xxx_messageInfo_ChangeSet.DiscardUnknown(m) +} + +var xxx_messageInfo_ChangeSet proto.InternalMessageInfo + +func (m *ChangeSet) GetPairs() []*KVPair { + if m != nil { + return m.Pairs + } + return nil +} + +func init() { + proto.RegisterType((*KVPair)(nil), "memiavl.KVPair") + proto.RegisterType((*ChangeSet)(nil), "memiavl.ChangeSet") +} + +func init() { proto.RegisterFile("memiavl/changeset.proto", fileDescriptor_54242fa334002fa1) } + +var fileDescriptor_54242fa334002fa1 = []byte{ + // 220 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0xcf, 0x4d, 0xcd, 0xcd, + 0x4c, 0x2c, 0xcb, 0xd1, 0x4f, 0xce, 0x48, 0xcc, 0x4b, 0x4f, 0x2d, 0x4e, 0x2d, 0xd1, 0x2b, 0x28, + 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x87, 0x4a, 0x28, 0x79, 0x70, 0xb1, 0x79, 0x87, 0x05, 0x24, 0x66, + 0x16, 0x09, 0x89, 0x71, 0xb1, 0xa5, 0xa4, 0xe6, 0xa4, 0x96, 0xa4, 0x4a, 0x30, 0x2a, 0x30, 0x6a, + 0x70, 0x04, 0x41, 0x79, 0x42, 0x02, 0x5c, 0xcc, 0xd9, 0xa9, 0x95, 0x12, 0x4c, 0x0a, 0x8c, 0x1a, + 0x3c, 0x41, 0x20, 0xa6, 0x90, 0x08, 0x17, 0x6b, 0x59, 0x62, 0x4e, 0x69, 0xaa, 0x04, 0x33, 0x58, + 0x0c, 0xc2, 0x51, 0x32, 0xe2, 0xe2, 0x74, 0x06, 0xdb, 0x12, 0x9c, 0x5a, 0x22, 0xa4, 0xca, 0xc5, + 0x5a, 0x90, 0x98, 0x59, 0x54, 0x2c, 0xc1, 0xa8, 0xc0, 0xac, 0xc1, 0x6d, 0xc4, 0xaf, 0x07, 0xb5, + 0x4f, 0x0f, 0x62, 0x59, 0x10, 0x44, 0xd6, 0xc9, 0xe5, 0xc4, 0x23, 0x39, 0xc6, 0x0b, 0x8f, 0xe4, + 0x18, 0x1f, 0x3c, 0x92, 0x63, 0x9c, 0xf0, 0x58, 0x8e, 0xe1, 0xc2, 0x63, 0x39, 0x86, 0x1b, 0x8f, + 0xe5, 0x18, 0xa2, 0xb4, 0xd2, 0x33, 0x4b, 0x32, 0x4a, 0x93, 0xf4, 0x92, 0xf3, 0x73, 0xf5, 0x93, + 0x8b, 0x2a, 0x0b, 0x4a, 0xf2, 0x75, 0xf3, 0x8b, 0xd2, 0x75, 0x93, 0x33, 0x12, 0x33, 0xf3, 0xf4, + 0x93, 0x8b, 0xf2, 0xf3, 0xf2, 0x8b, 0xf5, 0xa1, 0x66, 0x26, 0xb1, 0x81, 0xfd, 0x64, 0x0c, 0x08, + 0x00, 0x00, 0xff, 0xff, 0xb1, 0xcb, 0xb3, 0xdd, 0xee, 0x00, 0x00, 0x00, +} + +func (m *KVPair) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *KVPair) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *KVPair) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Value) > 0 { + i -= len(m.Value) + copy(dAtA[i:], m.Value) + i = encodeVarintChangeset(dAtA, i, uint64(len(m.Value))) + i-- + dAtA[i] = 0x1a + } + if len(m.Key) > 0 { + i -= len(m.Key) + copy(dAtA[i:], m.Key) + i = encodeVarintChangeset(dAtA, i, uint64(len(m.Key))) + i-- + dAtA[i] = 0x12 + } + if m.Delete { + i-- + if m.Delete { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *ChangeSet) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ChangeSet) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ChangeSet) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Pairs) > 0 { + for iNdEx := len(m.Pairs) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Pairs[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintChangeset(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func encodeVarintChangeset(dAtA []byte, offset int, v uint64) int { + offset -= sovChangeset(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *KVPair) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Delete { + n += 2 + } + l = len(m.Key) + if l > 0 { + n += 1 + l + sovChangeset(uint64(l)) + } + l = len(m.Value) + if l > 0 { + n += 1 + l + sovChangeset(uint64(l)) + } + return n +} + +func (m *ChangeSet) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Pairs) > 0 { + for _, e := range m.Pairs { + l = e.Size() + n += 1 + l + sovChangeset(uint64(l)) + } + } + return n +} + +func sovChangeset(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozChangeset(x uint64) (n int) { + return sovChangeset(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *KVPair) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowChangeset + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: KVPair: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: KVPair: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Delete", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowChangeset + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Delete = bool(v != 0) + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowChangeset + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthChangeset + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthChangeset + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Key = append(m.Key[:0], dAtA[iNdEx:postIndex]...) + if m.Key == nil { + m.Key = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowChangeset + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthChangeset + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthChangeset + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Value = append(m.Value[:0], dAtA[iNdEx:postIndex]...) + if m.Value == nil { + m.Value = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipChangeset(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthChangeset + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ChangeSet) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowChangeset + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ChangeSet: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ChangeSet: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pairs", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowChangeset + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthChangeset + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthChangeset + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Pairs = append(m.Pairs, &KVPair{}) + if err := m.Pairs[len(m.Pairs)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipChangeset(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthChangeset + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipChangeset(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowChangeset + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowChangeset + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowChangeset + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthChangeset + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupChangeset + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthChangeset + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthChangeset = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowChangeset = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupChangeset = fmt.Errorf("proto: unexpected end of group") +) diff --git a/memiavl/commit_info.pb.go b/memiavl/commit_info.pb.go new file mode 100644 index 0000000..d18637a --- /dev/null +++ b/memiavl/commit_info.pb.go @@ -0,0 +1,804 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: memiavl/commit_info.proto + +package memiavl + +import ( + fmt "fmt" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// CommitInfo defines commit information used by the multi-store when committing +// a version/height. +type CommitInfo struct { + Version int64 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"` + StoreInfos []StoreInfo `protobuf:"bytes,2,rep,name=store_infos,json=storeInfos,proto3" json:"store_infos"` +} + +func (m *CommitInfo) Reset() { *m = CommitInfo{} } +func (m *CommitInfo) String() string { return proto.CompactTextString(m) } +func (*CommitInfo) ProtoMessage() {} +func (*CommitInfo) Descriptor() ([]byte, []int) { + return fileDescriptor_11dece027dbaaf68, []int{0} +} +func (m *CommitInfo) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CommitInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CommitInfo.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *CommitInfo) XXX_Merge(src proto.Message) { + xxx_messageInfo_CommitInfo.Merge(m, src) +} +func (m *CommitInfo) XXX_Size() int { + return m.Size() +} +func (m *CommitInfo) XXX_DiscardUnknown() { + xxx_messageInfo_CommitInfo.DiscardUnknown(m) +} + +var xxx_messageInfo_CommitInfo proto.InternalMessageInfo + +func (m *CommitInfo) GetVersion() int64 { + if m != nil { + return m.Version + } + return 0 +} + +func (m *CommitInfo) GetStoreInfos() []StoreInfo { + if m != nil { + return m.StoreInfos + } + return nil +} + +// StoreInfo defines store-specific commit information. It contains a reference +// between a store name and the commit ID. +type StoreInfo struct { + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + CommitId CommitID `protobuf:"bytes,2,opt,name=commit_id,json=commitId,proto3" json:"commit_id"` +} + +func (m *StoreInfo) Reset() { *m = StoreInfo{} } +func (m *StoreInfo) String() string { return proto.CompactTextString(m) } +func (*StoreInfo) ProtoMessage() {} +func (*StoreInfo) Descriptor() ([]byte, []int) { + return fileDescriptor_11dece027dbaaf68, []int{1} +} +func (m *StoreInfo) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *StoreInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_StoreInfo.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *StoreInfo) XXX_Merge(src proto.Message) { + xxx_messageInfo_StoreInfo.Merge(m, src) +} +func (m *StoreInfo) XXX_Size() int { + return m.Size() +} +func (m *StoreInfo) XXX_DiscardUnknown() { + xxx_messageInfo_StoreInfo.DiscardUnknown(m) +} + +var xxx_messageInfo_StoreInfo proto.InternalMessageInfo + +func (m *StoreInfo) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *StoreInfo) GetCommitId() CommitID { + if m != nil { + return m.CommitId + } + return CommitID{} +} + +// CommitID defines the committment information when a specific store is +// committed. +type CommitID struct { + Version int64 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"` + Hash []byte `protobuf:"bytes,2,opt,name=hash,proto3" json:"hash,omitempty"` +} + +func (m *CommitID) Reset() { *m = CommitID{} } +func (*CommitID) ProtoMessage() {} +func (*CommitID) Descriptor() ([]byte, []int) { + return fileDescriptor_11dece027dbaaf68, []int{2} +} +func (m *CommitID) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CommitID) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CommitID.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *CommitID) XXX_Merge(src proto.Message) { + xxx_messageInfo_CommitID.Merge(m, src) +} +func (m *CommitID) XXX_Size() int { + return m.Size() +} +func (m *CommitID) XXX_DiscardUnknown() { + xxx_messageInfo_CommitID.DiscardUnknown(m) +} + +var xxx_messageInfo_CommitID proto.InternalMessageInfo + +func (m *CommitID) GetVersion() int64 { + if m != nil { + return m.Version + } + return 0 +} + +func (m *CommitID) GetHash() []byte { + if m != nil { + return m.Hash + } + return nil +} + +func init() { + proto.RegisterType((*CommitInfo)(nil), "memiavl.CommitInfo") + proto.RegisterType((*StoreInfo)(nil), "memiavl.StoreInfo") + proto.RegisterType((*CommitID)(nil), "memiavl.CommitID") +} + +func init() { proto.RegisterFile("memiavl/commit_info.proto", fileDescriptor_11dece027dbaaf68) } + +var fileDescriptor_11dece027dbaaf68 = []byte{ + // 286 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x90, 0xb1, 0x4e, 0xc3, 0x30, + 0x14, 0x45, 0xe3, 0x36, 0xa2, 0xed, 0x0b, 0x0b, 0x16, 0x43, 0x60, 0x70, 0xa3, 0x4c, 0x11, 0x52, + 0x13, 0xa9, 0xb0, 0xc0, 0xc0, 0x50, 0xba, 0x74, 0x0d, 0x62, 0x61, 0x41, 0x69, 0x9a, 0x26, 0x96, + 0xb0, 0x5f, 0x65, 0x87, 0x4a, 0xfc, 0x05, 0x23, 0x23, 0x9f, 0xd3, 0xb1, 0x23, 0x13, 0x42, 0xc9, + 0x8f, 0xa0, 0xb8, 0x49, 0x47, 0xb6, 0xeb, 0x7b, 0xec, 0xa3, 0x2b, 0xc3, 0x85, 0xc8, 0x04, 0x4f, + 0xb6, 0xaf, 0x51, 0x8a, 0x42, 0xf0, 0xf2, 0x85, 0xcb, 0x35, 0x86, 0x1b, 0x85, 0x25, 0xd2, 0x41, + 0x8b, 0x2e, 0xcf, 0x73, 0xcc, 0xd1, 0x74, 0x51, 0x93, 0x0e, 0xd8, 0x4f, 0x00, 0x1e, 0xcc, 0x9b, + 0x85, 0x5c, 0x23, 0x75, 0x61, 0xb0, 0xcd, 0x94, 0xe6, 0x28, 0x5d, 0xe2, 0x91, 0xa0, 0x1f, 0x77, + 0x47, 0x7a, 0x0b, 0x8e, 0x2e, 0x51, 0x65, 0x46, 0xad, 0xdd, 0x9e, 0xd7, 0x0f, 0x9c, 0x29, 0x0d, + 0x5b, 0x79, 0xf8, 0xd8, 0xb0, 0x46, 0x31, 0xb3, 0x77, 0x3f, 0x63, 0x2b, 0x06, 0xdd, 0x15, 0xda, + 0x7f, 0x82, 0xd1, 0x11, 0x53, 0x0a, 0xb6, 0x4c, 0x44, 0x66, 0xf4, 0xa3, 0xd8, 0x64, 0x7a, 0x03, + 0xa3, 0x6e, 0xf7, 0xca, 0xed, 0x79, 0x24, 0x70, 0xa6, 0x67, 0x47, 0x73, 0xbb, 0x6e, 0xde, 0x8a, + 0x87, 0x87, 0x9b, 0x8b, 0x95, 0x7f, 0x0f, 0xc3, 0x8e, 0xfd, 0xb3, 0x9b, 0x82, 0x5d, 0x24, 0xba, + 0x30, 0xda, 0xd3, 0xd8, 0xe4, 0x3b, 0xfb, 0xf3, 0x6b, 0x6c, 0xcd, 0xe6, 0xbb, 0x8a, 0x91, 0x7d, + 0xc5, 0xc8, 0x6f, 0xc5, 0xc8, 0x47, 0xcd, 0xac, 0x7d, 0xcd, 0xac, 0xef, 0x9a, 0x59, 0xcf, 0x57, + 0x39, 0x2f, 0x8b, 0xb7, 0x65, 0x98, 0xa2, 0x88, 0x52, 0xf5, 0xbe, 0x29, 0x71, 0x82, 0x2a, 0x9f, + 0xa4, 0x45, 0xc2, 0x65, 0x94, 0x2a, 0x94, 0xa8, 0xa3, 0x76, 0xde, 0xf2, 0xc4, 0x7c, 0xe3, 0xf5, + 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x25, 0x25, 0xfb, 0x55, 0x82, 0x01, 0x00, 0x00, +} + +func (m *CommitInfo) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *CommitInfo) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CommitInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.StoreInfos) > 0 { + for iNdEx := len(m.StoreInfos) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.StoreInfos[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintCommitInfo(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if m.Version != 0 { + i = encodeVarintCommitInfo(dAtA, i, uint64(m.Version)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *StoreInfo) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *StoreInfo) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *StoreInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.CommitId.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintCommitInfo(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintCommitInfo(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *CommitID) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *CommitID) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CommitID) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Hash) > 0 { + i -= len(m.Hash) + copy(dAtA[i:], m.Hash) + i = encodeVarintCommitInfo(dAtA, i, uint64(len(m.Hash))) + i-- + dAtA[i] = 0x12 + } + if m.Version != 0 { + i = encodeVarintCommitInfo(dAtA, i, uint64(m.Version)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func encodeVarintCommitInfo(dAtA []byte, offset int, v uint64) int { + offset -= sovCommitInfo(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *CommitInfo) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Version != 0 { + n += 1 + sovCommitInfo(uint64(m.Version)) + } + if len(m.StoreInfos) > 0 { + for _, e := range m.StoreInfos { + l = e.Size() + n += 1 + l + sovCommitInfo(uint64(l)) + } + } + return n +} + +func (m *StoreInfo) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + sovCommitInfo(uint64(l)) + } + l = m.CommitId.Size() + n += 1 + l + sovCommitInfo(uint64(l)) + return n +} + +func (m *CommitID) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Version != 0 { + n += 1 + sovCommitInfo(uint64(m.Version)) + } + l = len(m.Hash) + if l > 0 { + n += 1 + l + sovCommitInfo(uint64(l)) + } + return n +} + +func sovCommitInfo(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozCommitInfo(x uint64) (n int) { + return sovCommitInfo(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *CommitInfo) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommitInfo + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: CommitInfo: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CommitInfo: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType) + } + m.Version = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommitInfo + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Version |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field StoreInfos", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommitInfo + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthCommitInfo + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthCommitInfo + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.StoreInfos = append(m.StoreInfos, StoreInfo{}) + if err := m.StoreInfos[len(m.StoreInfos)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipCommitInfo(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthCommitInfo + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *StoreInfo) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommitInfo + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: StoreInfo: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: StoreInfo: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommitInfo + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthCommitInfo + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthCommitInfo + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CommitId", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommitInfo + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthCommitInfo + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthCommitInfo + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.CommitId.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipCommitInfo(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthCommitInfo + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CommitID) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommitInfo + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: CommitID: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CommitID: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType) + } + m.Version = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommitInfo + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Version |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Hash", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommitInfo + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthCommitInfo + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthCommitInfo + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Hash = append(m.Hash[:0], dAtA[iNdEx:postIndex]...) + if m.Hash == nil { + m.Hash = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipCommitInfo(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthCommitInfo + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipCommitInfo(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowCommitInfo + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowCommitInfo + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowCommitInfo + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthCommitInfo + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupCommitInfo + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthCommitInfo + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthCommitInfo = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowCommitInfo = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupCommitInfo = fmt.Errorf("proto: unexpected end of group") +) diff --git a/memiavl/db.go b/memiavl/db.go new file mode 100644 index 0000000..40ae660 --- /dev/null +++ b/memiavl/db.go @@ -0,0 +1,1094 @@ +package memiavl + +import ( + "context" + "errors" + "fmt" + "os" + "path/filepath" + "sort" + "strconv" + "strings" + "sync" + "time" + + "github.com/alitto/pond" + "github.com/tidwall/wal" +) + +const ( + DefaultSnapshotInterval = 1000 + LockFileName = "LOCK" + DefaultSnapshotWriterLimit = 4 + TmpSuffix = "-tmp" +) + +var errReadOnly = errors.New("db is read-only") + +// DB implements DB-like functionalities on top of MultiTree: +// - async snapshot rewriting +// - Write-ahead-log +// +// The memiavl.db directory looks like this: +// ``` +// > current -> snapshot-N +// > snapshot-N +// > bank +// > kvs +// > nodes +// > metadata +// > acc +// > ... other stores +// > wal +// ``` +type DB struct { + MultiTree + dir string + logger Logger + fileLock FileLock + readOnly bool + + // result channel of snapshot rewrite goroutine + snapshotRewriteChan chan snapshotResult + // context cancel function to cancel the snapshot rewrite goroutine + snapshotRewriteCancel context.CancelFunc + + // the number of old snapshots to keep (excluding the latest one) + snapshotKeepRecent uint32 + // block interval to take a new snapshot + snapshotInterval uint32 + // make sure only one snapshot rewrite is running + pruneSnapshotLock sync.Mutex + triggerStateSyncExport func(height int64) + + // invariant: the LastIndex always match the current version of MultiTree + wal *wal.Log + walChanSize int + walChan chan *walEntry + walQuit chan error + + // pending changes, will be written into WAL in next Commit call + pendingLog WALEntry + + // The assumptions to concurrency: + // - The methods on DB are protected by a mutex + // - Each call of Load loads a separate instance, in query scenarios, + // it should be immutable, the cache stores will handle the temporary writes. + // - The DB for the state machine will handle writes through the Commit call, + // this method is the sole entry point for tree modifications, and there's no concurrency internally + // (the background snapshot rewrite is handled separately), so we don't need locks in the Tree. + mtx sync.Mutex + // worker goroutine IdleTimeout = 5s + snapshotWriterPool *pond.WorkerPool +} + +type Options struct { + Logger Logger + CreateIfMissing bool + InitialVersion uint32 + ReadOnly bool + // the initial stores when initialize the empty instance + InitialStores []string + SnapshotKeepRecent uint32 + SnapshotInterval uint32 + TriggerStateSyncExport func(height int64) + // load the target version instead of latest version + TargetVersion uint32 + // Buffer size for the asynchronous commit queue, -1 means synchronous commit, + // default to 0. + AsyncCommitBuffer int + // ZeroCopy if true, the get and iterator methods could return a slice pointing to mmaped blob files. + ZeroCopy bool + // CacheSize defines the cache's max entry size for each memiavl store. + CacheSize int + // LoadForOverwriting if true rollbacks the state, specifically the Load method will + // truncate the versions after the `TargetVersion`, the `TargetVersion` becomes the latest version. + // it do nothing if the target version is `0`. + LoadForOverwriting bool + + SnapshotWriterLimit int +} + +func (opts Options) Validate() error { + if opts.ReadOnly && opts.CreateIfMissing { + return errors.New("can't create db in read-only mode") + } + + if opts.ReadOnly && opts.LoadForOverwriting { + return errors.New("can't rollback db in read-only mode") + } + + return nil +} + +func (opts *Options) FillDefaults() { + if opts.Logger == nil { + opts.Logger = NewNopLogger() + } + + if opts.SnapshotInterval == 0 { + opts.SnapshotInterval = DefaultSnapshotInterval + } + + if opts.SnapshotWriterLimit <= 0 { + opts.SnapshotWriterLimit = DefaultSnapshotWriterLimit + } +} + +const ( + SnapshotPrefix = "snapshot-" + SnapshotDirLen = len(SnapshotPrefix) + 20 +) + +func Load(dir string, opts Options) (*DB, error) { + if err := opts.Validate(); err != nil { + return nil, fmt.Errorf("invalid options: %w", err) + } + opts.FillDefaults() + + if opts.CreateIfMissing { + if err := createDBIfNotExist(dir, opts.InitialVersion); err != nil { + return nil, fmt.Errorf("fail to load db: %w", err) + } + } + + var ( + err error + fileLock FileLock + ) + if !opts.ReadOnly { + fileLock, err = LockFile(filepath.Join(dir, LockFileName)) + if err != nil { + return nil, fmt.Errorf("fail to lock db: %w", err) + } + + // cleanup any temporary directories left by interrupted snapshot rewrite + if err := removeTmpDirs(dir); err != nil { + return nil, fmt.Errorf("fail to cleanup tmp directories: %w", err) + } + } + + snapshot := "current" + if opts.TargetVersion > 0 { + // find the biggest snapshot version that's less than or equal to the target version + snapshotVersion, err := seekSnapshot(dir, opts.TargetVersion) + if err != nil { + return nil, fmt.Errorf("fail to seek snapshot: %w", err) + } + snapshot = snapshotName(snapshotVersion) + } + + path := filepath.Join(dir, snapshot) + mtree, err := LoadMultiTree(path, opts.ZeroCopy, opts.CacheSize) + if err != nil { + return nil, err + } + + wal, err := OpenWAL(walPath(dir), &wal.Options{NoCopy: true, NoSync: true}) + if err != nil { + return nil, err + } + + if opts.TargetVersion == 0 || int64(opts.TargetVersion) > mtree.Version() { + if err := mtree.CatchupWAL(wal, int64(opts.TargetVersion)); err != nil { + return nil, errors.Join(err, wal.Close()) + } + } + + if opts.LoadForOverwriting && opts.TargetVersion > 0 { + currentSnapshot, err := os.Readlink(currentPath(dir)) + if err != nil { + return nil, fmt.Errorf("fail to read current version: %w", err) + } + + if snapshot != currentSnapshot { + // downgrade `"current"` link first + opts.Logger.Info("downgrade current link to", "snapshot", snapshot) + if err := updateCurrentSymlink(dir, snapshot); err != nil { + return nil, fmt.Errorf("fail to update current snapshot link: %w", err) + } + } + + // truncate the WAL + opts.Logger.Info("truncate WAL from back", "version", opts.TargetVersion) + if err := wal.TruncateBack(walIndex(int64(opts.TargetVersion), mtree.initialVersion)); err != nil { + return nil, fmt.Errorf("fail to truncate wal logs: %w", err) + } + + // prune snapshots that's larger than the target version + if err := traverseSnapshots(dir, false, func(version int64) (bool, error) { + if version <= int64(opts.TargetVersion) { + return true, nil + } + + if err := atomicRemoveDir(filepath.Join(dir, snapshotName(version))); err != nil { + opts.Logger.Error("fail to prune snapshot", "version", version, "err", err) + } else { + opts.Logger.Info("prune snapshot", "version", version) + } + return false, nil + }); err != nil { + return nil, fmt.Errorf("fail to prune snapshots: %w", err) + } + } + // create worker pool. recv tasks to write snapshot + workerPool := pond.New(opts.SnapshotWriterLimit, opts.SnapshotWriterLimit*10) + + db := &DB{ + MultiTree: *mtree, + logger: opts.Logger, + dir: dir, + fileLock: fileLock, + readOnly: opts.ReadOnly, + wal: wal, + walChanSize: opts.AsyncCommitBuffer, + snapshotKeepRecent: opts.SnapshotKeepRecent, + snapshotInterval: opts.SnapshotInterval, + triggerStateSyncExport: opts.TriggerStateSyncExport, + snapshotWriterPool: workerPool, + } + + if !db.readOnly && db.Version() == 0 && len(opts.InitialStores) > 0 { + // do the initial upgrade with the `opts.InitialStores` + var upgrades []*TreeNameUpgrade + for _, name := range opts.InitialStores { + upgrades = append(upgrades, &TreeNameUpgrade{Name: name}) + } + if err := db.ApplyUpgrades(upgrades); err != nil { + return nil, errors.Join(err, db.Close()) + } + } + + return db, nil +} + +func removeTmpDirs(rootDir string) error { + entries, err := os.ReadDir(rootDir) + if err != nil { + return err + } + + for _, entry := range entries { + if !entry.IsDir() || !strings.HasSuffix(entry.Name(), TmpSuffix) { + continue + } + + if err := os.RemoveAll(filepath.Join(rootDir, entry.Name())); err != nil { + return err + } + } + + return nil +} + +// ReadOnly returns whether the DB is opened in read-only mode. +func (db *DB) ReadOnly() bool { + return db.readOnly +} + +// SetInitialVersion wraps `MultiTree.SetInitialVersion`. +// it do an immediate snapshot rewrite, because we can't use wal log to record this change, +// because we need it to convert versions to wal index in the first place. +func (db *DB) SetInitialVersion(initialVersion int64) error { + db.mtx.Lock() + defer db.mtx.Unlock() + + if db.readOnly { + return errReadOnly + } + + if db.lastCommitInfo.Version > 0 { + return errors.New("initial version can only be set before any commit") + } + + if err := db.MultiTree.SetInitialVersion(initialVersion); err != nil { + return err + } + + return initEmptyDB(db.dir, db.initialVersion) +} + +// ApplyUpgrades wraps MultiTree.ApplyUpgrades, it also append the upgrades in a pending log, +// which will be persisted to the WAL in next Commit call. +func (db *DB) ApplyUpgrades(upgrades []*TreeNameUpgrade) error { + db.mtx.Lock() + defer db.mtx.Unlock() + + if db.readOnly { + return errReadOnly + } + + if err := db.MultiTree.ApplyUpgrades(upgrades); err != nil { + return err + } + + db.pendingLog.Upgrades = append(db.pendingLog.Upgrades, upgrades...) + return nil +} + +// ApplyChangeSets wraps MultiTree.ApplyChangeSets, it also append the changesets in the pending log, +// which will be persisted to the WAL in next Commit call. +func (db *DB) ApplyChangeSets(changeSets []*NamedChangeSet) error { + if len(changeSets) == 0 { + return nil + } + + db.mtx.Lock() + defer db.mtx.Unlock() + + if db.readOnly { + return errReadOnly + } + + if len(db.pendingLog.Changesets) == 0 { + db.pendingLog.Changesets = changeSets + } else { + // slow path, merge into exist changesets one by one + for _, cs := range changeSets { + if err := db.applyChangeSet(cs.Name, cs.Changeset); err != nil { + return err + } + } + } + + return db.MultiTree.ApplyChangeSets(changeSets) +} + +// ApplyChangeSet wraps MultiTree.ApplyChangeSet, it also append the changesets in the pending log, +// which will be persisted to the WAL in next Commit call. +func (db *DB) ApplyChangeSet(name string, changeSet ChangeSet) error { + if len(changeSet.Pairs) == 0 { + return nil + } + + db.mtx.Lock() + defer db.mtx.Unlock() + + return db.applyChangeSet(name, changeSet) +} + +func (db *DB) applyChangeSet(name string, changeSet ChangeSet) error { + if len(changeSet.Pairs) == 0 { + return nil + } + + if db.readOnly { + return errReadOnly + } + + var updated bool + for _, cs := range db.pendingLog.Changesets { + if cs.Name == name { + cs.Changeset.Pairs = append(cs.Changeset.Pairs, changeSet.Pairs...) + updated = true + break + } + } + + if !updated { + db.pendingLog.Changesets = append(db.pendingLog.Changesets, &NamedChangeSet{ + Name: name, + Changeset: changeSet, + }) + sort.SliceStable(db.pendingLog.Changesets, func(i, j int) bool { + return db.pendingLog.Changesets[i].Name < db.pendingLog.Changesets[j].Name + }) + } + + return db.MultiTree.ApplyChangeSet(name, changeSet) +} + +// checkAsyncTasks checks the status of background tasks non-blocking-ly and process the result +func (db *DB) checkAsyncTasks() error { + return errors.Join( + db.checkAsyncCommit(), + db.checkBackgroundSnapshotRewrite(), + ) +} + +// checkAsyncCommit check the quit signal of async wal writing +func (db *DB) checkAsyncCommit() error { + select { + case err := <-db.walQuit: + // async wal writing failed, we need to abort the state machine + return fmt.Errorf("async wal writing goroutine quit unexpectedly: %w", err) + default: + } + + return nil +} + +// CommittedVersion returns the latest version written in wal, or snapshot version if wal is empty. +func (db *DB) CommittedVersion() (int64, error) { + lastIndex, err := db.wal.LastIndex() + if err != nil { + return 0, err + } + if lastIndex == 0 { + return db.SnapshotVersion(), nil + } + return walVersion(lastIndex, db.initialVersion), nil +} + +// checkBackgroundSnapshotRewrite check the result of background snapshot rewrite, cleans up the old snapshots and switches to a new multitree +func (db *DB) checkBackgroundSnapshotRewrite() error { + // check the completeness of background snapshot rewriting + select { + case result := <-db.snapshotRewriteChan: + db.snapshotRewriteChan = nil + db.snapshotRewriteCancel = nil + + if result.mtree == nil { + // background snapshot rewrite failed + return fmt.Errorf("background snapshot rewriting failed: %w", result.err) + } + + // wait for potential pending wal writings to finish, to make sure we catch up to latest state. + // in real world, block execution should be slower than wal writing, so this should not block for long. + for { + committedVersion, err := db.CommittedVersion() + if err != nil { + return fmt.Errorf("get wal version failed: %w", err) + } + if db.lastCommitInfo.Version == committedVersion { + break + } + time.Sleep(time.Nanosecond) + } + + // catchup the remaining wal + if err := result.mtree.CatchupWAL(db.wal, 0); err != nil { + return fmt.Errorf("catchup failed: %w", err) + } + + // do the switch + if err := db.reloadMultiTree(result.mtree); err != nil { + return fmt.Errorf("switch multitree failed: %w", err) + } + db.logger.Info("switched to new snapshot", "version", db.MultiTree.Version()) + + db.pruneSnapshots() + + // trigger state-sync snapshot export + if db.triggerStateSyncExport != nil { + db.triggerStateSyncExport(db.SnapshotVersion()) + } + default: + } + + return nil +} + +// pruneSnapshot prune the old snapshots +func (db *DB) pruneSnapshots() { + // wait until last prune finish + db.pruneSnapshotLock.Lock() + + go func() { + defer db.pruneSnapshotLock.Unlock() + + currentVersion, err := currentVersion(db.dir) + if err != nil { + db.logger.Error("failed to read current snapshot version", "err", err) + return + } + + counter := db.snapshotKeepRecent + if err := traverseSnapshots(db.dir, false, func(version int64) (bool, error) { + if version >= currentVersion { + // ignore any newer snapshot directories, there could be ongoning snapshot rewrite. + return false, nil + } + + if counter > 0 { + counter-- + return false, nil + } + + name := snapshotName(version) + db.logger.Info("prune snapshot", "name", name) + + if err := atomicRemoveDir(filepath.Join(db.dir, name)); err != nil { + db.logger.Error("failed to prune snapshot", "err", err) + } + + return false, nil + }); err != nil { + db.logger.Error("fail to prune snapshots", "err", err) + return + } + + // truncate WAL until the earliest remaining snapshot + earliestVersion, err := firstSnapshotVersion(db.dir) + if err != nil { + db.logger.Error("failed to find first snapshot", "err", err) + } + + if err := db.wal.TruncateFront(walIndex(earliestVersion+1, db.initialVersion)); err != nil { + db.logger.Error("failed to truncate wal", "err", err, "version", earliestVersion+1) + } + }() +} + +// Commit wraps SaveVersion to bump the version and writes the pending changes into log files to persist on disk +func (db *DB) Commit() (int64, error) { + db.mtx.Lock() + defer db.mtx.Unlock() + + if db.readOnly { + return 0, errReadOnly + } + + v, err := db.MultiTree.SaveVersion(true) + if err != nil { + return 0, err + } + + // write logs if enabled + if db.wal != nil { + entry := walEntry{index: walIndex(v, db.initialVersion), data: db.pendingLog} + if db.walChanSize >= 0 { + if db.walChan == nil { + db.initAsyncCommit() + } + + // async wal writing + db.walChan <- &entry + } else { + bz, err := entry.data.Marshal() + if err != nil { + return 0, err + } + if err := db.wal.Write(entry.index, bz); err != nil { + return 0, err + } + } + } + + db.pendingLog = WALEntry{} + + if err := db.checkAsyncTasks(); err != nil { + return 0, err + } + db.rewriteIfApplicable(v) + + return v, nil +} + +func (db *DB) initAsyncCommit() { + walChan := make(chan *walEntry, db.walChanSize) + walQuit := make(chan error) + + go func() { + defer close(walQuit) + + batch := wal.Batch{} + for { + entries := channelBatchRecv(walChan) + if len(entries) == 0 { + // channel is closed + break + } + + for _, entry := range entries { + bz, err := entry.data.Marshal() + if err != nil { + walQuit <- err + return + } + batch.Write(entry.index, bz) + } + + if err := db.wal.WriteBatch(&batch); err != nil { + walQuit <- err + return + } + batch.Clear() + } + }() + + db.walChan = walChan + db.walQuit = walQuit +} + +// WaitAsyncCommit waits for the completion of async commit +func (db *DB) WaitAsyncCommit() error { + db.mtx.Lock() + defer db.mtx.Unlock() + + return db.waitAsyncCommit() +} + +func (db *DB) waitAsyncCommit() error { + if db.walChan == nil { + return nil + } + + close(db.walChan) + err := <-db.walQuit + + db.walChan = nil + db.walQuit = nil + return err +} + +func (db *DB) Copy() *DB { + db.mtx.Lock() + defer db.mtx.Unlock() + + return db.copy(db.cacheSize) +} + +func (db *DB) copy(cacheSize int) *DB { + mtree := db.MultiTree.Copy(cacheSize) + + return &DB{ + MultiTree: *mtree, + logger: db.logger, + dir: db.dir, + snapshotWriterPool: db.snapshotWriterPool, + } +} + +// RewriteSnapshot writes the current version of memiavl into a snapshot, and update the `current` symlink. +func (db *DB) RewriteSnapshot() error { + return db.RewriteSnapshotWithContext(context.Background()) +} + +// RewriteSnapshotWithContext writes the current version of memiavl into a snapshot, and update the `current` symlink. +func (db *DB) RewriteSnapshotWithContext(ctx context.Context) error { + db.mtx.Lock() + defer db.mtx.Unlock() + + if db.readOnly { + return errReadOnly + } + + snapshotDir := snapshotName(db.lastCommitInfo.Version) + tmpDir := snapshotDir + TmpSuffix + path := filepath.Join(db.dir, tmpDir) + if err := db.MultiTree.WriteSnapshotWithContext(ctx, path, db.snapshotWriterPool); err != nil { + return errors.Join(err, os.RemoveAll(path)) + } + if err := os.Rename(path, filepath.Join(db.dir, snapshotDir)); err != nil { + return err + } + return updateCurrentSymlink(db.dir, snapshotDir) +} + +func (db *DB) Reload() error { + db.mtx.Lock() + defer db.mtx.Unlock() + + return db.reload() +} + +func (db *DB) reload() error { + mtree, err := LoadMultiTree(currentPath(db.dir), db.zeroCopy, db.cacheSize) + if err != nil { + return err + } + return db.reloadMultiTree(mtree) +} + +func (db *DB) reloadMultiTree(mtree *MultiTree) error { + if err := db.MultiTree.Close(); err != nil { + return err + } + + db.MultiTree = *mtree + // catch-up the pending changes + return db.MultiTree.applyWALEntry(db.pendingLog) +} + +// rewriteIfApplicable execute the snapshot rewrite strategy according to current height +func (db *DB) rewriteIfApplicable(height int64) { + if height%int64(db.snapshotInterval) != 0 { + return + } + + if err := db.rewriteSnapshotBackground(); err != nil { + db.logger.Error("failed to rewrite snapshot in background", "err", err) + } +} + +type snapshotResult struct { + mtree *MultiTree + err error +} + +// RewriteSnapshotBackground rewrite snapshot in a background goroutine, +// `Commit` will check the complete status, and switch to the new snapshot. +func (db *DB) RewriteSnapshotBackground() error { + db.mtx.Lock() + defer db.mtx.Unlock() + + if db.readOnly { + return errReadOnly + } + + return db.rewriteSnapshotBackground() +} + +func (db *DB) rewriteSnapshotBackground() error { + if db.snapshotRewriteChan != nil { + return errors.New("there's another ongoing snapshot rewriting process") + } + + ctx, cancel := context.WithCancel(context.Background()) + + ch := make(chan snapshotResult) + db.snapshotRewriteChan = ch + db.snapshotRewriteCancel = cancel + + cloned := db.copy(0) + wal := db.wal + go func() { + defer close(ch) + + cloned.logger.Info("start rewriting snapshot", "version", cloned.Version()) + if err := cloned.RewriteSnapshotWithContext(ctx); err != nil { + ch <- snapshotResult{err: err} + return + } + cloned.logger.Info("finished rewriting snapshot", "version", cloned.Version()) + mtree, err := LoadMultiTree(currentPath(cloned.dir), cloned.zeroCopy, 0) + if err != nil { + ch <- snapshotResult{err: err} + return + } + + // do a best effort catch-up, will do another final catch-up in main thread. + if err := mtree.CatchupWAL(wal, 0); err != nil { + ch <- snapshotResult{err: err} + return + } + + cloned.logger.Info("finished best-effort WAL catchup", "version", cloned.Version(), "latest", mtree.Version()) + + ch <- snapshotResult{mtree: mtree} + }() + + return nil +} + +func (db *DB) Close() error { + db.mtx.Lock() + defer db.mtx.Unlock() + + errs := []error{db.waitAsyncCommit()} + + if db.snapshotRewriteChan != nil { + db.snapshotRewriteCancel() + <-db.snapshotRewriteChan + db.snapshotRewriteChan = nil + db.snapshotRewriteCancel = nil + } + + errs = append(errs, + db.MultiTree.Close(), + db.wal.Close(), + ) + + db.wal = nil + + if db.fileLock != nil { + errs = append(errs, db.fileLock.Unlock(), db.fileLock.Destroy()) + db.fileLock = nil + } + + return errors.Join(errs...) +} + +// TreeByName wraps MultiTree.TreeByName to add a lock. +func (db *DB) TreeByName(name string) *Tree { + db.mtx.Lock() + defer db.mtx.Unlock() + + return db.MultiTree.TreeByName(name) +} + +// Version wraps MultiTree.Version to add a lock. +func (db *DB) Version() int64 { + db.mtx.Lock() + defer db.mtx.Unlock() + + return db.MultiTree.Version() +} + +// LastCommitInfo returns the last commit info. +func (db *DB) LastCommitInfo() *CommitInfo { + db.mtx.Lock() + defer db.mtx.Unlock() + + return db.MultiTree.LastCommitInfo() +} + +func (db *DB) SaveVersion(updateCommitInfo bool) (int64, error) { + db.mtx.Lock() + defer db.mtx.Unlock() + + if db.readOnly { + return 0, errReadOnly + } + + return db.MultiTree.SaveVersion(updateCommitInfo) +} + +func (db *DB) WorkingCommitInfo() *CommitInfo { + db.mtx.Lock() + defer db.mtx.Unlock() + + return db.MultiTree.WorkingCommitInfo() +} + +// UpdateCommitInfo wraps MultiTree.UpdateCommitInfo to add a lock. +func (db *DB) UpdateCommitInfo() { + db.mtx.Lock() + defer db.mtx.Unlock() + + if db.readOnly { + panic("can't update commit info in read-only mode") + } + + db.MultiTree.UpdateCommitInfo() +} + +// WriteSnapshot wraps MultiTree.WriteSnapshot to add a lock. +func (db *DB) WriteSnapshot(dir string) error { + return db.WriteSnapshotWithContext(context.Background(), dir) +} + +// WriteSnapshotWithContext wraps MultiTree.WriteSnapshotWithContext to add a lock. +func (db *DB) WriteSnapshotWithContext(ctx context.Context, dir string) error { + db.mtx.Lock() + defer db.mtx.Unlock() + + return db.MultiTree.WriteSnapshotWithContext(ctx, dir, db.snapshotWriterPool) +} + +func snapshotName(version int64) string { + return fmt.Sprintf("%s%020d", SnapshotPrefix, version) +} + +func currentPath(root string) string { + return filepath.Join(root, "current") +} + +func currentTmpPath(root string) string { + return filepath.Join(root, "current-tmp") +} + +func currentVersion(root string) (int64, error) { + name, err := os.Readlink(currentPath(root)) + if err != nil { + return 0, err + } + + version, err := parseVersion(name) + if err != nil { + return 0, err + } + + return version, nil +} + +func parseVersion(name string) (int64, error) { + if !isSnapshotName(name) { + return 0, fmt.Errorf("invalid snapshot name %s", name) + } + + v, err := strconv.ParseInt(name[len(SnapshotPrefix):], 10, 32) + if err != nil { + return 0, fmt.Errorf("snapshot version overflows: %d", err) + } + + return v, nil +} + +// seekSnapshot find the biggest snapshot version that's smaller than or equal to the target version, +// returns 0 if not found. +func seekSnapshot(root string, targetVersion uint32) (int64, error) { + var ( + snapshotVersion int64 + found bool + ) + if err := traverseSnapshots(root, false, func(version int64) (bool, error) { + if version <= int64(targetVersion) { + found = true + snapshotVersion = version + return true, nil + } + return false, nil + }); err != nil { + return 0, err + } + + if !found { + return 0, fmt.Errorf("target version is pruned: %d", targetVersion) + } + + return snapshotVersion, nil +} + +// firstSnapshotVersion returns the earliest snapshot name in the db +func firstSnapshotVersion(root string) (int64, error) { + var found int64 + if err := traverseSnapshots(root, true, func(version int64) (bool, error) { + found = version + return true, nil + }); err != nil { + return 0, err + } + + if found == 0 { + return 0, errors.New("empty memiavl db") + } + + return found, nil +} + +func walPath(root string) string { + return filepath.Join(root, "wal") +} + +// init a empty memiavl db +// +// ``` +// snapshot-0 +// +// commit_info +// +// current -> snapshot-0 +// ``` +func initEmptyDB(dir string, initialVersion uint32) error { + tmp := NewEmptyMultiTree(initialVersion, 0) + snapshotDir := snapshotName(0) + // create tmp worker pool + pool := pond.New(DefaultSnapshotWriterLimit, DefaultSnapshotWriterLimit*10) + defer pool.Stop() + + if err := tmp.WriteSnapshot(filepath.Join(dir, snapshotDir), pool); err != nil { + return err + } + return updateCurrentSymlink(dir, snapshotDir) +} + +// updateCurrentSymlink creates or replace the current symblic link atomically. +// it could fail under concurrent usage for tmp file conflicts. +func updateCurrentSymlink(dir, snapshot string) error { + tmpPath := currentTmpPath(dir) + if err := os.Symlink(snapshot, tmpPath); err != nil { + return err + } + // assuming file renaming operation is atomic + return os.Rename(tmpPath, currentPath(dir)) +} + +// traverseSnapshots traverse the snapshot list in specified order. +func traverseSnapshots(dir string, ascending bool, callback func(int64) (bool, error)) error { + entries, err := os.ReadDir(dir) + if err != nil { + return err + } + + process := func(entry os.DirEntry) (bool, error) { + if !entry.IsDir() || !isSnapshotName(entry.Name()) { + return false, nil + } + + version, err := parseVersion(entry.Name()) + if err != nil { + return true, fmt.Errorf("invalid snapshot name: %w", err) + } + + return callback(version) + } + + if ascending { + for i := 0; i < len(entries); i++ { + stop, err := process(entries[i]) + if stop || err != nil { + return err + } + } + } else { + for i := len(entries) - 1; i >= 0; i-- { + stop, err := process(entries[i]) + if stop || err != nil { + return err + } + } + } + + return nil +} + +// atomicRemoveDir is equavalent to `mv snapshot snapshot-tmp && rm -r snapshot-tmp` +func atomicRemoveDir(path string) error { + tmpPath := path + TmpSuffix + if err := os.Rename(path, tmpPath); err != nil { + return err + } + + return os.RemoveAll(tmpPath) +} + +// createDBIfNotExist detects if db does not exist and try to initialize an empty one. +func createDBIfNotExist(dir string, initialVersion uint32) error { + _, err := os.Stat(filepath.Join(dir, "current", MetadataFileName)) + if err != nil && os.IsNotExist(err) { + return initEmptyDB(dir, initialVersion) + } + return nil +} + +type walEntry struct { + index uint64 + data WALEntry +} + +func isSnapshotName(name string) bool { + return strings.HasPrefix(name, SnapshotPrefix) && len(name) == SnapshotDirLen +} + +// GetLatestVersion finds the latest version number without loading the whole db, +// it's needed for upgrade module to check store upgrades, +// it returns 0 if db don't exists or is empty. +func GetLatestVersion(dir string) (int64, error) { + metadata, err := readMetadata(currentPath(dir)) + if err != nil { + if os.IsNotExist(err) { + return 0, nil + } + return 0, err + } + + wal, err := OpenWAL(walPath(dir), &wal.Options{NoCopy: true}) + if err != nil { + return 0, err + } + lastIndex, err := wal.LastIndex() + if err != nil { + return 0, err + } + return walVersion(lastIndex, uint32(metadata.InitialVersion)), nil +} + +func channelBatchRecv[T any](ch <-chan *T) []*T { + // block if channel is empty + item := <-ch + if item == nil { + // channel is closed + return nil + } + + remaining := len(ch) + result := make([]*T, 0, remaining+1) + result = append(result, item) + for i := 0; i < remaining; i++ { + result = append(result, <-ch) + } + + return result +} diff --git a/memiavl/db_test.go b/memiavl/db_test.go new file mode 100644 index 0000000..f249d59 --- /dev/null +++ b/memiavl/db_test.go @@ -0,0 +1,508 @@ +package memiavl + +import ( + "encoding/hex" + "errors" + "os" + "path/filepath" + "runtime/debug" + "strconv" + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +func TestRewriteSnapshot(t *testing.T) { + db, err := Load(t.TempDir(), Options{ + CreateIfMissing: true, + InitialStores: []string{"test"}, + }) + require.NoError(t, err) + + for i, changes := range ChangeSets { + cs := []*NamedChangeSet{ + { + Name: "test", + Changeset: changes, + }, + } + t.Run(strconv.Itoa(i), func(t *testing.T) { + require.NoError(t, db.ApplyChangeSets(cs)) + v, err := db.Commit() + require.NoError(t, err) + require.Equal(t, i+1, int(v)) + require.Equal(t, RefHashes[i], db.lastCommitInfo.StoreInfos[0].CommitId.Hash) + require.NoError(t, db.RewriteSnapshot()) + require.NoError(t, db.Reload()) + }) + } +} + +func TestRemoveSnapshotDir(t *testing.T) { + dbDir := t.TempDir() + defer os.RemoveAll(dbDir) + + snapshotDir := filepath.Join(dbDir, snapshotName(0)) + tmpDir := snapshotDir + TmpSuffix + if err := os.MkdirAll(tmpDir, os.ModePerm); err != nil { + t.Fatalf("Failed to create dummy snapshot directory: %v", err) + } + db, err := Load(dbDir, Options{ + CreateIfMissing: true, + InitialStores: []string{"test"}, + SnapshotKeepRecent: 0, + }) + require.NoError(t, err) + require.NoError(t, db.Close()) + + _, err = os.Stat(tmpDir) + require.True(t, os.IsNotExist(err), "Expected temporary snapshot directory to be deleted, but it still exists") + + err = os.MkdirAll(tmpDir, os.ModePerm) + require.NoError(t, err) + + _, err = Load(dbDir, Options{ + ReadOnly: true, + }) + require.NoError(t, err) + + _, err = os.Stat(tmpDir) + require.False(t, os.IsNotExist(err)) + + db, err = Load(dbDir, Options{}) + require.NoError(t, err) + + _, err = os.Stat(tmpDir) + require.True(t, os.IsNotExist(err)) + + require.NoError(t, db.Close()) +} + +func TestRewriteSnapshotBackground(t *testing.T) { + db, err := Load(t.TempDir(), Options{ + CreateIfMissing: true, + InitialStores: []string{"test"}, + SnapshotKeepRecent: 0, // only a single snapshot is kept + }) + require.NoError(t, err) + + for i, changes := range ChangeSets { + cs := []*NamedChangeSet{ + { + Name: "test", + Changeset: changes, + }, + } + require.NoError(t, db.ApplyChangeSets(cs)) + v, err := db.Commit() + require.NoError(t, err) + require.Equal(t, i+1, int(v)) + require.Equal(t, RefHashes[i], db.lastCommitInfo.StoreInfos[0].CommitId.Hash) + + _ = db.RewriteSnapshotBackground() + time.Sleep(time.Millisecond * 20) + } + + for db.snapshotRewriteChan != nil { + require.NoError(t, db.checkAsyncTasks()) + } + + db.pruneSnapshotLock.Lock() + defer db.pruneSnapshotLock.Unlock() + + entries, err := os.ReadDir(db.dir) + require.NoError(t, err) + + // three files: snapshot, current link, wal, LOCK + require.Equal(t, 4, len(entries)) +} + +func TestWAL(t *testing.T) { + dir := t.TempDir() + db, err := Load(dir, Options{CreateIfMissing: true, InitialStores: []string{"test", "delete"}}) + require.NoError(t, err) + + for _, changes := range ChangeSets { + cs := []*NamedChangeSet{ + { + Name: "test", + Changeset: changes, + }, + } + require.NoError(t, db.ApplyChangeSets(cs)) + _, err := db.Commit() + require.NoError(t, err) + } + + require.Equal(t, 2, len(db.lastCommitInfo.StoreInfos)) + + require.NoError(t, db.ApplyUpgrades([]*TreeNameUpgrade{ + { + Name: "newtest", + RenameFrom: "test", + }, + { + Name: "delete", + Delete: true, + }, + })) + _, err = db.Commit() + require.NoError(t, err) + + require.NoError(t, db.Close()) + + db, err = Load(dir, Options{}) + require.NoError(t, err) + + require.Equal(t, "newtest", db.lastCommitInfo.StoreInfos[0].Name) + require.Equal(t, 1, len(db.lastCommitInfo.StoreInfos)) + require.Equal(t, RefHashes[len(RefHashes)-1], db.lastCommitInfo.StoreInfos[0].CommitId.Hash) +} + +func mockNameChangeSet(name, key, value string) []*NamedChangeSet { + return []*NamedChangeSet{ + { + Name: name, + Changeset: ChangeSet{ + Pairs: mockKVPairs(key, value), + }, + }, + } +} + +// 0/1 -> v :1 +// ... +// 100 -> v: 100 +func TestInitialVersion(t *testing.T) { + name := "test" + name1 := "new" + name2 := "new2" + key := "hello" + value := "world" + for _, initialVersion := range []int64{0, 1, 100} { + dir := t.TempDir() + db, err := Load(dir, Options{CreateIfMissing: true, InitialStores: []string{name}}) + require.NoError(t, err) + db.SetInitialVersion(initialVersion) + require.NoError(t, db.ApplyChangeSets(mockNameChangeSet(name, key, value))) + v, err := db.Commit() + require.NoError(t, err) + if initialVersion <= 1 { + require.Equal(t, int64(1), v) + } else { + require.Equal(t, initialVersion, v) + } + hash := db.LastCommitInfo().StoreInfos[0].CommitId.Hash + require.Equal(t, "6032661ab0d201132db7a8fa1da6a0afe427e6278bd122c301197680ab79ca02", hex.EncodeToString(hash)) + require.NoError(t, db.ApplyChangeSets(mockNameChangeSet(name, key, "world1"))) + v, err = db.Commit() + require.NoError(t, err) + hash = db.LastCommitInfo().StoreInfos[0].CommitId.Hash + if initialVersion <= 1 { + require.Equal(t, int64(2), v) + require.Equal(t, "ef0530f9bf1af56c19a3bac32a3ec4f76a6fefaacb2efd4027a2cf37240f60bb", hex.EncodeToString(hash)) + } else { + require.Equal(t, initialVersion+1, v) + require.Equal(t, "a719e7d699d42ea8e5637ec84675a2c28f14a00a71fb518f20aa2c395673a3b8", hex.EncodeToString(hash)) + } + require.NoError(t, db.Close()) + + db, err = Load(dir, Options{}) + require.NoError(t, err) + require.Equal(t, uint32(initialVersion), db.initialVersion) + require.Equal(t, v, db.Version()) + require.Equal(t, hex.EncodeToString(hash), hex.EncodeToString(db.LastCommitInfo().StoreInfos[0].CommitId.Hash)) + + db.ApplyUpgrades([]*TreeNameUpgrade{{Name: name1}}) + require.NoError(t, db.ApplyChangeSets(mockNameChangeSet(name1, key, value))) + v, err = db.Commit() + require.NoError(t, err) + if initialVersion <= 1 { + require.Equal(t, int64(3), v) + } else { + require.Equal(t, initialVersion+2, v) + } + require.Equal(t, 2, len(db.lastCommitInfo.StoreInfos)) + info := db.lastCommitInfo.StoreInfos[0] + require.Equal(t, name1, info.Name) + require.Equal(t, v, info.CommitId.Version) + require.Equal(t, "6032661ab0d201132db7a8fa1da6a0afe427e6278bd122c301197680ab79ca02", hex.EncodeToString(info.CommitId.Hash)) + // the nodes are created with version 1, which is compatible with iavl behavior: https://github.com/cosmos/iavl/pull/660 + require.Equal(t, info.CommitId.Hash, HashNode(newLeafNode([]byte(key), []byte(value), 1))) + + require.NoError(t, db.RewriteSnapshot()) + require.NoError(t, db.Reload()) + + db.ApplyUpgrades([]*TreeNameUpgrade{{Name: name2}}) + require.NoError(t, db.ApplyChangeSets(mockNameChangeSet(name2, key, value))) + v, err = db.Commit() + require.NoError(t, err) + if initialVersion <= 1 { + require.Equal(t, int64(4), v) + } else { + require.Equal(t, initialVersion+3, v) + } + require.Equal(t, 3, len(db.lastCommitInfo.StoreInfos)) + info2 := db.lastCommitInfo.StoreInfos[1] + require.Equal(t, name2, info2.Name) + require.Equal(t, v, info2.CommitId.Version) + require.Equal(t, hex.EncodeToString(info.CommitId.Hash), hex.EncodeToString(info2.CommitId.Hash)) + } +} + +func TestLoadVersion(t *testing.T) { + dir := t.TempDir() + db, err := Load(dir, Options{ + CreateIfMissing: true, + InitialStores: []string{"test"}, + }) + require.NoError(t, err) + + for i, changes := range ChangeSets { + cs := []*NamedChangeSet{ + { + Name: "test", + Changeset: changes, + }, + } + t.Run(strconv.Itoa(i), func(t *testing.T) { + require.NoError(t, db.ApplyChangeSets(cs)) + + // check the root hash + require.Equal(t, RefHashes[db.Version()], db.WorkingCommitInfo().StoreInfos[0].CommitId.Hash) + + _, err := db.Commit() + require.NoError(t, err) + }) + } + require.NoError(t, db.Close()) + + for v, expItems := range ExpectItems { + if v == 0 { + continue + } + tmp, err := Load(dir, Options{ + TargetVersion: uint32(v), + ReadOnly: true, + }) + require.NoError(t, err) + require.Equal(t, RefHashes[v-1], tmp.TreeByName("test").RootHash()) + require.Equal(t, expItems, collectIter(tmp.TreeByName("test").Iterator(nil, nil, true))) + } +} + +func TestZeroCopy(t *testing.T) { + db, err := Load(t.TempDir(), Options{InitialStores: []string{"test", "test2"}, CreateIfMissing: true, ZeroCopy: true}) + require.NoError(t, err) + require.NoError(t, db.ApplyChangeSets([]*NamedChangeSet{ + {Name: "test", Changeset: ChangeSets[0]}, + })) + _, err = db.Commit() + require.NoError(t, err) + require.NoError(t, errors.Join( + db.RewriteSnapshot(), + db.Reload(), + )) + + // the test tree's root hash will reference the zero-copy value + require.NoError(t, db.ApplyChangeSets([]*NamedChangeSet{ + {Name: "test2", Changeset: ChangeSets[0]}, + })) + _, err = db.Commit() + require.NoError(t, err) + + commitInfo := *db.LastCommitInfo() + + value := db.TreeByName("test").Get([]byte("hello")) + require.Equal(t, []byte("world"), value) + + db.SetZeroCopy(false) + valueCloned := db.TreeByName("test").Get([]byte("hello")) + require.Equal(t, []byte("world"), valueCloned) + + _ = commitInfo.StoreInfos[0].CommitId.Hash[0] + + require.NoError(t, db.Close()) + + require.Equal(t, []byte("world"), valueCloned) + + // accessing the zero-copy value after the db is closed triggers segment fault. + // reset global panic on fault setting after function finished + defer debug.SetPanicOnFault(debug.SetPanicOnFault(true)) + require.Panics(t, func() { + require.Equal(t, []byte("world"), value) + }) + + // it's ok to access after db closed + _ = commitInfo.StoreInfos[0].CommitId.Hash[0] +} + +func TestWalIndexConversion(t *testing.T) { + testCases := []struct { + index uint64 + version int64 + initialVersion uint32 + }{ + {1, 1, 0}, + {1, 1, 1}, + {1, 10, 10}, + {2, 11, 10}, + } + for _, tc := range testCases { + require.Equal(t, tc.index, walIndex(tc.version, tc.initialVersion)) + require.Equal(t, tc.version, walVersion(tc.index, tc.initialVersion)) + } +} + +func TestEmptyValue(t *testing.T) { + dir := t.TempDir() + db, err := Load(dir, Options{InitialStores: []string{"test"}, CreateIfMissing: true, ZeroCopy: true}) + require.NoError(t, err) + + require.NoError(t, db.ApplyChangeSets([]*NamedChangeSet{ + {Name: "test", Changeset: ChangeSet{ + Pairs: []*KVPair{ + {Key: []byte("hello1"), Value: []byte("")}, + {Key: []byte("hello2"), Value: []byte("")}, + {Key: []byte("hello3"), Value: []byte("")}, + }, + }}, + })) + _, err = db.Commit() + require.NoError(t, err) + + require.NoError(t, db.ApplyChangeSets([]*NamedChangeSet{ + {Name: "test", Changeset: ChangeSet{ + Pairs: []*KVPair{{Key: []byte("hello1"), Delete: true}}, + }}, + })) + version, err := db.Commit() + require.NoError(t, err) + + require.NoError(t, db.Close()) + + db, err = Load(dir, Options{ZeroCopy: true}) + require.NoError(t, err) + require.Equal(t, version, db.Version()) +} + +func TestInvalidOptions(t *testing.T) { + dir := t.TempDir() + + _, err := Load(dir, Options{ReadOnly: true}) + require.Error(t, err) + + _, err = Load(dir, Options{ReadOnly: true, CreateIfMissing: true}) + require.Error(t, err) + + db, err := Load(dir, Options{CreateIfMissing: true}) + require.NoError(t, err) + require.NoError(t, db.Close()) + + _, err = Load(dir, Options{LoadForOverwriting: true, ReadOnly: true}) + require.Error(t, err) + + _, err = Load(dir, Options{ReadOnly: true}) + require.NoError(t, err) +} + +func TestExclusiveLock(t *testing.T) { + dir := t.TempDir() + + db, err := Load(dir, Options{CreateIfMissing: true}) + require.NoError(t, err) + + _, err = Load(dir, Options{}) + require.Error(t, err) + + _, err = Load(dir, Options{ReadOnly: true}) + require.NoError(t, err) + + require.NoError(t, db.Close()) + + _, err = Load(dir, Options{}) + require.NoError(t, err) +} + +func TestFastCommit(t *testing.T) { + dir := t.TempDir() + + db, err := Load(dir, Options{CreateIfMissing: true, InitialStores: []string{"test"}, SnapshotInterval: 3, AsyncCommitBuffer: 10}) + require.NoError(t, err) + + cs := ChangeSet{ + Pairs: []*KVPair{ + {Key: []byte("hello1"), Value: make([]byte, 1024*1024)}, + }, + } + + // the bug reproduce when the wal writing is slower than commit, that happens when wal segment is full and create a new one, the wal writing will slow down a little bit, + // segment size is 20m, each change set is 1m, so we need a bit more than 20 commits to reproduce. + for i := 0; i < 30; i++ { + require.NoError(t, db.ApplyChangeSets([]*NamedChangeSet{{Name: "test", Changeset: cs}})) + _, err := db.Commit() + require.NoError(t, err) + } + + <-db.snapshotRewriteChan + require.NoError(t, db.Close()) +} + +func TestRepeatedApplyChangeSet(t *testing.T) { + db, err := Load(t.TempDir(), Options{CreateIfMissing: true, InitialStores: []string{"test1", "test2"}, SnapshotInterval: 3, AsyncCommitBuffer: 10}) + require.NoError(t, err) + + err = db.ApplyChangeSets([]*NamedChangeSet{ + {Name: "test1", Changeset: ChangeSet{ + Pairs: []*KVPair{ + {Key: []byte("hello1"), Value: []byte("world1")}, + }, + }}, + {Name: "test2", Changeset: ChangeSet{ + Pairs: []*KVPair{ + {Key: []byte("hello2"), Value: []byte("world2")}, + }, + }}, + }) + require.NoError(t, err) + + err = db.ApplyChangeSets([]*NamedChangeSet{{Name: "test1"}}) + require.NoError(t, err) + + err = db.ApplyChangeSet("test1", ChangeSet{ + Pairs: []*KVPair{ + {Key: []byte("hello2"), Value: []byte("world2")}, + }, + }) + require.NoError(t, err) + + _, err = db.Commit() + require.NoError(t, err) + + err = db.ApplyChangeSet("test1", ChangeSet{ + Pairs: []*KVPair{ + {Key: []byte("hello2"), Value: []byte("world2")}, + }, + }) + require.NoError(t, err) + err = db.ApplyChangeSet("test2", ChangeSet{ + Pairs: []*KVPair{ + {Key: []byte("hello2"), Value: []byte("world2")}, + }, + }) + require.NoError(t, err) + + err = db.ApplyChangeSet("test1", ChangeSet{ + Pairs: []*KVPair{ + {Key: []byte("hello2"), Value: []byte("world2")}, + }, + }) + require.NoError(t, err) + err = db.ApplyChangeSet("test2", ChangeSet{ + Pairs: []*KVPair{ + {Key: []byte("hello2"), Value: []byte("world2")}, + }, + }) + require.NoError(t, err) +} diff --git a/memiavl/export.go b/memiavl/export.go new file mode 100644 index 0000000..ec54185 --- /dev/null +++ b/memiavl/export.go @@ -0,0 +1,146 @@ +package memiavl + +import ( + "context" + "errors" + "fmt" + "path/filepath" +) + +// ErrorExportDone is returned by Exporter.Next() when all items have been exported. +var ErrorExportDone = errors.New("export is complete") + +// exportBufferSize is the number of nodes to buffer in the exporter. It improves throughput by +// processing multiple nodes per context switch, but take care to avoid excessive memory usage, +// especially since callers may export several IAVL stores in parallel (e.g. the Cosmos SDK). +const exportBufferSize = 32 + +type MultiTreeExporter struct { + // only one of them is non-nil + db *DB + mtree *MultiTree + + iTree int + exporter *Exporter +} + +func NewMultiTreeExporter(dir string, version uint32, supportExportNonSnapshotVersion bool) (exporter *MultiTreeExporter, err error) { + var ( + db *DB + mtree *MultiTree + ) + if supportExportNonSnapshotVersion { + db, err = Load(dir, Options{ + TargetVersion: version, + ZeroCopy: true, + ReadOnly: true, + SnapshotWriterLimit: DefaultSnapshotWriterLimit, + }) + if err != nil { + return nil, fmt.Errorf("invalid height: %d, %w", version, err) + } + } else { + curVersion, err := currentVersion(dir) + if err != nil { + return nil, fmt.Errorf("failed to load current version: %w", err) + } + if int64(version) > curVersion { + return nil, fmt.Errorf("snapshot is not created yet: height: %d", version) + } + mtree, err = LoadMultiTree(filepath.Join(dir, snapshotName(int64(version))), true, 0) + if err != nil { + return nil, fmt.Errorf("snapshot don't exists: height: %d, %w", version, err) + } + } + + return &MultiTreeExporter{ + db: db, + mtree: mtree, + }, nil +} + +func (mte *MultiTreeExporter) trees() []NamedTree { + if mte.db != nil { + return mte.db.trees + } + return mte.mtree.trees +} + +func (mte *MultiTreeExporter) Next() (interface{}, error) { + if mte.exporter != nil { + node, err := mte.exporter.Next() + if err != nil { + if err == ErrorExportDone { + mte.exporter.Close() + mte.exporter = nil + return mte.Next() + } + return nil, err + } + return node, nil + } + + trees := mte.trees() + if mte.iTree >= len(trees) { + return nil, ErrorExportDone + } + + tree := trees[mte.iTree] + mte.exporter = tree.Export() + mte.iTree++ + return tree.Name, nil +} + +func (mte *MultiTreeExporter) Close() error { + if mte.exporter != nil { + mte.exporter.Close() + mte.exporter = nil + } + + if mte.db != nil { + return mte.db.Close() + } + if mte.mtree != nil { + return mte.mtree.Close() + } + + return nil +} + +type exportWorker func(callback func(*ExportNode) bool) + +type Exporter struct { + ch <-chan *ExportNode + cancel context.CancelFunc +} + +func newExporter(worker exportWorker) *Exporter { + ctx, cancel := context.WithCancel(context.Background()) + ch := make(chan *ExportNode, exportBufferSize) + go func() { + defer close(ch) + worker(func(enode *ExportNode) bool { + select { + case ch <- enode: + case <-ctx.Done(): + return true + } + return false + }) + }() + return &Exporter{ch, cancel} +} + +func (e *Exporter) Next() (*ExportNode, error) { + if exportNode, ok := <-e.ch; ok { + return exportNode, nil + } + return nil, ErrorExportDone +} + +// Close closes the exporter. It is safe to call multiple times. +func (e *Exporter) Close() { + e.cancel() + for range e.ch { // drain channel + } +} diff --git a/memiavl/filelock.go b/memiavl/filelock.go new file mode 100644 index 0000000..6becb39 --- /dev/null +++ b/memiavl/filelock.go @@ -0,0 +1,28 @@ +package memiavl + +import ( + "path/filepath" + + "github.com/zbiljic/go-filelock" +) + +type FileLock interface { + Unlock() error + Destroy() error +} + +func LockFile(fname string) (FileLock, error) { + path, err := filepath.Abs(fname) + if err != nil { + return nil, err + } + fl, err := filelock.New(path) + if err != nil { + return nil, err + } + if _, err := fl.TryLock(); err != nil { + return nil, err + } + + return fl, nil +} diff --git a/memiavl/go.mod b/memiavl/go.mod new file mode 100644 index 0000000..432fafb --- /dev/null +++ b/memiavl/go.mod @@ -0,0 +1,65 @@ +module github.com/crypto-org-chain/cronos/memiavl + +go 1.22 + +toolchain go1.22.0 + +require ( + cosmossdk.io/log v1.3.1 + cosmossdk.io/store v1.1.0 + github.com/alitto/pond v1.8.3 + github.com/cosmos/cosmos-db v1.0.2 + github.com/cosmos/gogoproto v1.4.11 + github.com/cosmos/iavl v1.1.2 + github.com/cosmos/ics23/go v0.10.0 + github.com/ledgerwatch/erigon-lib v0.0.0-20230210071639-db0e7ed11263 + github.com/stretchr/testify v1.8.4 + github.com/tidwall/btree v1.7.0 + github.com/tidwall/gjson v1.10.2 + github.com/tidwall/wal v1.1.7 + github.com/zbiljic/go-filelock v0.0.0-20170914061330-1dbf7103ab7d + golang.org/x/exp v0.0.0-20240314144324-c7f7c6466f7f +) + +require ( + github.com/DataDog/zstd v1.5.5 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/cockroachdb/errors v1.11.1 // indirect + github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect + github.com/cockroachdb/pebble v1.1.0 // 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.1 // 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/klauspost/compress v1.17.7 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/linxGnu/grocksdb v1.8.12 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/prometheus/client_golang v1.19.0 // indirect + github.com/prometheus/client_model v0.6.0 // indirect + github.com/prometheus/common v0.50.0 // indirect + github.com/prometheus/procfs v0.13.0 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect + github.com/rs/zerolog v1.32.0 // indirect + github.com/spf13/cast v1.6.0 // indirect + github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect + github.com/tidwall/match v1.1.1 // indirect + github.com/tidwall/pretty v1.2.0 // indirect + github.com/tidwall/tinylru v1.1.0 // indirect + golang.org/x/crypto v0.21.0 // indirect + golang.org/x/net v0.23.0 // indirect + golang.org/x/sys v0.18.0 // indirect + golang.org/x/text v0.14.0 // indirect + google.golang.org/protobuf v1.33.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/memiavl/go.sum b/memiavl/go.sum new file mode 100644 index 0000000..a11ec86 --- /dev/null +++ b/memiavl/go.sum @@ -0,0 +1,250 @@ +cosmossdk.io/log v1.3.1 h1:UZx8nWIkfbbNEWusZqzAx3ZGvu54TZacWib3EzUYmGI= +cosmossdk.io/log v1.3.1/go.mod h1:2/dIomt8mKdk6vl3OWJcPk2be3pGOS8OQaLUM/3/tCM= +cosmossdk.io/store v1.1.0 h1:LnKwgYMc9BInn9PhpTFEQVbL9UK475G2H911CGGnWHk= +cosmossdk.io/store v1.1.0/go.mod h1:oZfW/4Fc/zYqu3JmQcQdUJ3fqu5vnYTn3LZFFy8P8ng= +github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ= +github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= +github.com/alitto/pond v1.8.3 h1:ydIqygCLVPqIX/USe5EaV/aSRXTRXDEI9JwuDdu+/xs= +github.com/alitto/pond v1.8.3/go.mod h1:CmvIIGd5jKLasGI3D87qDkQxjzChdKMmnXMg3fG6M6Q= +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.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/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-db v1.0.2 h1:hwMjozuY1OlJs/uh6vddqnk9j7VamLv+0DBlbEXbAKs= +github.com/cosmos/cosmos-db v1.0.2/go.mod h1:Z8IXcFJ9PqKK6BIsVOB3QXtkKoqUOp1vRvPT39kOXEA= +github.com/cosmos/gogoproto v1.4.11 h1:LZcMHrx4FjUgrqQSWeaGC1v/TeuVFqSLa43CC6aWR2g= +github.com/cosmos/gogoproto v1.4.11/go.mod h1:/g39Mh8m17X8Q/GDEs5zYTSNaNnInBSohtaxzQnYq1Y= +github.com/cosmos/iavl v1.1.2 h1:zL9FK7C4L/P4IF1Dm5fIwz0WXCnn7Bp1M2FxH0ayM7Y= +github.com/cosmos/iavl v1.1.2/go.mod h1:jLeUvm6bGT1YutCaL2fIar/8vGUE8cPZvh/gXEWDaDM= +github.com/cosmos/ics23/go v0.10.0 h1:iXqLLgp2Lp+EdpIuwXTYIQU+AiHj9mOC2X9ab++bZDM= +github.com/cosmos/ics23/go v0.10.0/go.mod h1:ZfJSmng/TBNTBkFemHHHj5YY7VAU/MBU980F4VU1NG0= +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.1 h1:ujpDlBkkwgWUY+qPId5IwapRW/xEoligRSYjioR6DFI= +github.com/emicklei/dot v1.6.1/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 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= +github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +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-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.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.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/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/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +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/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.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg= +github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/ledgerwatch/erigon-lib v0.0.0-20230210071639-db0e7ed11263 h1:LGEzZvf33Y1NhuP5+jI/ni9l1TFS6oYPDilgy74NusM= +github.com/ledgerwatch/erigon-lib v0.0.0-20230210071639-db0e7ed11263/go.mod h1:OXgMDuUo2lZ3NpH29ZvMYbk+LxFd5ffDl2Z2mGMuY/I= +github.com/linxGnu/grocksdb v1.8.12 h1:1/pCztQUOa3BX/1gR3jSZDoaKFpeHFvQ1XrqZpSvZVo= +github.com/linxGnu/grocksdb v1.8.12/go.mod h1:xZCIb5Muw+nhbDK4Y5UJuOrin5MceOuiXkVUR7vp4WY= +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/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/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= +github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/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 v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU= +github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k= +github.com/prometheus/client_model v0.6.0 h1:k1v3CzpSRUTrKMppY35TLwPvxHqBu0bYgxZzqGIgaos= +github.com/prometheus/client_model v0.6.0/go.mod h1:NTQHnmxFpouOD0DpvP4XujX3CdOAGQPoaGhyTchlyt8= +github.com/prometheus/common v0.50.0 h1:YSZE6aa9+luNa2da6/Tik0q0A5AbR+U003TItK57CPQ= +github.com/prometheus/common v0.50.0/go.mod h1:wHFBCEVWVmHMUpg7pYcOm2QUR/ocQdYSJVQJKnHc3xQ= +github.com/prometheus/procfs v0.13.0 h1:GqzLlQyfsPbaEHaQkO7tbDlriv/4o5Hudv6OXHGKX7o= +github.com/prometheus/procfs v0.13.0/go.mod h1:cd4PFCR54QLnGKPaKGA6l+cfuNXtht43ZKY6tow0Y1g= +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.32.0 h1:keLypqrlIjaFsbmJOBdB/qvyF8KEtCWHwobLp5l/mQ0= +github.com/rs/zerolog v1.32.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +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.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +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/tidwall/gjson v1.10.2 h1:APbLGOM0rrEkd8WBw9C24nllro4ajFuJu0Sc9hRz8Bo= +github.com/tidwall/gjson v1.10.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/tinylru v1.1.0 h1:XY6IUfzVTU9rpwdhKUF6nQdChgCdGjkMfLzbWyiau6I= +github.com/tidwall/tinylru v1.1.0/go.mod h1:3+bX+TJ2baOLMWTnlyNWHh4QMnFyARg2TLTQ6OFbzw8= +github.com/tidwall/wal v1.1.7 h1:emc1TRjIVsdKKSnpwGBAcsAGg0767SvUk8+ygx7Bb+4= +github.com/tidwall/wal v1.1.7/go.mod h1:r6lR1j27W9EPalgHiB7zLJDYu3mzW5BQP5KrzBpYY/E= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/zbiljic/go-filelock v0.0.0-20170914061330-1dbf7103ab7d h1:XQyeLr7N9iY9mi+TGgsBFkj54+j3fdoo8e2u6zrGP5A= +github.com/zbiljic/go-filelock v0.0.0-20170914061330-1dbf7103ab7d/go.mod h1:hoMeDjlNXTNqVwrCk8YDyaBS2g5vFfEX2ezMi4vb6CY= +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.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/exp v0.0.0-20240314144324-c7f7c6466f7f h1:3CW0unweImhOzd5FmYuRsD4Y4oQFKZIjAnKbjV4WIrw= +golang.org/x/exp v0.0.0-20240314144324-c7f7c6466f7f/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= +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-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +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.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= +golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/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.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-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.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.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.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +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.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-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.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.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/memiavl/import.go b/memiavl/import.go new file mode 100644 index 0000000..9bab85a --- /dev/null +++ b/memiavl/import.go @@ -0,0 +1,263 @@ +package memiavl + +import ( + "context" + "errors" + "fmt" + "math" + "os" + "path/filepath" +) + +const NodeChannelBuffer = 2048 + +type MultiTreeImporter struct { + dir string + snapshotDir string + height int64 + importer *TreeImporter + fileLock FileLock +} + +func NewMultiTreeImporter(dir string, height uint64) (*MultiTreeImporter, error) { + if height > math.MaxUint32 { + return nil, fmt.Errorf("version overflows uint32: %d", height) + } + + var fileLock FileLock + fileLock, err := LockFile(filepath.Join(dir, LockFileName)) + if err != nil { + return nil, fmt.Errorf("fail to lock db: %w", err) + } + + return &MultiTreeImporter{ + dir: dir, + height: int64(height), + snapshotDir: snapshotName(int64(height)), + fileLock: fileLock, + }, nil +} + +func (mti *MultiTreeImporter) tmpDir() string { + return filepath.Join(mti.dir, mti.snapshotDir+TmpSuffix) +} + +func (mti *MultiTreeImporter) Add(item interface{}) error { + switch item := item.(type) { + case *ExportNode: + mti.AddNode(item) + return nil + case string: + return mti.AddTree(item) + default: + return fmt.Errorf("unknown item type: %T", item) + } +} + +func (mti *MultiTreeImporter) AddTree(name string) error { + if mti.importer != nil { + if err := mti.importer.Close(); err != nil { + return err + } + } + mti.importer = NewTreeImporter(filepath.Join(mti.tmpDir(), name), mti.height) + return nil +} + +func (mti *MultiTreeImporter) AddNode(node *ExportNode) { + mti.importer.Add(node) +} + +func (mti *MultiTreeImporter) Finalize() error { + if mti.importer != nil { + if err := mti.importer.Close(); err != nil { + return err + } + mti.importer = nil + } + + tmpDir := mti.tmpDir() + if err := updateMetadataFile(tmpDir, mti.height); err != nil { + return err + } + + if err := os.Rename(tmpDir, filepath.Join(mti.dir, mti.snapshotDir)); err != nil { + return err + } + + return updateCurrentSymlink(mti.dir, mti.snapshotDir) +} + +func (mti *MultiTreeImporter) Close() error { + var err error + if mti.importer != nil { + err = mti.importer.Close() + } + return errors.Join(err, mti.fileLock.Unlock(), mti.fileLock.Destroy()) +} + +// TreeImporter import a single memiavl tree from state-sync snapshot +type TreeImporter struct { + nodesChan chan *ExportNode + quitChan chan error +} + +func NewTreeImporter(dir string, version int64) *TreeImporter { + nodesChan := make(chan *ExportNode, NodeChannelBuffer) + quitChan := make(chan error) + go func() { + defer close(quitChan) + quitChan <- doImport(dir, version, nodesChan) + }() + return &TreeImporter{nodesChan, quitChan} +} + +func (ai *TreeImporter) Add(node *ExportNode) { + ai.nodesChan <- node +} + +func (ai *TreeImporter) Close() error { + var err error + // tolerate double close + if ai.nodesChan != nil { + close(ai.nodesChan) + err = <-ai.quitChan + } + ai.nodesChan = nil + ai.quitChan = nil + return err +} + +// doImport a stream of `ExportNode`s into a new snapshot. +func doImport(dir string, version int64, nodes <-chan *ExportNode) (returnErr error) { + if version > int64(math.MaxUint32) { + return errors.New("version overflows uint32") + } + + return writeSnapshot(context.Background(), dir, uint32(version), func(w *snapshotWriter) (uint32, error) { + i := &importer{ + snapshotWriter: *w, + } + + for node := range nodes { + if err := i.Add(node); err != nil { + return 0, err + } + } + + switch len(i.leavesStack) { + case 0: + return 0, nil + case 1: + return i.leafCounter, nil + default: + return 0, fmt.Errorf("invalid node structure, found stack size %v after imported", len(i.leavesStack)) + } + }) +} + +type importer struct { + snapshotWriter + + // keep track of how many leaves has been written before the pending nodes + leavesStack []uint32 + // keep track of the pending nodes + nodeStack []*MemNode +} + +func (i *importer) Add(n *ExportNode) error { + if n.Version > int64(math.MaxUint32) { + return errors.New("version overflows uint32") + } + + if n.Height == 0 { + node := &MemNode{ + height: 0, + size: 1, + version: uint32(n.Version), + key: n.Key, + value: n.Value, + } + nodeHash := node.Hash() + if err := i.writeLeaf(node.version, node.key, node.value, nodeHash); err != nil { + return err + } + i.leavesStack = append(i.leavesStack, i.leafCounter) + i.nodeStack = append(i.nodeStack, node) + return nil + } + + // branch node + keyLeaf := i.leavesStack[len(i.leavesStack)-2] + leftNode := i.nodeStack[len(i.nodeStack)-2] + rightNode := i.nodeStack[len(i.nodeStack)-1] + + node := &MemNode{ + height: uint8(n.Height), + size: leftNode.size + rightNode.size, + version: uint32(n.Version), + key: n.Key, + left: leftNode, + right: rightNode, + } + nodeHash := node.Hash() + + // remove unnecessary reference to avoid memory leak + node.left = nil + node.right = nil + + preTrees := uint8(len(i.nodeStack) - 2) + if err := i.writeBranch(node.version, uint32(node.size), node.height, preTrees, keyLeaf, nodeHash); err != nil { + return err + } + + i.leavesStack = i.leavesStack[:len(i.leavesStack)-2] + i.leavesStack = append(i.leavesStack, i.leafCounter) + + i.nodeStack = i.nodeStack[:len(i.nodeStack)-2] + i.nodeStack = append(i.nodeStack, node) + return nil +} + +func updateMetadataFile(dir string, height int64) (returnErr error) { + entries, err := os.ReadDir(dir) + if err != nil { + return err + } + storeInfos := make([]StoreInfo, 0, len(entries)) + for _, e := range entries { + if !e.IsDir() { + continue + } + name := e.Name() + snapshot, err := OpenSnapshot(filepath.Join(dir, name)) + if err != nil { + return err + } + defer func() { + if err := snapshot.Close(); returnErr == nil { + returnErr = err + } + }() + storeInfos = append(storeInfos, StoreInfo{ + Name: name, + CommitId: CommitID{ + Version: height, + Hash: snapshot.RootHash(), + }, + }) + } + metadata := MultiTreeMetadata{ + CommitInfo: &CommitInfo{ + Version: height, + StoreInfos: storeInfos, + }, + // initial version should correspond to the first wal entry + InitialVersion: height + 1, + } + bz, err := metadata.Marshal() + if err != nil { + return err + } + return WriteFileSync(filepath.Join(dir, MetadataFileName), bz) +} diff --git a/memiavl/iterator.go b/memiavl/iterator.go new file mode 100644 index 0000000..d1aa0b0 --- /dev/null +++ b/memiavl/iterator.go @@ -0,0 +1,114 @@ +package memiavl + +import "bytes" + +type Iterator struct { + // domain of iteration, end is exclusive + start, end []byte + ascending bool + zeroCopy bool + + // cache the next key-value pair + key, value []byte + + valid bool + + stack []Node +} + +func NewIterator(start, end []byte, ascending bool, root Node, zeroCopy bool) *Iterator { + iter := &Iterator{ + start: start, + end: end, + ascending: ascending, + valid: true, + zeroCopy: zeroCopy, + } + + if root != nil { + iter.stack = []Node{root} + } + + // cache the first key-value + iter.Next() + return iter +} + +func (iter *Iterator) Domain() ([]byte, []byte) { + return iter.start, iter.end +} + +// Valid implements dbm.Iterator. +func (iter *Iterator) Valid() bool { + return iter.valid +} + +// Error implements dbm.Iterator +func (iter *Iterator) Error() error { + return nil +} + +// Key implements dbm.Iterator +func (iter *Iterator) Key() []byte { + if !iter.zeroCopy { + return bytes.Clone(iter.key) + } + return iter.key +} + +// Value implements dbm.Iterator +func (iter *Iterator) Value() []byte { + if !iter.zeroCopy { + return bytes.Clone(iter.value) + } + return iter.value +} + +// Next implements dbm.Iterator +func (iter *Iterator) Next() { + for len(iter.stack) > 0 { + // pop node + node := iter.stack[len(iter.stack)-1] + iter.stack = iter.stack[:len(iter.stack)-1] + + key := node.Key() + startCmp := bytes.Compare(iter.start, key) + afterStart := iter.start == nil || startCmp < 0 + beforeEnd := iter.end == nil || bytes.Compare(key, iter.end) < 0 + + if node.IsLeaf() { + startOrAfter := afterStart || startCmp == 0 + if startOrAfter && beforeEnd { + iter.key = key + iter.value = node.Value() + return + } + } else { + // push children to stack + if iter.ascending { + if beforeEnd { + iter.stack = append(iter.stack, node.Right()) + } + if afterStart { + iter.stack = append(iter.stack, node.Left()) + } + } else { + if afterStart { + iter.stack = append(iter.stack, node.Left()) + } + if beforeEnd { + iter.stack = append(iter.stack, node.Right()) + } + } + } + } + + iter.valid = false +} + +// Close implements dbm.Iterator +func (iter *Iterator) Close() error { + iter.valid = false + iter.stack = nil + return nil +} diff --git a/memiavl/iterator_test.go b/memiavl/iterator_test.go new file mode 100644 index 0000000..3255d7a --- /dev/null +++ b/memiavl/iterator_test.go @@ -0,0 +1,60 @@ +package memiavl + +import ( + "testing" + + dbm "github.com/cosmos/cosmos-db" + "github.com/stretchr/testify/require" +) + +func TestIterator(t *testing.T) { + tree := New(0) + require.Equal(t, ExpectItems[0], collectIter(tree.Iterator(nil, nil, true))) + + for _, changes := range ChangeSets { + tree.ApplyChangeSet(changes) + _, v, err := tree.SaveVersion(true) + require.NoError(t, err) + require.Equal(t, ExpectItems[v], collectIter(tree.Iterator(nil, nil, true))) + require.Equal(t, reverse(ExpectItems[v]), collectIter(tree.Iterator(nil, nil, false))) + } +} + +func TestIteratorRange(t *testing.T) { + tree := New(0) + for _, changes := range ChangeSets[:6] { + tree.ApplyChangeSet(changes) + _, _, err := tree.SaveVersion(true) + require.NoError(t, err) + } + + expItems := []pair{ + {[]byte("aello05"), []byte("world1")}, + {[]byte("aello06"), []byte("world1")}, + {[]byte("aello07"), []byte("world1")}, + {[]byte("aello08"), []byte("world1")}, + {[]byte("aello09"), []byte("world1")}, + } + require.Equal(t, expItems, collectIter(tree.Iterator([]byte("aello05"), []byte("aello10"), true))) + require.Equal(t, reverse(expItems), collectIter(tree.Iterator([]byte("aello05"), []byte("aello10"), false))) +} + +type pair struct { + key, value []byte +} + +func collectIter(iter dbm.Iterator) []pair { + result := []pair{} + for ; iter.Valid(); iter.Next() { + result = append(result, pair{key: iter.Key(), value: iter.Value()}) + } + return result +} + +func reverse[S ~[]E, E any](s S) S { + r := make(S, len(s)) + for i, j := 0, len(s)-1; i <= j; i, j = i+1, j-1 { + r[i], r[j] = s[j], s[i] + } + return r +} diff --git a/memiavl/kv.pb.go b/memiavl/kv.pb.go new file mode 100644 index 0000000..5528933 --- /dev/null +++ b/memiavl/kv.pb.go @@ -0,0 +1,558 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: memiavl/kv.proto + +// vendored from cosmos.store.internal.kv.v1beta1; + +package memiavl + +import ( + fmt "fmt" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// Pairs defines a repeated slice of Pair objects. +type Pairs struct { + Pairs []Pair `protobuf:"bytes,1,rep,name=pairs,proto3" json:"pairs"` +} + +func (m *Pairs) Reset() { *m = Pairs{} } +func (m *Pairs) String() string { return proto.CompactTextString(m) } +func (*Pairs) ProtoMessage() {} +func (*Pairs) Descriptor() ([]byte, []int) { + return fileDescriptor_3fb942e3f05db0d2, []int{0} +} +func (m *Pairs) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Pairs) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Pairs.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Pairs) XXX_Merge(src proto.Message) { + xxx_messageInfo_Pairs.Merge(m, src) +} +func (m *Pairs) XXX_Size() int { + return m.Size() +} +func (m *Pairs) XXX_DiscardUnknown() { + xxx_messageInfo_Pairs.DiscardUnknown(m) +} + +var xxx_messageInfo_Pairs proto.InternalMessageInfo + +func (m *Pairs) GetPairs() []Pair { + if m != nil { + return m.Pairs + } + return nil +} + +// Pair defines a key/value bytes tuple. +type Pair struct { + Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` +} + +func (m *Pair) Reset() { *m = Pair{} } +func (m *Pair) String() string { return proto.CompactTextString(m) } +func (*Pair) ProtoMessage() {} +func (*Pair) Descriptor() ([]byte, []int) { + return fileDescriptor_3fb942e3f05db0d2, []int{1} +} +func (m *Pair) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Pair) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Pair.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Pair) XXX_Merge(src proto.Message) { + xxx_messageInfo_Pair.Merge(m, src) +} +func (m *Pair) XXX_Size() int { + return m.Size() +} +func (m *Pair) XXX_DiscardUnknown() { + xxx_messageInfo_Pair.DiscardUnknown(m) +} + +var xxx_messageInfo_Pair proto.InternalMessageInfo + +func (m *Pair) GetKey() []byte { + if m != nil { + return m.Key + } + return nil +} + +func (m *Pair) GetValue() []byte { + if m != nil { + return m.Value + } + return nil +} + +func init() { + proto.RegisterType((*Pairs)(nil), "memiavl.Pairs") + proto.RegisterType((*Pair)(nil), "memiavl.Pair") +} + +func init() { proto.RegisterFile("memiavl/kv.proto", fileDescriptor_3fb942e3f05db0d2) } + +var fileDescriptor_3fb942e3f05db0d2 = []byte{ + // 206 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0xc8, 0x4d, 0xcd, 0xcd, + 0x4c, 0x2c, 0xcb, 0xd1, 0xcf, 0x2e, 0xd3, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x87, 0x8a, + 0x48, 0x89, 0xa4, 0xe7, 0xa7, 0xe7, 0x83, 0xc5, 0xf4, 0x41, 0x2c, 0x88, 0xb4, 0x92, 0x11, 0x17, + 0x6b, 0x40, 0x62, 0x66, 0x51, 0xb1, 0x90, 0x26, 0x17, 0x6b, 0x01, 0x88, 0x21, 0xc1, 0xa8, 0xc0, + 0xac, 0xc1, 0x6d, 0xc4, 0xab, 0x07, 0xd5, 0xa7, 0x07, 0x92, 0x76, 0x62, 0x39, 0x71, 0x4f, 0x9e, + 0x21, 0x08, 0xa2, 0x42, 0x49, 0x8f, 0x8b, 0x05, 0x24, 0x28, 0x24, 0xc0, 0xc5, 0x9c, 0x9d, 0x5a, + 0x29, 0xc1, 0xa8, 0xc0, 0xa8, 0xc1, 0x13, 0x04, 0x62, 0x0a, 0x89, 0x70, 0xb1, 0x96, 0x25, 0xe6, + 0x94, 0xa6, 0x4a, 0x30, 0x81, 0xc5, 0x20, 0x1c, 0x27, 0x97, 0x13, 0x8f, 0xe4, 0x18, 0x2f, 0x3c, + 0x92, 0x63, 0x7c, 0xf0, 0x48, 0x8e, 0x71, 0xc2, 0x63, 0x39, 0x86, 0x0b, 0x8f, 0xe5, 0x18, 0x6e, + 0x3c, 0x96, 0x63, 0x88, 0xd2, 0x4a, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, + 0x4f, 0x2e, 0xaa, 0x2c, 0x28, 0xc9, 0xd7, 0xcd, 0x2f, 0x4a, 0xd7, 0x4d, 0xce, 0x48, 0xcc, 0xcc, + 0xd3, 0x4f, 0x2e, 0xca, 0xcf, 0xcb, 0x2f, 0xd6, 0x87, 0xba, 0x23, 0x89, 0x0d, 0xec, 0x60, 0x63, + 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0x40, 0xbe, 0xe7, 0x7c, 0xe3, 0x00, 0x00, 0x00, +} + +func (m *Pairs) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Pairs) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Pairs) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Pairs) > 0 { + for iNdEx := len(m.Pairs) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Pairs[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintKv(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *Pair) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Pair) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Pair) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Value) > 0 { + i -= len(m.Value) + copy(dAtA[i:], m.Value) + i = encodeVarintKv(dAtA, i, uint64(len(m.Value))) + i-- + dAtA[i] = 0x12 + } + if len(m.Key) > 0 { + i -= len(m.Key) + copy(dAtA[i:], m.Key) + i = encodeVarintKv(dAtA, i, uint64(len(m.Key))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintKv(dAtA []byte, offset int, v uint64) int { + offset -= sovKv(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *Pairs) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Pairs) > 0 { + for _, e := range m.Pairs { + l = e.Size() + n += 1 + l + sovKv(uint64(l)) + } + } + return n +} + +func (m *Pair) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Key) + if l > 0 { + n += 1 + l + sovKv(uint64(l)) + } + l = len(m.Value) + if l > 0 { + n += 1 + l + sovKv(uint64(l)) + } + return n +} + +func sovKv(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozKv(x uint64) (n int) { + return sovKv(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *Pairs) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowKv + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Pairs: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Pairs: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pairs", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowKv + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthKv + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthKv + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Pairs = append(m.Pairs, Pair{}) + if err := m.Pairs[len(m.Pairs)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipKv(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthKv + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Pair) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowKv + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Pair: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Pair: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowKv + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthKv + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthKv + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Key = append(m.Key[:0], dAtA[iNdEx:postIndex]...) + if m.Key == nil { + m.Key = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowKv + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthKv + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthKv + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Value = append(m.Value[:0], dAtA[iNdEx:postIndex]...) + if m.Value == nil { + m.Value = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipKv(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthKv + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipKv(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowKv + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowKv + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowKv + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthKv + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupKv + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthKv + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthKv = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowKv = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupKv = fmt.Errorf("proto: unexpected end of group") +) diff --git a/memiavl/layout_little_endian.go b/memiavl/layout_little_endian.go new file mode 100644 index 0000000..51c0f3e --- /dev/null +++ b/memiavl/layout_little_endian.go @@ -0,0 +1,85 @@ +//go:build !nativebyteorder +// +build !nativebyteorder + +package memiavl + +import ( + "encoding/binary" +) + +// Nodes is a continuously stored IAVL nodes +type Nodes struct { + data []byte +} + +func NewNodes(data []byte) (Nodes, error) { + return Nodes{data}, nil +} + +func (nodes Nodes) Node(i uint32) NodeLayout { + offset := int(i) * SizeNode + return NodeLayout{data: (*[SizeNode]byte)(nodes.data[offset : offset+SizeNode])} +} + +// see comment of `PersistedNode` +type NodeLayout struct { + data *[SizeNode]byte +} + +func (node NodeLayout) Height() uint8 { + return node.data[OffsetHeight] +} + +func (node NodeLayout) PreTrees() uint8 { + return node.data[OffsetPreTrees] +} + +func (node NodeLayout) Version() uint32 { + return binary.LittleEndian.Uint32(node.data[OffsetVersion : OffsetVersion+4]) +} + +func (node NodeLayout) Size() uint32 { + return binary.LittleEndian.Uint32(node.data[OffsetSize : OffsetSize+4]) +} + +func (node NodeLayout) KeyLeaf() uint32 { + return binary.LittleEndian.Uint32(node.data[OffsetKeyLeaf : OffsetKeyLeaf+4]) +} + +func (node NodeLayout) Hash() []byte { + return node.data[OffsetHash : OffsetHash+SizeHash] +} + +// Leaves is a continuously stored IAVL nodes +type Leaves struct { + data []byte +} + +func NewLeaves(data []byte) (Leaves, error) { + return Leaves{data}, nil +} + +func (leaves Leaves) Leaf(i uint32) LeafLayout { + offset := int(i) * SizeLeaf + return LeafLayout{data: (*[SizeLeaf]byte)(leaves.data[offset : offset+SizeLeaf])} +} + +type LeafLayout struct { + data *[SizeLeaf]byte +} + +func (leaf LeafLayout) Version() uint32 { + return binary.LittleEndian.Uint32(leaf.data[OffsetLeafVersion : OffsetLeafVersion+4]) +} + +func (leaf LeafLayout) KeyLength() uint32 { + return binary.LittleEndian.Uint32(leaf.data[OffsetLeafKeyLen : OffsetLeafKeyLen+4]) +} + +func (leaf LeafLayout) KeyOffset() uint64 { + return binary.LittleEndian.Uint64(leaf.data[OffsetLeafKeyOffset : OffsetLeafKeyOffset+8]) +} + +func (leaf LeafLayout) Hash() []byte { + return leaf.data[OffsetLeafHash : OffsetLeafHash+32] +} diff --git a/memiavl/layout_native.go b/memiavl/layout_native.go new file mode 100644 index 0000000..34b6134 --- /dev/null +++ b/memiavl/layout_native.go @@ -0,0 +1,125 @@ +//go:build nativebyteorder +// +build nativebyteorder + +package memiavl + +import ( + "errors" + "unsafe" +) + +func init() { + buf := [2]byte{} + *(*uint16)(unsafe.Pointer(&buf[0])) = uint16(0xABCD) + + if buf != [2]byte{0xCD, 0xAB} { + panic("native byte order is not little endian, please build without nativebyteorder") + } +} + +type NodeLayout = *nodeLayout + +// Nodes is a continuously stored IAVL nodes +type Nodes struct { + nodes []nodeLayout +} + +func NewNodes(buf []byte) (Nodes, error) { + // check alignment and size of the buffer + p := unsafe.Pointer(unsafe.SliceData(buf)) + if uintptr(p)%unsafe.Alignof(nodeLayout{}) != 0 { + return Nodes{}, errors.New("input buffer is not aligned") + } + size := int(unsafe.Sizeof(nodeLayout{})) + if len(buf)%size != 0 { + return Nodes{}, errors.New("input buffer length is not correct") + } + nodes := unsafe.Slice((*nodeLayout)(p), len(buf)/size) + return Nodes{nodes}, nil +} + +func (nodes Nodes) Node(i uint32) NodeLayout { + return &nodes.nodes[i] +} + +// see comment of `PersistedNode` +type nodeLayout struct { + data [4]uint32 + hash [32]byte +} + +func (node *nodeLayout) Height() uint8 { + return uint8(node.data[0]) +} + +func (node NodeLayout) PreTrees() uint8 { + return uint8(node.data[0] >> 8) +} + +func (node *nodeLayout) Version() uint32 { + return node.data[1] +} + +func (node *nodeLayout) Size() uint32 { + return node.data[2] +} + +func (node *nodeLayout) KeyLeaf() uint32 { + return node.data[3] +} + +func (node *nodeLayout) KeyOffset() uint64 { + return uint64(node.data[2]) | uint64(node.data[3])<<32 +} + +func (node *nodeLayout) Hash() []byte { + return node.hash[:] +} + +type LeafLayout = *leafLayout + +// Nodes is a continuously stored IAVL nodes +type Leaves struct { + leaves []leafLayout +} + +func NewLeaves(buf []byte) (Leaves, error) { + // check alignment and size of the buffer + p := unsafe.Pointer(unsafe.SliceData(buf)) + if uintptr(p)%unsafe.Alignof(leafLayout{}) != 0 { + return Leaves{}, errors.New("input buffer is not aligned") + } + size := int(unsafe.Sizeof(leafLayout{})) + if len(buf)%size != 0 { + return Leaves{}, errors.New("input buffer length is not correct") + } + leaves := unsafe.Slice((*leafLayout)(p), len(buf)/size) + return Leaves{leaves}, nil +} + +func (leaves Leaves) Leaf(i uint32) LeafLayout { + return &leaves.leaves[i] +} + +type leafLayout struct { + version uint32 + keyLen uint32 + keyOffset uint64 + hash [32]byte +} + +func (leaf *leafLayout) Version() uint32 { + return leaf.version +} + +func (leaf *leafLayout) KeyLength() uint32 { + return leaf.keyLen +} + +func (leaf *leafLayout) KeyOffset() uint64 { + return leaf.keyOffset +} + +func (leaf *leafLayout) Hash() []byte { + return leaf.hash[:] +} diff --git a/memiavl/mem_node.go b/memiavl/mem_node.go new file mode 100644 index 0000000..f56e345 --- /dev/null +++ b/memiavl/mem_node.go @@ -0,0 +1,218 @@ +package memiavl + +import ( + "bytes" + "encoding/binary" + "io" +) + +type MemNode struct { + height uint8 + size int64 + version uint32 + key []byte + value []byte + left Node + right Node + + hash []byte +} + +var _ Node = (*MemNode)(nil) + +func newLeafNode(key, value []byte, version uint32) *MemNode { + return &MemNode{ + key: key, value: value, version: version, size: 1, + } +} + +func (node *MemNode) Height() uint8 { + return node.height +} + +func (node *MemNode) IsLeaf() bool { + return node.height == 0 +} + +func (node *MemNode) Size() int64 { + return node.size +} + +func (node *MemNode) Version() uint32 { + return node.version +} + +func (node *MemNode) Key() []byte { + return node.key +} + +func (node *MemNode) Value() []byte { + return node.value +} + +func (node *MemNode) Left() Node { + return node.left +} + +func (node *MemNode) Right() Node { + return node.right +} + +// Mutate clones the node if it's version is smaller than or equal to cowVersion, otherwise modify in-place +func (node *MemNode) Mutate(version, cowVersion uint32) *MemNode { + n := node + if node.version <= cowVersion { + cloned := *node + n = &cloned + } + n.version = version + n.hash = nil + return n +} + +func (node *MemNode) SafeHash() []byte { + return node.Hash() +} + +// Computes the hash of the node without computing its descendants. Must be +// called on nodes which have descendant node hashes already computed. +func (node *MemNode) Hash() []byte { + if node == nil { + return nil + } + if node.hash != nil { + return node.hash + } + node.hash = HashNode(node) + return node.hash +} + +func (node *MemNode) updateHeightSize() { + node.height = maxUInt8(node.left.Height(), node.right.Height()) + 1 + node.size = node.left.Size() + node.right.Size() +} + +func (node *MemNode) calcBalance() int { + return int(node.left.Height()) - int(node.right.Height()) +} + +func calcBalance(node Node) int { + return int(node.Left().Height()) - int(node.Right().Height()) +} + +// Invariant: node is returned by `Mutate(version)`. +// +// S L +// / \ => / \ +// L S +// / \ / \ +// LR LR +func (node *MemNode) rotateRight(version, cowVersion uint32) *MemNode { + newSelf := node.left.Mutate(version, cowVersion) + node.left = node.left.Right() + newSelf.right = node + node.updateHeightSize() + newSelf.updateHeightSize() + return newSelf +} + +// Invariant: node is returned by `Mutate(version, cowVersion)`. +// +// S R +// / \ => / \ +// R S +// / \ / \ +// RL RL +func (node *MemNode) rotateLeft(version, cowVersion uint32) *MemNode { + newSelf := node.right.Mutate(version, cowVersion) + node.right = node.right.Left() + newSelf.left = node + node.updateHeightSize() + newSelf.updateHeightSize() + return newSelf +} + +// Invariant: node is returned by `Mutate(version, cowVersion)`. +func (node *MemNode) reBalance(version, cowVersion uint32) *MemNode { + balance := node.calcBalance() + switch { + case balance > 1: + leftBalance := calcBalance(node.left) + if leftBalance >= 0 { + // left left + return node.rotateRight(version, cowVersion) + } + // left right + node.left = node.left.Mutate(version, cowVersion).rotateLeft(version, cowVersion) + return node.rotateRight(version, cowVersion) + case balance < -1: + rightBalance := calcBalance(node.right) + if rightBalance <= 0 { + // right right + return node.rotateLeft(version, cowVersion) + } + // right left + node.right = node.right.Mutate(version, cowVersion).rotateRight(version, cowVersion) + return node.rotateLeft(version, cowVersion) + default: + // nothing changed + return node + } +} + +func (node *MemNode) Get(key []byte) ([]byte, uint32) { + if node.IsLeaf() { + switch bytes.Compare(node.key, key) { + case -1: + return nil, 1 + case 1: + return nil, 0 + default: + return node.value, 0 + } + } + + if bytes.Compare(key, node.key) == -1 { + return node.Left().Get(key) + } + right := node.Right() + value, index := right.Get(key) + return value, index + uint32(node.Size()) - uint32(right.Size()) +} + +func (node *MemNode) GetByIndex(index uint32) ([]byte, []byte) { + if node.IsLeaf() { + if index == 0 { + return node.key, node.value + } + return nil, nil + } + + left := node.Left() + leftSize := uint32(left.Size()) + if index < leftSize { + return left.GetByIndex(index) + } + + right := node.Right() + return right.GetByIndex(index - leftSize) +} + +// EncodeBytes writes a varint length-prefixed byte slice to the writer, +// it's used for hash computation, must be compactible with the official IAVL implementation. +func EncodeBytes(w io.Writer, bz []byte) error { + var buf [binary.MaxVarintLen64]byte + n := binary.PutUvarint(buf[:], uint64(len(bz))) + if _, err := w.Write(buf[0:n]); err != nil { + return err + } + _, err := w.Write(bz) + return err +} + +func maxUInt8(a, b uint8) uint8 { + if a > b { + return a + } + return b +} diff --git a/memiavl/mmap.go b/memiavl/mmap.go new file mode 100644 index 0000000..402156a --- /dev/null +++ b/memiavl/mmap.go @@ -0,0 +1,60 @@ +package memiavl + +import ( + "errors" + "os" + + "github.com/ledgerwatch/erigon-lib/mmap" +) + +// MmapFile manage the resources of a mmap-ed file +type MmapFile struct { + file *os.File + data []byte + // mmap handle for windows (this is used to close mmap) + handle *[mmap.MaxMapSize]byte +} + +// Open openes the file and create the mmap. +// the mmap is created with flags: PROT_READ, MAP_SHARED, MADV_RANDOM. +func NewMmap(path string) (*MmapFile, error) { + file, err := os.Open(path) + if err != nil { + return nil, err + } + + data, handle, err := Mmap(file) + if err != nil { + _ = file.Close() + return nil, err + } + + return &MmapFile{ + file: file, + data: data, + handle: handle, + }, nil +} + +// Close closes the file and mmap handles +func (m *MmapFile) Close() error { + var err error + if m.handle != nil { + err = mmap.Munmap(m.data, m.handle) + } + return errors.Join(err, m.file.Close()) +} + +// Data returns the mmap-ed buffer +func (m *MmapFile) Data() []byte { + return m.data +} + +func Mmap(f *os.File) ([]byte, *[mmap.MaxMapSize]byte, error) { + fi, err := f.Stat() + if err != nil || fi.Size() == 0 { + return nil, nil, err + } + + return mmap.Mmap(f, int(fi.Size())) +} diff --git a/memiavl/multitree.go b/memiavl/multitree.go new file mode 100644 index 0000000..866123b --- /dev/null +++ b/memiavl/multitree.go @@ -0,0 +1,462 @@ +package memiavl + +import ( + "context" + "errors" + "fmt" + "math" + "os" + "path/filepath" + "sort" + + "github.com/alitto/pond" + "github.com/tidwall/wal" + "golang.org/x/exp/slices" +) + +const MetadataFileName = "__metadata" + +type NamedTree struct { + *Tree + Name string +} + +// MultiTree manages multiple memiavl tree together, +// all the trees share the same latest version, the snapshots are always created at the same version. +// +// The snapshot structure is like this: +// ``` +// > snapshot-V +// > metadata +// > bank +// > kvs +// > nodes +// > metadata +// > acc +// > other stores... +// ``` +type MultiTree struct { + // if the tree is start from genesis, it's the initial version of the chain, + // if the tree is imported from snapshot, it's the imported version plus one, + // it always corresponds to the wal entry with index 1. + initialVersion uint32 + + zeroCopy bool + cacheSize int + + trees []NamedTree // always ordered by tree name + treesByName map[string]int // index of the trees by name + lastCommitInfo CommitInfo + + // the initial metadata loaded from disk snapshot + metadata MultiTreeMetadata +} + +func NewEmptyMultiTree(initialVersion uint32, cacheSize int) *MultiTree { + return &MultiTree{ + initialVersion: initialVersion, + treesByName: make(map[string]int), + zeroCopy: true, + cacheSize: cacheSize, + } +} + +func LoadMultiTree(dir string, zeroCopy bool, cacheSize int) (*MultiTree, error) { + metadata, err := readMetadata(dir) + if err != nil { + return nil, err + } + + entries, err := os.ReadDir(dir) + if err != nil { + return nil, err + } + + treeMap := make(map[string]*Tree, len(entries)) + treeNames := make([]string, 0, len(entries)) + for _, e := range entries { + if !e.IsDir() { + continue + } + name := e.Name() + treeNames = append(treeNames, name) + snapshot, err := OpenSnapshot(filepath.Join(dir, name)) + if err != nil { + return nil, err + } + treeMap[name] = NewFromSnapshot(snapshot, zeroCopy, cacheSize) + } + + slices.Sort(treeNames) + + trees := make([]NamedTree, len(treeNames)) + treesByName := make(map[string]int, len(trees)) + for i, name := range treeNames { + tree := treeMap[name] + trees[i] = NamedTree{Tree: tree, Name: name} + treesByName[name] = i + } + + mtree := &MultiTree{ + trees: trees, + treesByName: treesByName, + lastCommitInfo: *metadata.CommitInfo, + metadata: *metadata, + zeroCopy: zeroCopy, + cacheSize: cacheSize, + } + // initial version is nesserary for wal index conversion + mtree.setInitialVersion(metadata.InitialVersion) + return mtree, nil +} + +// TreeByName returns the tree by name, returns nil if not found +func (t *MultiTree) TreeByName(name string) *Tree { + if i, ok := t.treesByName[name]; ok { + return t.trees[i].Tree + } + return nil +} + +// Trees returns all the trees together with the name, ordered by name. +func (t *MultiTree) Trees() []NamedTree { + return t.trees +} + +func (t *MultiTree) SetInitialVersion(initialVersion int64) error { + if initialVersion >= math.MaxUint32 { + return fmt.Errorf("version overflows uint32: %d", initialVersion) + } + + if t.Version() != 0 { + return fmt.Errorf("multi tree is not empty: %d", t.Version()) + } + + for _, entry := range t.trees { + if !entry.Tree.IsEmpty() { + return fmt.Errorf("tree is not empty: %s", entry.Name) + } + } + + t.setInitialVersion(initialVersion) + return nil +} + +func (t *MultiTree) setInitialVersion(initialVersion int64) { + t.initialVersion = uint32(initialVersion) + for _, entry := range t.trees { + entry.Tree.initialVersion = t.initialVersion + } +} + +func (t *MultiTree) SetZeroCopy(zeroCopy bool) { + t.zeroCopy = zeroCopy + for _, entry := range t.trees { + entry.Tree.SetZeroCopy(zeroCopy) + } +} + +// Copy returns a snapshot of the tree which won't be corrupted by further modifications on the main tree. +func (t *MultiTree) Copy(cacheSize int) *MultiTree { + trees := make([]NamedTree, len(t.trees)) + treesByName := make(map[string]int, len(t.trees)) + for i, entry := range t.trees { + tree := entry.Tree.Copy(cacheSize) + trees[i] = NamedTree{Tree: tree, Name: entry.Name} + treesByName[entry.Name] = i + } + + clone := *t + clone.trees = trees + clone.treesByName = treesByName + return &clone +} + +func (t *MultiTree) Version() int64 { + return t.lastCommitInfo.Version +} + +func (t *MultiTree) SnapshotVersion() int64 { + return t.metadata.CommitInfo.Version +} + +func (t *MultiTree) LastCommitInfo() *CommitInfo { + return &t.lastCommitInfo +} + +func (t *MultiTree) applyWALEntry(entry WALEntry) error { + if err := t.ApplyUpgrades(entry.Upgrades); err != nil { + return err + } + return t.ApplyChangeSets(entry.Changesets) +} + +// ApplyUpgrades store name upgrades +func (t *MultiTree) ApplyUpgrades(upgrades []*TreeNameUpgrade) error { + if len(upgrades) == 0 { + return nil + } + + t.treesByName = nil // rebuild in the end + + for _, upgrade := range upgrades { + switch { + case upgrade.Delete: + i := slices.IndexFunc(t.trees, func(entry NamedTree) bool { + return entry.Name == upgrade.Name + }) + if i < 0 { + return fmt.Errorf("unknown tree name %s", upgrade.Name) + } + // swap deletion + t.trees[i], t.trees[len(t.trees)-1] = t.trees[len(t.trees)-1], t.trees[i] + t.trees = t.trees[:len(t.trees)-1] + case upgrade.RenameFrom != "": + // rename tree + i := slices.IndexFunc(t.trees, func(entry NamedTree) bool { + return entry.Name == upgrade.RenameFrom + }) + if i < 0 { + return fmt.Errorf("unknown tree name %s", upgrade.RenameFrom) + } + t.trees[i].Name = upgrade.Name + default: + // add tree + tree := NewWithInitialVersion(uint32(nextVersion(t.Version(), t.initialVersion)), t.cacheSize) + t.trees = append(t.trees, NamedTree{Tree: tree, Name: upgrade.Name}) + } + } + + sort.SliceStable(t.trees, func(i, j int) bool { + return t.trees[i].Name < t.trees[j].Name + }) + t.treesByName = make(map[string]int, len(t.trees)) + for i, tree := range t.trees { + if _, ok := t.treesByName[tree.Name]; ok { + return fmt.Errorf("memiavl tree name conflicts: %s", tree.Name) + } + t.treesByName[tree.Name] = i + } + + return nil +} + +// ApplyChangeSet applies change set for a single tree. +func (t *MultiTree) ApplyChangeSet(name string, changeSet ChangeSet) error { + i, found := t.treesByName[name] + if !found { + return fmt.Errorf("unknown tree name %s", name) + } + t.trees[i].Tree.ApplyChangeSet(changeSet) + return nil +} + +// ApplyChangeSets applies change sets for multiple trees. +func (t *MultiTree) ApplyChangeSets(changeSets []*NamedChangeSet) error { + for _, cs := range changeSets { + if err := t.ApplyChangeSet(cs.Name, cs.Changeset); err != nil { + return err + } + } + return nil +} + +// WorkingCommitInfo returns the commit info for the working tree +func (t *MultiTree) WorkingCommitInfo() *CommitInfo { + version := nextVersion(t.lastCommitInfo.Version, t.initialVersion) + return t.buildCommitInfo(version) +} + +// SaveVersion bumps the versions of all the stores and optionally returns the new app hash +func (t *MultiTree) SaveVersion(updateCommitInfo bool) (int64, error) { + t.lastCommitInfo.Version = nextVersion(t.lastCommitInfo.Version, t.initialVersion) + for _, entry := range t.trees { + if _, _, err := entry.Tree.SaveVersion(updateCommitInfo); err != nil { + return 0, err + } + } + + if updateCommitInfo { + t.UpdateCommitInfo() + } else { + // clear the dirty information + t.lastCommitInfo.StoreInfos = []StoreInfo{} + } + + return t.lastCommitInfo.Version, nil +} + +func (t *MultiTree) buildCommitInfo(version int64) *CommitInfo { + var infos []StoreInfo + for _, entry := range t.trees { + infos = append(infos, StoreInfo{ + Name: entry.Name, + CommitId: CommitID{ + Version: entry.Tree.Version(), + Hash: entry.Tree.RootHash(), + }, + }) + } + + return &CommitInfo{ + Version: version, + StoreInfos: infos, + } +} + +// UpdateCommitInfo update lastCommitInfo based on current status of trees. +// it's needed if `updateCommitInfo` is set to `false` in `ApplyChangeSet`. +func (t *MultiTree) UpdateCommitInfo() { + t.lastCommitInfo = *t.buildCommitInfo(t.lastCommitInfo.Version) +} + +// CatchupWAL replay the new entries in the WAL on the tree to catch-up to the target or latest version. +func (t *MultiTree) CatchupWAL(wal *wal.Log, endVersion int64) error { + lastIndex, err := wal.LastIndex() + if err != nil { + return fmt.Errorf("read wal last index failed, %w", err) + } + + firstIndex := walIndex(nextVersion(t.Version(), t.initialVersion), t.initialVersion) + if firstIndex > lastIndex { + // already up-to-date + return nil + } + + endIndex := lastIndex + if endVersion != 0 { + endIndex = walIndex(endVersion, t.initialVersion) + } + + if endIndex < firstIndex { + return fmt.Errorf("target index %d is pruned", endIndex) + } + + if endIndex > lastIndex { + return fmt.Errorf("target index %d is in the future, latest index: %d", endIndex, lastIndex) + } + + for i := firstIndex; i <= endIndex; i++ { + bz, err := wal.Read(i) + if err != nil { + return fmt.Errorf("read wal log failed, %w", err) + } + var entry WALEntry + if err := entry.Unmarshal(bz); err != nil { + return fmt.Errorf("unmarshal wal log failed, %w", err) + } + if err := t.applyWALEntry(entry); err != nil { + return fmt.Errorf("replay wal entry failed, %w", err) + } + if _, err := t.SaveVersion(false); err != nil { + return fmt.Errorf("replay change set failed, %w", err) + } + } + t.UpdateCommitInfo() + return nil +} + +func (t *MultiTree) WriteSnapshot(dir string, wp *pond.WorkerPool) error { + return t.WriteSnapshotWithContext(context.Background(), dir, wp) +} + +func (t *MultiTree) WriteSnapshotWithContext(ctx context.Context, dir string, wp *pond.WorkerPool) error { + if err := os.MkdirAll(dir, os.ModePerm); err != nil { + return err + } + + // write the snapshots in parallel and wait all jobs done + group, _ := wp.GroupContext(context.Background()) + + for _, entry := range t.trees { + tree, name := entry.Tree, entry.Name + group.Submit(func() error { + return tree.WriteSnapshotWithContext(ctx, filepath.Join(dir, name)) + }) + } + + if err := group.Wait(); err != nil { + return err + } + + // write commit info + metadata := MultiTreeMetadata{ + CommitInfo: &t.lastCommitInfo, + InitialVersion: int64(t.initialVersion), + } + bz, err := metadata.Marshal() + if err != nil { + return err + } + return WriteFileSync(filepath.Join(dir, MetadataFileName), bz) +} + +// WriteFileSync calls `f.Sync` after before closing the file +func WriteFileSync(name string, data []byte) error { + f, err := os.OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.ModePerm) + if err != nil { + return err + } + _, err = f.Write(data) + if err == nil { + err = f.Sync() + } + if err1 := f.Close(); err1 != nil && err == nil { + err = err1 + } + return err +} + +func (t *MultiTree) Close() error { + errs := make([]error, 0, len(t.trees)) + for _, entry := range t.trees { + errs = append(errs, entry.Tree.Close()) + } + t.trees = nil + t.treesByName = nil + t.lastCommitInfo = CommitInfo{} + return errors.Join(errs...) +} + +func nextVersion(v int64, initialVersion uint32) int64 { + if v == 0 && initialVersion > 1 { + return int64(initialVersion) + } + return v + 1 +} + +// walIndex converts version to wal index based on initial version +func walIndex(v int64, initialVersion uint32) uint64 { + if initialVersion > 1 { + return uint64(v) - uint64(initialVersion) + 1 + } + return uint64(v) +} + +// walVersion converts wal index to version, reverse of walIndex +func walVersion(index uint64, initialVersion uint32) int64 { + if initialVersion > 1 { + return int64(index) + int64(initialVersion) - 1 + } + return int64(index) +} + +func readMetadata(dir string) (*MultiTreeMetadata, error) { + // load commit info + bz, err := os.ReadFile(filepath.Join(dir, MetadataFileName)) + if err != nil { + return nil, err + } + var metadata MultiTreeMetadata + if err := metadata.Unmarshal(bz); err != nil { + return nil, err + } + if metadata.CommitInfo.Version > math.MaxUint32 { + return nil, fmt.Errorf("commit info version overflows uint32: %d", metadata.CommitInfo.Version) + } + if metadata.InitialVersion > math.MaxUint32 { + return nil, fmt.Errorf("initial version overflows uint32: %d", metadata.InitialVersion) + } + + return &metadata, nil +} diff --git a/memiavl/node.go b/memiavl/node.go new file mode 100644 index 0000000..6f65931 --- /dev/null +++ b/memiavl/node.go @@ -0,0 +1,201 @@ +package memiavl + +import ( + "bytes" + "crypto/sha256" + "encoding/binary" + "fmt" + "io" +) + +// Node interface encapsulate the interface of both PersistedNode and MemNode. +type Node interface { + Height() uint8 + IsLeaf() bool + Size() int64 + Version() uint32 + Key() []byte + Value() []byte + Left() Node + Right() Node + Hash() []byte + + // SafeHash returns byte slice that's safe to retain + SafeHash() []byte + + // PersistedNode clone a new node, MemNode modify in place + Mutate(version, cowVersion uint32) *MemNode + + // Get query the value for a key, it's put into interface because a specialized implementation is more efficient. + Get(key []byte) ([]byte, uint32) + GetByIndex(uint32) ([]byte, []byte) +} + +// setRecursive do set operation. +// it always do modification and return new `MemNode`, even if the value is the same. +// also returns if it's an update or insertion, if update, the tree height and balance is not changed. +func setRecursive(node Node, key, value []byte, version, cowVersion uint32) (*MemNode, bool) { + if node == nil { + return newLeafNode(key, value, version), true + } + + nodeKey := node.Key() + if node.IsLeaf() { + switch bytes.Compare(key, nodeKey) { + case -1: + return &MemNode{ + height: 1, + size: 2, + version: version, + key: nodeKey, + left: newLeafNode(key, value, version), + right: node, + }, false + case 1: + return &MemNode{ + height: 1, + size: 2, + version: version, + key: key, + left: node, + right: newLeafNode(key, value, version), + }, false + default: + newNode := node.Mutate(version, cowVersion) + newNode.value = value + return newNode, true + } + } else { + var ( + newChild, newNode *MemNode + updated bool + ) + if bytes.Compare(key, nodeKey) == -1 { + newChild, updated = setRecursive(node.Left(), key, value, version, cowVersion) + newNode = node.Mutate(version, cowVersion) + newNode.left = newChild + } else { + newChild, updated = setRecursive(node.Right(), key, value, version, cowVersion) + newNode = node.Mutate(version, cowVersion) + newNode.right = newChild + } + + if !updated { + newNode.updateHeightSize() + newNode = newNode.reBalance(version, cowVersion) + } + + return newNode, updated + } +} + +// removeRecursive returns: +// - (nil, origNode, nil) -> nothing changed in subtree +// - (value, nil, newKey) -> leaf node is removed +// - (value, new node, newKey) -> subtree changed +func removeRecursive(node Node, key []byte, version, cowVersion uint32) ([]byte, Node, []byte) { + if node == nil { + return nil, nil, nil + } + + if node.IsLeaf() { + if bytes.Equal(node.Key(), key) { + return node.Value(), nil, nil + } + return nil, node, nil + } + + if bytes.Compare(key, node.Key()) == -1 { + value, newLeft, newKey := removeRecursive(node.Left(), key, version, cowVersion) + if value == nil { + return nil, node, nil + } + if newLeft == nil { + return value, node.Right(), node.Key() + } + newNode := node.Mutate(version, cowVersion) + newNode.left = newLeft + newNode.updateHeightSize() + return value, newNode.reBalance(version, cowVersion), newKey + } + + value, newRight, newKey := removeRecursive(node.Right(), key, version, cowVersion) + if value == nil { + return nil, node, nil + } + if newRight == nil { + return value, node.Left(), nil + } + + newNode := node.Mutate(version, cowVersion) + newNode.right = newRight + if newKey != nil { + newNode.key = newKey + } + newNode.updateHeightSize() + return value, newNode.reBalance(version, cowVersion), nil +} + +// Writes the node's hash to the given `io.Writer`. This function recursively calls +// children to update hashes. +func writeHashBytes(node Node, w io.Writer) error { + var ( + n int + buf [binary.MaxVarintLen64]byte + ) + + n = binary.PutVarint(buf[:], int64(node.Height())) + if _, err := w.Write(buf[0:n]); err != nil { + return fmt.Errorf("writing height, %w", err) + } + n = binary.PutVarint(buf[:], node.Size()) + if _, err := w.Write(buf[0:n]); err != nil { + return fmt.Errorf("writing size, %w", err) + } + n = binary.PutVarint(buf[:], int64(node.Version())) + if _, err := w.Write(buf[0:n]); err != nil { + return fmt.Errorf("writing version, %w", err) + } + + // Key is not written for inner nodes, unlike writeBytes. + + if node.IsLeaf() { + if err := EncodeBytes(w, node.Key()); err != nil { + return fmt.Errorf("writing key, %w", err) + } + + // Indirection needed to provide proofs without values. + // (e.g. ProofLeafNode.ValueHash) + valueHash := sha256.Sum256(node.Value()) + + if err := EncodeBytes(w, valueHash[:]); err != nil { + return fmt.Errorf("writing value, %w", err) + } + } else { + if err := EncodeBytes(w, node.Left().Hash()); err != nil { + return fmt.Errorf("writing left hash, %w", err) + } + if err := EncodeBytes(w, node.Right().Hash()); err != nil { + return fmt.Errorf("writing right hash, %w", err) + } + } + + return nil +} + +// HashNode computes the hash of the node. +func HashNode(node Node) []byte { + if node == nil { + return nil + } + h := sha256.New() + if err := writeHashBytes(node, h); err != nil { + panic(err) + } + return h.Sum(nil) +} + +// VerifyHash compare node's cached hash with computed one +func VerifyHash(node Node) bool { + return bytes.Equal(HashNode(node), node.Hash()) +} diff --git a/memiavl/persisted_node.go b/memiavl/persisted_node.go new file mode 100644 index 0000000..59f8c2a --- /dev/null +++ b/memiavl/persisted_node.go @@ -0,0 +1,248 @@ +package memiavl + +import ( + "bytes" + "crypto/sha256" + "sort" +) + +const ( + OffsetHeight = 0 + OffsetPreTrees = OffsetHeight + 1 + OffsetVersion = OffsetHeight + 4 + OffsetSize = OffsetVersion + 4 + OffsetKeyLeaf = OffsetSize + 4 + + OffsetHash = OffsetKeyLeaf + 4 + SizeHash = sha256.Size + SizeNodeWithoutHash = OffsetHash + SizeNode = SizeNodeWithoutHash + SizeHash + + OffsetLeafVersion = 0 + OffsetLeafKeyLen = OffsetLeafVersion + 4 + OffsetLeafKeyOffset = OffsetLeafKeyLen + 4 + OffsetLeafHash = OffsetLeafKeyOffset + 8 + SizeLeafWithoutHash = OffsetLeafHash + SizeLeaf = SizeLeafWithoutHash + SizeHash +) + +// PersistedNode is backed by serialized byte array, usually mmap-ed from disk file. +// Encoding format (all integers are encoded in little endian): +// +// Branch node: +// - height : 1 +// - preTrees : 1 +// - _padding : 2 +// - version : 4 +// - size : 4 +// - key node : 4 // node index of the smallest leaf in right branch +// - hash : 32 +// Leaf node: +// - version : 4 +// - key len : 4 +// - key offset : 8 +// - hash : 32 +type PersistedNode struct { + snapshot *Snapshot + isLeaf bool + index uint32 +} + +var _ Node = PersistedNode{} + +func (node PersistedNode) branchNode() NodeLayout { + return node.snapshot.nodesLayout.Node(node.index) +} + +func (node PersistedNode) leafNode() LeafLayout { + return node.snapshot.leavesLayout.Leaf(node.index) +} + +func (node PersistedNode) Height() uint8 { + if node.isLeaf { + return 0 + } + return node.branchNode().Height() +} + +func (node PersistedNode) IsLeaf() bool { + return node.isLeaf +} + +func (node PersistedNode) Version() uint32 { + if node.isLeaf { + return node.leafNode().Version() + } + return node.branchNode().Version() +} + +func (node PersistedNode) Size() int64 { + if node.isLeaf { + return 1 + } + return int64(node.branchNode().Size()) +} + +func (node PersistedNode) Key() []byte { + if node.isLeaf { + return node.snapshot.LeafKey(node.index) + } + index := node.branchNode().KeyLeaf() + return node.snapshot.LeafKey(index) +} + +// Value returns nil for non-leaf node. +func (node PersistedNode) Value() []byte { + if !node.isLeaf { + return nil + } + _, value := node.snapshot.LeafKeyValue(node.index) + return value +} + +// Left result is not defined for leaf nodes. +func (node PersistedNode) Left() Node { + if node.isLeaf { + panic("can't call Left on leaf node") + } + + data := node.branchNode() + preTrees := uint32(data.PreTrees()) + startLeaf := getStartLeaf(node.index, data.Size(), preTrees) + keyLeaf := data.KeyLeaf() + if startLeaf+1 == keyLeaf { + return PersistedNode{snapshot: node.snapshot, index: startLeaf, isLeaf: true} + } + return PersistedNode{snapshot: node.snapshot, index: getLeftBranch(keyLeaf, preTrees)} +} + +// Right result is not defined for leaf nodes. +func (node PersistedNode) Right() Node { + if node.isLeaf { + panic("can't call Right on leaf node") + } + + data := node.branchNode() + keyLeaf := data.KeyLeaf() + preTrees := uint32(data.PreTrees()) + if keyLeaf == getEndLeaf(node.index, preTrees) { + return PersistedNode{snapshot: node.snapshot, index: keyLeaf, isLeaf: true} + } + return PersistedNode{snapshot: node.snapshot, index: node.index - 1} +} + +func (node PersistedNode) SafeHash() []byte { + return bytes.Clone(node.Hash()) +} + +func (node PersistedNode) Hash() []byte { + if node.isLeaf { + return node.leafNode().Hash() + } + return node.branchNode().Hash() +} + +func (node PersistedNode) Mutate(version, _ uint32) *MemNode { + if node.isLeaf { + key, value := node.snapshot.LeafKeyValue(node.index) + return &MemNode{ + height: 0, + size: 1, + version: version, + key: key, + value: value, + } + } + data := node.branchNode() + return &MemNode{ + height: data.Height(), + size: int64(data.Size()), + version: version, + key: node.Key(), + left: node.Left(), + right: node.Right(), + } +} + +func (node PersistedNode) Get(key []byte) ([]byte, uint32) { + var start, count uint32 + if node.isLeaf { + start = node.index + count = 1 + } else { + data := node.branchNode() + preTrees := uint32(data.PreTrees()) + count = data.Size() + start = getStartLeaf(node.index, count, preTrees) + } + + // binary search in the leaf node array + i := uint32(sort.Search(int(count), func(i int) bool { + leafKey := node.snapshot.LeafKey(start + uint32(i)) + return bytes.Compare(leafKey, key) >= 0 + })) + + leaf := i + start + if leaf >= start+count { + // return the next index if the key is greater than all keys in the node + return nil, i + } + + nodeKey, value := node.snapshot.LeafKeyValue(leaf) + if !bytes.Equal(nodeKey, key) { + return nil, i + } + + return value, i +} + +func (node PersistedNode) GetByIndex(leafIndex uint32) ([]byte, []byte) { + if node.isLeaf { + if leafIndex != 0 { + return nil, nil + } + return node.snapshot.LeafKeyValue(node.index) + } + data := node.branchNode() + preTrees := uint32(data.PreTrees()) + startLeaf := getStartLeaf(node.index, data.Size(), preTrees) + endLeaf := getEndLeaf(node.index, preTrees) + + i := startLeaf + leafIndex + if i > endLeaf { + return nil, nil + } + return node.snapshot.LeafKeyValue(i) +} + +// getStartLeaf returns the index of the first leaf in the node. +// +// > start leaf = pre leaves +// > = pre branches + pre trees +// > = total branches - sub branches + pre trees +// > = (index + 1) - (size - 1) + preTrees +// > = index + 2 - size + preTrees +func getStartLeaf(index, size, preTrees uint32) uint32 { + return index + 2 - size + preTrees +} + +// getEndLeaf returns the index of the last leaf in the node. +// +// > end leaf = start leaf + size - 1 +// > = (index + 2 - size + preTrees) + size - 1 +// > = index + 1 + preTrees +func getEndLeaf(index, preTrees uint32) uint32 { + return index + preTrees + 1 +} + +// getLeftBranch returns the index of the left branch of the node. +// +// > left branch = pre branches + left branches - 1 +// > = (total branches - sub branches) + (left leaves - 1) - 1 +// > = (total branches - sub branches) + (key leaf - start leaf - 1) - 1 +// > = (index+1 - (size-1)) + (key leaf - (index + 2 - size + preTrees) - 1) - 1 +// > = (index - size + 2) + key leaf - index - 2 + size - preTrees - 2 +// > = key leaf - preTrees - 2 +func getLeftBranch(keyLeaf, preTrees uint32) uint32 { + return keyLeaf - preTrees - 2 +} diff --git a/memiavl/proof.go b/memiavl/proof.go new file mode 100644 index 0000000..3adfb27 --- /dev/null +++ b/memiavl/proof.go @@ -0,0 +1,194 @@ +package memiavl + +import ( + "bytes" + "encoding/binary" + "errors" + "fmt" + + "github.com/cosmos/iavl" + ics23 "github.com/cosmos/ics23/go" +) + +/* +GetMembershipProof will produce a CommitmentProof that the given key (and queries value) exists in the iavl tree. +If the key doesn't exist in the tree, this will return an error. +*/ +func (t *Tree) GetMembershipProof(key []byte) (*ics23.CommitmentProof, error) { + exist, err := t.createExistenceProof(key) + if err != nil { + return nil, err + } + proof := &ics23.CommitmentProof{ + Proof: &ics23.CommitmentProof_Exist{ + Exist: exist, + }, + } + return proof, nil +} + +// VerifyMembership returns true iff proof is an ExistenceProof for the given key. +func (t *Tree) VerifyMembership(proof *ics23.CommitmentProof, key []byte) bool { + val := t.Get(key) + root := t.RootHash() + return ics23.VerifyMembership(ics23.IavlSpec, root, proof, key, val) +} + +/* +GetNonMembershipProof will produce a CommitmentProof that the given key doesn't exist in the iavl tree. +If the key exists in the tree, this will return an error. +*/ +func (t *Tree) GetNonMembershipProof(key []byte) (*ics23.CommitmentProof, error) { + // idx is one node right of what we want.... + var err error + idx, val := t.GetWithIndex(key) + if val != nil { + return nil, fmt.Errorf("cannot create NonExistanceProof when Key in State") + } + + nonexist := &ics23.NonExistenceProof{ + Key: key, + } + + if idx >= 1 { + leftkey, _ := t.GetByIndex(idx - 1) + nonexist.Left, err = t.createExistenceProof(leftkey) + if err != nil { + return nil, err + } + } + + // this will be nil if nothing right of the queried key + rightkey, _ := t.GetByIndex(idx) + if rightkey != nil { + nonexist.Right, err = t.createExistenceProof(rightkey) + if err != nil { + return nil, err + } + } + + proof := &ics23.CommitmentProof{ + Proof: &ics23.CommitmentProof_Nonexist{ + Nonexist: nonexist, + }, + } + return proof, nil +} + +// VerifyNonMembership returns true iff proof is a NonExistenceProof for the given key. +func (t *Tree) VerifyNonMembership(proof *ics23.CommitmentProof, key []byte) bool { + root := t.RootHash() + return ics23.VerifyNonMembership(ics23.IavlSpec, root, proof, key) +} + +// createExistenceProof will get the proof from the tree and convert the proof into a valid +// existence proof, if that's what it is. +func (t *Tree) createExistenceProof(key []byte) (*ics23.ExistenceProof, error) { + path, node, err := pathToLeaf(t.root, key) + return &ics23.ExistenceProof{ + Key: node.Key(), + Value: node.Value(), + Leaf: convertLeafOp(int64(node.Version())), + Path: convertInnerOps(path), + }, err +} + +func convertLeafOp(version int64) *ics23.LeafOp { + // this is adapted from iavl/proof.go:proofLeafNode.Hash() + prefix := convertVarIntToBytes(0) + prefix = append(prefix, convertVarIntToBytes(1)...) + prefix = append(prefix, convertVarIntToBytes(version)...) + + return &ics23.LeafOp{ + Hash: ics23.HashOp_SHA256, + PrehashValue: ics23.HashOp_SHA256, + Length: ics23.LengthOp_VAR_PROTO, + Prefix: prefix, + } +} + +// we cannot get the proofInnerNode type, so we need to do the whole path in one function +func convertInnerOps(path iavl.PathToLeaf) []*ics23.InnerOp { + steps := make([]*ics23.InnerOp, 0, len(path)) + + // lengthByte is the length prefix prepended to each of the sha256 sub-hashes + var lengthByte byte = 0x20 + + // we need to go in reverse order, iavl starts from root to leaf, + // we want to go up from the leaf to the root + for i := len(path) - 1; i >= 0; i-- { + // this is adapted from iavl/proof.go:proofInnerNode.Hash() + prefix := convertVarIntToBytes(int64(path[i].Height)) + prefix = append(prefix, convertVarIntToBytes(path[i].Size)...) + prefix = append(prefix, convertVarIntToBytes(path[i].Version)...) + + var suffix []byte + if len(path[i].Left) > 0 { + // length prefixed left side + prefix = append(prefix, lengthByte) + prefix = append(prefix, path[i].Left...) + // prepend the length prefix for child + prefix = append(prefix, lengthByte) + } else { + // prepend the length prefix for child + prefix = append(prefix, lengthByte) + // length-prefixed right side + suffix = []byte{lengthByte} + suffix = append(suffix, path[i].Right...) + } + + op := &ics23.InnerOp{ + Hash: ics23.HashOp_SHA256, + Prefix: prefix, + Suffix: suffix, + } + steps = append(steps, op) + } + return steps +} + +func convertVarIntToBytes(orig int64) []byte { + var buf [binary.MaxVarintLen64]byte + n := binary.PutVarint(buf[:], orig) + return buf[:n] +} + +func pathToLeaf(node Node, key []byte) (iavl.PathToLeaf, Node, error) { + var path iavl.PathToLeaf + + for { + height := node.Height() + if height == 0 { + if bytes.Equal(node.Key(), key) { + return path, node, nil + } + + return path, node, errors.New("key does not exist") + } + + if bytes.Compare(key, node.Key()) < 0 { + // left side + right := node.Right() + path = append(path, iavl.ProofInnerNode{ + Height: int8(height), + Size: node.Size(), + Version: int64(node.Version()), + Left: nil, + Right: right.Hash(), + }) + node = node.Left() + continue + } + + // right side + left := node.Left() + path = append(path, iavl.ProofInnerNode{ + Height: int8(height), + Size: node.Size(), + Version: int64(node.Version()), + Left: left.Hash(), + Right: nil, + }) + node = node.Right() + } +} diff --git a/memiavl/proof_test.go b/memiavl/proof_test.go new file mode 100644 index 0000000..18672c4 --- /dev/null +++ b/memiavl/proof_test.go @@ -0,0 +1,59 @@ +package memiavl + +import ( + "strconv" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestProofs(t *testing.T) { + // do a round test for each version in ChangeSets + testCases := []struct { + existKey []byte + nonExistKey []byte + }{ + {[]byte("hello"), []byte("hello1")}, + {[]byte("hello1"), []byte("hello2")}, + {[]byte("hello2"), []byte("hell")}, + {[]byte("hello00"), []byte("hell")}, + {[]byte("hello00"), []byte("hello")}, + {[]byte("aello00"), []byte("hello")}, + {[]byte("hello1"), []byte("aello00")}, + } + + tmpDir := t.TempDir() + tree := New(0) + + for i, tc := range testCases { + t.Run(strconv.Itoa(i), func(t *testing.T) { + changes := ChangeSets[i] + tree.ApplyChangeSet(changes) + _, _, err := tree.SaveVersion(true) + require.NoError(t, err) + + proof, err := tree.GetMembershipProof(tc.existKey) + require.NoError(t, err) + require.True(t, tree.VerifyMembership(proof, tc.existKey)) + + proof, err = tree.GetNonMembershipProof(tc.nonExistKey) + require.NoError(t, err) + require.True(t, tree.VerifyNonMembership(proof, tc.nonExistKey)) + + // test persisted tree + require.NoError(t, tree.WriteSnapshot(tmpDir)) + snapshot, err := OpenSnapshot(tmpDir) + require.NoError(t, err) + ptree := NewFromSnapshot(snapshot, true, 0) + defer ptree.Close() + + proof, err = ptree.GetMembershipProof(tc.existKey) + require.NoError(t, err) + require.True(t, ptree.VerifyMembership(proof, tc.existKey)) + + proof, err = ptree.GetNonMembershipProof(tc.nonExistKey) + require.NoError(t, err) + require.True(t, ptree.VerifyNonMembership(proof, tc.nonExistKey)) + }) + } +} diff --git a/memiavl/snapshot.go b/memiavl/snapshot.go new file mode 100644 index 0000000..30dc1d4 --- /dev/null +++ b/memiavl/snapshot.go @@ -0,0 +1,585 @@ +package memiavl + +import ( + "bufio" + "context" + "encoding/binary" + "errors" + "fmt" + "io" + "os" + "path/filepath" +) + +const ( + // SnapshotFileMagic is little endian encoded b"IAVL" + SnapshotFileMagic = 1280721225 + + // the initial snapshot format + SnapshotFormat = 0 + + // magic: uint32, format: uint32, version: uint32 + SizeMetadata = 12 + + FileNameNodes = "nodes" + FileNameLeaves = "leaves" + FileNameKVs = "kvs" + FileNameMetadata = "metadata" + + // check for cancel every 1000 leaves + CancelCheckInterval = 1000 +) + +// Snapshot manage the lifecycle of mmap-ed files for the snapshot, +// it must out live the objects that derived from it. +type Snapshot struct { + nodesMap *MmapFile + leavesMap *MmapFile + kvsMap *MmapFile + + nodes []byte + leaves []byte + kvs []byte + + // parsed from metadata file + version uint32 + + // wrapping the raw nodes buffer + nodesLayout Nodes + leavesLayout Leaves + + // nil means empty snapshot + root *PersistedNode +} + +func NewEmptySnapshot(version uint32) *Snapshot { + return &Snapshot{ + version: version, + } +} + +// OpenSnapshot parse the version number and the root node index from metadata file, +// and mmap the other files. +func OpenSnapshot(snapshotDir string) (snapshot *Snapshot, err error) { + // read metadata file + bz, err := os.ReadFile(filepath.Join(snapshotDir, FileNameMetadata)) + if err != nil { + return nil, err + } + if len(bz) != SizeMetadata { + return nil, fmt.Errorf("wrong metadata file size, expcted: %d, found: %d", SizeMetadata, len(bz)) + } + + magic := binary.LittleEndian.Uint32(bz) + if magic != SnapshotFileMagic { + return nil, fmt.Errorf("invalid metadata file magic: %d", magic) + } + format := binary.LittleEndian.Uint32(bz[4:]) + if format != SnapshotFormat { + return nil, fmt.Errorf("unknown snapshot format: %d", format) + } + version := binary.LittleEndian.Uint32(bz[8:]) + + var nodesMap, leavesMap, kvsMap *MmapFile + defer func() { + if err != nil { + errs := []error{err} + if nodesMap != nil { + errs = append(errs, nodesMap.Close()) + } + if leavesMap != nil { + errs = append(errs, leavesMap.Close()) + } + if kvsMap != nil { + errs = append(errs, kvsMap.Close()) + } + err = errors.Join(errs...) + } + }() + + if nodesMap, err = NewMmap(filepath.Join(snapshotDir, FileNameNodes)); err != nil { + return nil, err + } + if leavesMap, err = NewMmap(filepath.Join(snapshotDir, FileNameLeaves)); err != nil { + return nil, err + } + if kvsMap, err = NewMmap(filepath.Join(snapshotDir, FileNameKVs)); err != nil { + return nil, err + } + + nodes := nodesMap.Data() + leaves := leavesMap.Data() + kvs := kvsMap.Data() + + // validate nodes length + if len(nodes)%SizeNode != 0 { + return nil, fmt.Errorf("corrupted snapshot, nodes file size %d is not a multiple of %d", len(nodes), SizeNode) + } + if len(leaves)%SizeLeaf != 0 { + return nil, fmt.Errorf("corrupted snapshot, leaves file size %d is not a multiple of %d", len(leaves), SizeLeaf) + } + + nodesLen := len(nodes) / SizeNode + leavesLen := len(leaves) / SizeLeaf + if (leavesLen > 0 && nodesLen+1 != leavesLen) || (leavesLen == 0 && nodesLen != 0) { + return nil, fmt.Errorf("corrupted snapshot, branch nodes size %d don't match leaves size %d", nodesLen, leavesLen) + } + + nodesData, err := NewNodes(nodes) + if err != nil { + return nil, err + } + + leavesData, err := NewLeaves(leaves) + if err != nil { + return nil, err + } + + snapshot = &Snapshot{ + nodesMap: nodesMap, + leavesMap: leavesMap, + kvsMap: kvsMap, + + // cache the pointers + nodes: nodes, + leaves: leaves, + kvs: kvs, + + version: version, + + nodesLayout: nodesData, + leavesLayout: leavesData, + } + + if nodesLen > 0 { + snapshot.root = &PersistedNode{ + snapshot: snapshot, + isLeaf: false, + index: uint32(nodesLen - 1), + } + } else if leavesLen > 0 { + snapshot.root = &PersistedNode{ + snapshot: snapshot, + isLeaf: true, + index: 0, + } + } + + return snapshot, nil +} + +// Close closes the file and mmap handles, clears the buffers. +func (snapshot *Snapshot) Close() error { + var errs []error + + if snapshot.nodesMap != nil { + errs = append(errs, snapshot.nodesMap.Close()) + } + if snapshot.leavesMap != nil { + errs = append(errs, snapshot.leavesMap.Close()) + } + if snapshot.kvsMap != nil { + errs = append(errs, snapshot.kvsMap.Close()) + } + + // reset to an empty tree + *snapshot = *NewEmptySnapshot(snapshot.version) + return errors.Join(errs...) +} + +// IsEmpty returns if the snapshot is an empty tree. +func (snapshot *Snapshot) IsEmpty() bool { + return snapshot.root == nil +} + +// Node returns the branch node by index +func (snapshot *Snapshot) Node(index uint32) PersistedNode { + return PersistedNode{ + snapshot: snapshot, + index: index, + isLeaf: false, + } +} + +// Leaf returns the leaf node by index +func (snapshot *Snapshot) Leaf(index uint32) PersistedNode { + return PersistedNode{ + snapshot: snapshot, + index: index, + isLeaf: true, + } +} + +// Version returns the version of the snapshot +func (snapshot *Snapshot) Version() uint32 { + return snapshot.version +} + +// RootNode returns the root node +func (snapshot *Snapshot) RootNode() PersistedNode { + if snapshot.IsEmpty() { + panic("RootNode not supported on an empty snapshot") + } + return *snapshot.root +} + +func (snapshot *Snapshot) RootHash() []byte { + if snapshot.IsEmpty() { + return emptyHash + } + return snapshot.RootNode().Hash() +} + +// nodesLen returns the number of nodes in the snapshot +func (snapshot *Snapshot) nodesLen() int { + return len(snapshot.nodes) / SizeNode +} + +// leavesLen returns the number of nodes in the snapshot +func (snapshot *Snapshot) leavesLen() int { + return len(snapshot.leaves) / SizeLeaf +} + +// ScanNodes iterate over the nodes in the snapshot order (depth-first post-order, leaf nodes before branch nodes) +func (snapshot *Snapshot) ScanNodes(callback func(node PersistedNode) error) error { + for i := 0; i < snapshot.leavesLen(); i++ { + if err := callback(snapshot.Leaf(uint32(i))); err != nil { + return err + } + } + for i := 0; i < snapshot.nodesLen(); i++ { + if err := callback(snapshot.Node(uint32(i))); err != nil { + return err + } + } + return nil +} + +// Key returns a zero-copy slice of key by offset +func (snapshot *Snapshot) Key(offset uint64) []byte { + keyLen := binary.LittleEndian.Uint32(snapshot.kvs[offset:]) + offset += 4 + return snapshot.kvs[offset : offset+uint64(keyLen)] +} + +// KeyValue returns a zero-copy slice of key/value pair by offset +func (snapshot *Snapshot) KeyValue(offset uint64) ([]byte, []byte) { + len := uint64(binary.LittleEndian.Uint32(snapshot.kvs[offset:])) + offset += 4 + key := snapshot.kvs[offset : offset+len] + offset += len + len = uint64(binary.LittleEndian.Uint32(snapshot.kvs[offset:])) + offset += 4 + value := snapshot.kvs[offset : offset+len] + return key, value +} + +func (snapshot *Snapshot) LeafKey(index uint32) []byte { + leaf := snapshot.leavesLayout.Leaf(index) + offset := leaf.KeyOffset() + 4 + return snapshot.kvs[offset : offset+uint64(leaf.KeyLength())] +} + +func (snapshot *Snapshot) LeafKeyValue(index uint32) ([]byte, []byte) { + leaf := snapshot.leavesLayout.Leaf(index) + offset := leaf.KeyOffset() + 4 + length := uint64(leaf.KeyLength()) + key := snapshot.kvs[offset : offset+length] + offset += length + length = uint64(binary.LittleEndian.Uint32(snapshot.kvs[offset:])) + offset += 4 + return key, snapshot.kvs[offset : offset+length] +} + +// Export exports the nodes from snapshot file sequentially, more efficient than a post-order traversal. +func (snapshot *Snapshot) Export() *Exporter { + return newExporter(snapshot.export) +} + +func (snapshot *Snapshot) export(callback func(*ExportNode) bool) { + if snapshot.leavesLen() == 0 { + return + } + + if snapshot.leavesLen() == 1 { + leaf := snapshot.Leaf(0) + callback(&ExportNode{ + Height: 0, + Version: int64(leaf.Version()), + Key: leaf.Key(), + Value: leaf.Value(), + }) + return + } + + var pendingTrees int + var i, j uint32 + for ; i < uint32(snapshot.nodesLen()); i++ { + // pending branch node + node := snapshot.nodesLayout.Node(i) + for pendingTrees < int(node.PreTrees())+2 { + // add more leaf nodes + leaf := snapshot.leavesLayout.Leaf(j) + key, value := snapshot.KeyValue(leaf.KeyOffset()) + enode := &ExportNode{ + Height: 0, + Version: int64(leaf.Version()), + Key: key, + Value: value, + } + j++ + pendingTrees++ + + if callback(enode) { + return + } + } + enode := &ExportNode{ + Height: int8(node.Height()), + Version: int64(node.Version()), + Key: snapshot.LeafKey(node.KeyLeaf()), + } + pendingTrees-- + + if callback(enode) { + return + } + } +} + +func (t *Tree) WriteSnapshot(snapshotDir string) error { + return t.WriteSnapshotWithContext(context.Background(), snapshotDir) +} + +// WriteSnapshotWithContext save the IAVL tree to a new snapshot directory. +func (t *Tree) WriteSnapshotWithContext(ctx context.Context, snapshotDir string) error { + return writeSnapshot(ctx, snapshotDir, t.version, func(w *snapshotWriter) (uint32, error) { + if t.root == nil { + return 0, nil + } else { + if err := w.writeRecursive(t.root); err != nil { + return 0, err + } + return w.leafCounter, nil + } + }) +} + +func writeSnapshot( + ctx context.Context, + dir string, version uint32, + doWrite func(*snapshotWriter) (uint32, error), +) (returnErr error) { + if err := os.MkdirAll(dir, os.ModePerm); err != nil { + return err + } + + nodesFile := filepath.Join(dir, FileNameNodes) + leavesFile := filepath.Join(dir, FileNameLeaves) + kvsFile := filepath.Join(dir, FileNameKVs) + + fpNodes, err := createFile(nodesFile) + if err != nil { + return err + } + defer func() { + if err := fpNodes.Close(); returnErr == nil { + returnErr = err + } + }() + + fpLeaves, err := createFile(leavesFile) + if err != nil { + return err + } + defer func() { + if err := fpLeaves.Close(); returnErr == nil { + returnErr = err + } + }() + + fpKVs, err := createFile(kvsFile) + if err != nil { + return err + } + defer func() { + if err := fpKVs.Close(); returnErr == nil { + returnErr = err + } + }() + + nodesWriter := bufio.NewWriter(fpNodes) + leavesWriter := bufio.NewWriter(fpLeaves) + kvsWriter := bufio.NewWriter(fpKVs) + + w := newSnapshotWriter(ctx, nodesWriter, leavesWriter, kvsWriter) + leaves, err := doWrite(w) + if err != nil { + return err + } + + if leaves > 0 { + if err := nodesWriter.Flush(); err != nil { + return err + } + if err := leavesWriter.Flush(); err != nil { + return err + } + if err := kvsWriter.Flush(); err != nil { + return err + } + + if err := fpKVs.Sync(); err != nil { + return err + } + if err := fpLeaves.Sync(); err != nil { + return err + } + if err := fpNodes.Sync(); err != nil { + return err + } + } + + // write metadata + var metadataBuf [SizeMetadata]byte + binary.LittleEndian.PutUint32(metadataBuf[:], SnapshotFileMagic) + binary.LittleEndian.PutUint32(metadataBuf[4:], SnapshotFormat) + binary.LittleEndian.PutUint32(metadataBuf[8:], version) + + metadataFile := filepath.Join(dir, FileNameMetadata) + fpMetadata, err := createFile(metadataFile) + if err != nil { + return err + } + defer func() { + if err := fpMetadata.Close(); returnErr == nil { + returnErr = err + } + }() + + if _, err := fpMetadata.Write(metadataBuf[:]); err != nil { + return err + } + + return fpMetadata.Sync() +} + +type snapshotWriter struct { + // context for cancel the writing process + ctx context.Context + + nodesWriter, leavesWriter, kvWriter io.Writer + + // count how many nodes have been written + branchCounter, leafCounter uint32 + + // record the current writing offset in kvs file + kvsOffset uint64 +} + +func newSnapshotWriter(ctx context.Context, nodesWriter, leavesWriter, kvsWriter io.Writer) *snapshotWriter { + return &snapshotWriter{ + ctx: ctx, + nodesWriter: nodesWriter, + leavesWriter: leavesWriter, + kvWriter: kvsWriter, + } +} + +// writeKeyValue append key-value pair to kvs file and record the offset +func (w *snapshotWriter) writeKeyValue(key, value []byte) error { + var numBuf [4]byte + + binary.LittleEndian.PutUint32(numBuf[:], uint32(len(key))) + if _, err := w.kvWriter.Write(numBuf[:]); err != nil { + return err + } + if _, err := w.kvWriter.Write(key); err != nil { + return err + } + + binary.LittleEndian.PutUint32(numBuf[:], uint32(len(value))) + if _, err := w.kvWriter.Write(numBuf[:]); err != nil { + return err + } + if _, err := w.kvWriter.Write(value); err != nil { + return err + } + + w.kvsOffset += 4 + 4 + uint64(len(key)) + uint64(len(value)) + return nil +} + +func (w *snapshotWriter) writeLeaf(version uint32, key, value, hash []byte) error { + if w.leafCounter%CancelCheckInterval == 0 { + select { + case <-w.ctx.Done(): + return w.ctx.Err() + default: + } + } + + var buf [SizeLeafWithoutHash]byte + binary.LittleEndian.PutUint32(buf[OffsetLeafVersion:], version) + binary.LittleEndian.PutUint32(buf[OffsetLeafKeyLen:], uint32(len(key))) + binary.LittleEndian.PutUint64(buf[OffsetLeafKeyOffset:], w.kvsOffset) + + if err := w.writeKeyValue(key, value); err != nil { + return err + } + + if _, err := w.leavesWriter.Write(buf[:]); err != nil { + return err + } + if _, err := w.leavesWriter.Write(hash); err != nil { + return err + } + + w.leafCounter++ + return nil +} + +func (w *snapshotWriter) writeBranch(version, size uint32, height, preTrees uint8, keyLeaf uint32, hash []byte) error { + var buf [SizeNodeWithoutHash]byte + buf[OffsetHeight] = height + buf[OffsetPreTrees] = preTrees + binary.LittleEndian.PutUint32(buf[OffsetVersion:], version) + binary.LittleEndian.PutUint32(buf[OffsetSize:], size) + binary.LittleEndian.PutUint32(buf[OffsetKeyLeaf:], keyLeaf) + + if _, err := w.nodesWriter.Write(buf[:]); err != nil { + return err + } + if _, err := w.nodesWriter.Write(hash); err != nil { + return err + } + + w.branchCounter++ + return nil +} + +// writeRecursive write the node recursively in depth-first post-order, +// returns `(nodeIndex, err)`. +func (w *snapshotWriter) writeRecursive(node Node) error { + if node.IsLeaf() { + return w.writeLeaf(node.Version(), node.Key(), node.Value(), node.Hash()) + } + + // record the number of pending subtrees before the current one, + // it's always positive and won't exceed the tree height, so we can use an uint8 to store it. + preTrees := uint8(w.leafCounter - w.branchCounter) + + if err := w.writeRecursive(node.Left()); err != nil { + return err + } + keyLeaf := w.leafCounter + if err := w.writeRecursive(node.Right()); err != nil { + return err + } + + return w.writeBranch(node.Version(), uint32(node.Size()), node.Height(), preTrees, keyLeaf, node.Hash()) +} + +func createFile(name string) (*os.File, error) { + return os.OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o600) +} diff --git a/memiavl/snapshot_test.go b/memiavl/snapshot_test.go new file mode 100644 index 0000000..63a11d4 --- /dev/null +++ b/memiavl/snapshot_test.go @@ -0,0 +1,193 @@ +package memiavl + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestSnapshotEncodingRoundTrip(t *testing.T) { + // setup test tree + tree := New(0) + for _, changes := range ChangeSets[:len(ChangeSets)-1] { + tree.ApplyChangeSet(changes) + _, _, err := tree.SaveVersion(true) + require.NoError(t, err) + } + + snapshotDir := t.TempDir() + require.NoError(t, tree.WriteSnapshot(snapshotDir)) + + snapshot, err := OpenSnapshot(snapshotDir) + require.NoError(t, err) + + tree2 := NewFromSnapshot(snapshot, true, 0) + + require.Equal(t, tree.Version(), tree2.Version()) + require.Equal(t, tree.RootHash(), tree2.RootHash()) + + // verify all the node hashes in snapshot + for i := 0; i < snapshot.nodesLen(); i++ { + node := snapshot.Node(uint32(i)) + require.Equal(t, node.Hash(), HashNode(node)) + } + + require.NoError(t, snapshot.Close()) + + // test modify tree loaded from snapshot + snapshot, err = OpenSnapshot(snapshotDir) + require.NoError(t, err) + tree3 := NewFromSnapshot(snapshot, true, 0) + tree3.ApplyChangeSet(ChangeSets[len(ChangeSets)-1]) + hash, v, err := tree3.SaveVersion(true) + require.NoError(t, err) + require.Equal(t, RefHashes[len(ChangeSets)-1], hash) + require.Equal(t, len(ChangeSets), int(v)) + require.NoError(t, snapshot.Close()) +} + +func TestSnapshotExport(t *testing.T) { + expNodes := []*ExportNode{ + {Key: []byte("hello"), Value: []byte("world1"), Version: 2, Height: 0}, + {Key: []byte("hello1"), Value: []byte("world1"), Version: 2, Height: 0}, + {Key: []byte("hello1"), Value: nil, Version: 3, Height: 1}, + {Key: []byte("hello2"), Value: []byte("world1"), Version: 3, Height: 0}, + {Key: []byte("hello3"), Value: []byte("world1"), Version: 3, Height: 0}, + {Key: []byte("hello3"), Value: nil, Version: 3, Height: 1}, + {Key: []byte("hello2"), Value: nil, Version: 3, Height: 2}, + } + + // setup test tree + tree := New(0) + for _, changes := range ChangeSets[:3] { + tree.ApplyChangeSet(changes) + _, _, err := tree.SaveVersion(true) + require.NoError(t, err) + } + + snapshotDir := t.TempDir() + require.NoError(t, tree.WriteSnapshot(snapshotDir)) + + snapshot, err := OpenSnapshot(snapshotDir) + require.NoError(t, err) + + var nodes []*ExportNode + exporter := snapshot.Export() + for { + node, err := exporter.Next() + if err == ErrorExportDone { + break + } + require.NoError(t, err) + nodes = append(nodes, node) + } + + require.Equal(t, expNodes, nodes) +} + +func TestSnapshotImportExport(t *testing.T) { + // setup test tree + tree := New(0) + for _, changes := range ChangeSets { + tree.ApplyChangeSet(changes) + _, _, err := tree.SaveVersion(true) + require.NoError(t, err) + } + + snapshotDir := t.TempDir() + require.NoError(t, tree.WriteSnapshot(snapshotDir)) + snapshot, err := OpenSnapshot(snapshotDir) + require.NoError(t, err) + + ch := make(chan *ExportNode) + + go func() { + defer close(ch) + + exporter := snapshot.Export() + for { + node, err := exporter.Next() + if err == ErrorExportDone { + break + } + require.NoError(t, err) + ch <- node + } + }() + + snapshotDir2 := t.TempDir() + err = doImport(snapshotDir2, tree.Version(), ch) + require.NoError(t, err) + + snapshot2, err := OpenSnapshot(snapshotDir2) + require.NoError(t, err) + require.Equal(t, snapshot.RootNode().Hash(), snapshot2.RootNode().Hash()) + + // verify all the node hashes in snapshot + for i := 0; i < snapshot2.nodesLen(); i++ { + node := snapshot2.Node(uint32(i)) + require.Equal(t, node.Hash(), HashNode(node)) + } +} + +func TestDBSnapshotRestore(t *testing.T) { + db, err := Load(t.TempDir(), Options{ + CreateIfMissing: true, + InitialStores: []string{"test", "test2"}, + AsyncCommitBuffer: -1, + }) + require.NoError(t, err) + + for _, changes := range ChangeSets { + cs := []*NamedChangeSet{ + { + Name: "test", + Changeset: changes, + }, + { + Name: "test2", + Changeset: changes, + }, + } + require.NoError(t, db.ApplyChangeSets(cs)) + _, err := db.Commit() + require.NoError(t, err) + + testSnapshotRoundTrip(t, db) + } + + require.NoError(t, db.RewriteSnapshot()) + require.NoError(t, db.Reload()) + require.Equal(t, len(ChangeSets), int(db.metadata.CommitInfo.Version)) + testSnapshotRoundTrip(t, db) +} + +func testSnapshotRoundTrip(t *testing.T, db *DB) { + exporter, err := NewMultiTreeExporter(db.dir, uint32(db.Version()), true) + require.NoError(t, err) + + restoreDir := t.TempDir() + importer, err := NewMultiTreeImporter(restoreDir, uint64(db.Version())) + require.NoError(t, err) + + for { + item, err := exporter.Next() + if err == ErrorExportDone { + break + } + require.NoError(t, err) + require.NoError(t, importer.Add(item)) + } + + require.NoError(t, importer.Finalize()) + require.NoError(t, importer.Close()) + require.NoError(t, exporter.Close()) + + db2, err := Load(restoreDir, Options{}) + require.NoError(t, err) + require.Equal(t, db.LastCommitInfo(), db2.LastCommitInfo()) + + // the imported db function normally + _, err = db2.Commit() + require.NoError(t, err) +} diff --git a/memiavl/tree.go b/memiavl/tree.go new file mode 100644 index 0000000..0b26290 --- /dev/null +++ b/memiavl/tree.go @@ -0,0 +1,295 @@ +package memiavl + +import ( + "bytes" + "crypto/sha256" + "errors" + "fmt" + "math" + + "github.com/cosmos/iavl/cache" +) + +var emptyHash = sha256.New().Sum(nil) + +func NewCache(cacheSize int) cache.Cache { + if cacheSize == 0 { + return nil + } + return cache.New(cacheSize) +} + +// verify change sets by replay them to rebuild iavl tree and verify the root hashes +type Tree struct { + version uint32 + // root node of empty tree is represented as `nil` + root Node + snapshot *Snapshot + + // simple lru cache provided by iavl library + cache cache.Cache + + initialVersion, cowVersion uint32 + + // when true, the get and iterator methods could return a slice pointing to mmaped blob files. + zeroCopy bool +} + +type cacheNode struct { + key, value []byte +} + +func (n *cacheNode) GetKey() []byte { + return n.key +} + +// NewEmptyTree creates an empty tree at an arbitrary version. +func NewEmptyTree(version uint64, initialVersion uint32, cacheSize int) *Tree { + if version >= math.MaxUint32 { + panic("version overflows uint32") + } + + return &Tree{ + version: uint32(version), + initialVersion: initialVersion, + // no need to copy if the tree is not backed by snapshot + zeroCopy: true, + cache: NewCache(cacheSize), + } +} + +// New creates an empty tree at genesis version +func New(cacheSize int) *Tree { + return NewEmptyTree(0, 0, cacheSize) +} + +// New creates a empty tree with initial-version, +// it happens when a new store created at the middle of the chain. +func NewWithInitialVersion(initialVersion uint32, cacheSize int) *Tree { + return NewEmptyTree(0, initialVersion, cacheSize) +} + +// NewFromSnapshot mmap the blob files and create the root node. +func NewFromSnapshot(snapshot *Snapshot, zeroCopy bool, cacheSize int) *Tree { + tree := &Tree{ + version: snapshot.Version(), + snapshot: snapshot, + zeroCopy: zeroCopy, + cache: NewCache(cacheSize), + } + + if !snapshot.IsEmpty() { + tree.root = snapshot.RootNode() + } + + return tree +} + +func (t *Tree) SetZeroCopy(zeroCopy bool) { + t.zeroCopy = zeroCopy +} + +func (t *Tree) IsEmpty() bool { + return t.root == nil +} + +func (t *Tree) SetInitialVersion(initialVersion int64) error { + if initialVersion >= math.MaxUint32 { + return fmt.Errorf("version overflows uint32: %d", initialVersion) + } + t.initialVersion = uint32(initialVersion) + return nil +} + +// Copy returns a snapshot of the tree which won't be modified by further modifications on the main tree, +// the returned new tree can be accessed concurrently with the main tree. +func (t *Tree) Copy(cacheSize int) *Tree { + if _, ok := t.root.(*MemNode); ok { + // protect the existing `MemNode`s from get modified in-place + t.cowVersion = t.version + } + newTree := *t + // cache is not copied along because it's not thread-safe to access + newTree.cache = NewCache(cacheSize) + return &newTree +} + +// ApplyChangeSet apply the change set of a whole version, and update hashes. +func (t *Tree) ApplyChangeSet(changeSet ChangeSet) { + for _, pair := range changeSet.Pairs { + if pair.Delete { + t.remove(pair.Key) + } else { + t.set(pair.Key, pair.Value) + } + } +} + +func (t *Tree) set(key, value []byte) { + if value == nil { + // the value could be nil when replaying changes from write-ahead-log because of protobuf decoding + value = []byte{} + } + t.root, _ = setRecursive(t.root, key, value, t.version+1, t.cowVersion) + if t.cache != nil { + t.cache.Add(&cacheNode{key, value}) + } +} + +func (t *Tree) remove(key []byte) { + _, t.root, _ = removeRecursive(t.root, key, t.version+1, t.cowVersion) + if t.cache != nil { + t.cache.Remove(key) + } +} + +// SaveVersion increases the version number and optionally updates the hashes +func (t *Tree) SaveVersion(updateHash bool) ([]byte, int64, error) { + if t.version >= uint32(math.MaxUint32) { + return nil, 0, errors.New("version overflows uint32") + } + + var hash []byte + if updateHash { + hash = t.RootHash() + } + + t.version = nextVersionU32(t.version, t.initialVersion) + return hash, int64(t.version), nil +} + +// Version returns the current tree version +func (t *Tree) Version() int64 { + return int64(t.version) +} + +// RootHash updates the hashes and return the current root hash, +// it clones the persisted node's bytes, so the returned bytes is safe to retain. +func (t *Tree) RootHash() []byte { + if t.root == nil { + return emptyHash + } + return t.root.SafeHash() +} + +func (t *Tree) GetWithIndex(key []byte) (int64, []byte) { + if t.root == nil { + return 0, nil + } + + value, index := t.root.Get(key) + if !t.zeroCopy { + value = bytes.Clone(value) + } + return int64(index), value +} + +func (t *Tree) GetByIndex(index int64) ([]byte, []byte) { + if index > math.MaxUint32 { + return nil, nil + } + if t.root == nil { + return nil, nil + } + + key, value := t.root.GetByIndex(uint32(index)) + if !t.zeroCopy { + key = bytes.Clone(key) + value = bytes.Clone(value) + } + return key, value +} + +func (t *Tree) Get(key []byte) []byte { + if t.cache != nil { + if node := t.cache.Get(key); node != nil { + return node.(*cacheNode).value + } + } + + _, value := t.GetWithIndex(key) + if value == nil { + return nil + } + + if t.cache != nil { + t.cache.Add(&cacheNode{key, value}) + } + return value +} + +func (t *Tree) Has(key []byte) bool { + return t.Get(key) != nil +} + +func (t *Tree) Iterator(start, end []byte, ascending bool) *Iterator { + return NewIterator(start, end, ascending, t.root, t.zeroCopy) +} + +// ScanPostOrder scans the tree in post-order, and call the callback function on each node. +// If the callback function returns false, the scan will be stopped. +func (t *Tree) ScanPostOrder(callback func(node Node) bool) { + if t.root == nil { + return + } + + stack := []*stackEntry{{node: t.root}} + + for len(stack) > 0 { + entry := stack[len(stack)-1] + + if entry.node.IsLeaf() || entry.expanded { + callback(entry.node) + stack = stack[:len(stack)-1] + continue + } + + entry.expanded = true + stack = append(stack, &stackEntry{node: entry.node.Right()}) + stack = append(stack, &stackEntry{node: entry.node.Left()}) + } +} + +type stackEntry struct { + node Node + expanded bool +} + +// Export returns a snapshot of the tree which won't be corrupted by further modifications on the main tree. +func (t *Tree) Export() *Exporter { + if t.snapshot != nil && t.version == t.snapshot.Version() { + // snapshot export algorithm is more efficient + return t.snapshot.Export() + } + + // do normal post-order traversal export + return newExporter(func(callback func(node *ExportNode) bool) { + t.ScanPostOrder(func(node Node) bool { + return callback(&ExportNode{ + Key: node.Key(), + Value: node.Value(), + Version: int64(node.Version()), + Height: int8(node.Height()), + }) + }) + }) +} + +func (t *Tree) Close() error { + var err error + if t.snapshot != nil { + err = t.snapshot.Close() + t.snapshot = nil + } + t.root = nil + return err +} + +// nextVersionU32 is compatible with existing golang iavl implementation. +// see: https://github.com/cosmos/iavl/pull/660 +func nextVersionU32(v uint32, initialVersion uint32) uint32 { + if v == 0 && initialVersion > 1 { + return initialVersion + } + return v + 1 +} diff --git a/memiavl/tree_test.go b/memiavl/tree_test.go new file mode 100644 index 0000000..6a01341 --- /dev/null +++ b/memiavl/tree_test.go @@ -0,0 +1,271 @@ +package memiavl + +import ( + "fmt" + "strconv" + "testing" + + "cosmossdk.io/log" + "cosmossdk.io/store/wrapper" + db "github.com/cosmos/cosmos-db" + "github.com/cosmos/iavl" + "github.com/stretchr/testify/require" +) + +var ( + ChangeSets []ChangeSet + RefHashes [][]byte + ExpectItems [][]pair +) + +func mockKVPairs(kvPairs ...string) []*KVPair { + result := make([]*KVPair, len(kvPairs)/2) + for i := 0; i < len(kvPairs); i += 2 { + result[i/2] = &KVPair{ + Key: []byte(kvPairs[i]), + Value: []byte(kvPairs[i+1]), + } + } + return result +} + +func init() { + ChangeSets = []ChangeSet{ + {Pairs: mockKVPairs("hello", "world")}, + {Pairs: mockKVPairs("hello", "world1", "hello1", "world1")}, + {Pairs: mockKVPairs("hello2", "world1", "hello3", "world1")}, + } + + changes := ChangeSet{} + for i := 0; i < 1; i++ { + changes.Pairs = append(changes.Pairs, &KVPair{Key: []byte(fmt.Sprintf("hello%02d", i)), Value: []byte("world1")}) + } + + ChangeSets = append(ChangeSets, changes) + ChangeSets = append(ChangeSets, ChangeSet{Pairs: []*KVPair{{Key: []byte("hello"), Delete: true}, {Key: []byte("hello19"), Delete: true}}}) + + changes = ChangeSet{} + for i := 0; i < 21; i++ { + changes.Pairs = append(changes.Pairs, &KVPair{Key: []byte(fmt.Sprintf("aello%02d", i)), Value: []byte("world1")}) + } + ChangeSets = append(ChangeSets, changes) + + changes = ChangeSet{} + for i := 0; i < 21; i++ { + changes.Pairs = append(changes.Pairs, &KVPair{Key: []byte(fmt.Sprintf("aello%02d", i)), Delete: true}) + } + for i := 0; i < 19; i++ { + changes.Pairs = append(changes.Pairs, &KVPair{Key: []byte(fmt.Sprintf("hello%02d", i)), Delete: true}) + } + ChangeSets = append(ChangeSets, changes) + + // generate ref hashes with ref impl + d := wrapper.NewDBWrapper(db.NewMemDB()) + refTree := iavl.NewMutableTree(d, 0, true, log.NewNopLogger()) + for _, changes := range ChangeSets { + if err := applyChangeSetRef(refTree, changes); err != nil { + panic(err) + } + refHash, _, err := refTree.SaveVersion() + if err != nil { + panic(err) + } + RefHashes = append(RefHashes, refHash) + } + + ExpectItems = [][]pair{ + {}, + {{[]byte("hello"), []byte("world")}}, + { + {[]byte("hello"), []byte("world1")}, + {[]byte("hello1"), []byte("world1")}, + }, + { + {[]byte("hello"), []byte("world1")}, + {[]byte("hello1"), []byte("world1")}, + {[]byte("hello2"), []byte("world1")}, + {[]byte("hello3"), []byte("world1")}, + }, + { + {[]byte("hello"), []byte("world1")}, + {[]byte("hello00"), []byte("world1")}, + {[]byte("hello1"), []byte("world1")}, + {[]byte("hello2"), []byte("world1")}, + {[]byte("hello3"), []byte("world1")}, + }, + { + {[]byte("hello00"), []byte("world1")}, + {[]byte("hello1"), []byte("world1")}, + {[]byte("hello2"), []byte("world1")}, + {[]byte("hello3"), []byte("world1")}, + }, + { + {[]byte("aello00"), []byte("world1")}, + {[]byte("aello01"), []byte("world1")}, + {[]byte("aello02"), []byte("world1")}, + {[]byte("aello03"), []byte("world1")}, + {[]byte("aello04"), []byte("world1")}, + {[]byte("aello05"), []byte("world1")}, + {[]byte("aello06"), []byte("world1")}, + {[]byte("aello07"), []byte("world1")}, + {[]byte("aello08"), []byte("world1")}, + {[]byte("aello09"), []byte("world1")}, + {[]byte("aello10"), []byte("world1")}, + {[]byte("aello11"), []byte("world1")}, + {[]byte("aello12"), []byte("world1")}, + {[]byte("aello13"), []byte("world1")}, + {[]byte("aello14"), []byte("world1")}, + {[]byte("aello15"), []byte("world1")}, + {[]byte("aello16"), []byte("world1")}, + {[]byte("aello17"), []byte("world1")}, + {[]byte("aello18"), []byte("world1")}, + {[]byte("aello19"), []byte("world1")}, + {[]byte("aello20"), []byte("world1")}, + {[]byte("hello00"), []byte("world1")}, + {[]byte("hello1"), []byte("world1")}, + {[]byte("hello2"), []byte("world1")}, + {[]byte("hello3"), []byte("world1")}, + }, + { + {[]byte("hello1"), []byte("world1")}, + {[]byte("hello2"), []byte("world1")}, + {[]byte("hello3"), []byte("world1")}, + }, + } +} + +func applyChangeSetRef(t *iavl.MutableTree, changes ChangeSet) error { + for _, change := range changes.Pairs { + if change.Delete { + if _, _, err := t.Remove(change.Key); err != nil { + return err + } + } else { + if _, err := t.Set(change.Key, change.Value); err != nil { + return err + } + } + } + return nil +} + +func TestRootHashes(t *testing.T) { + tree := New(0) + + for i, changes := range ChangeSets { + tree.ApplyChangeSet(changes) + hash, v, err := tree.SaveVersion(true) + require.NoError(t, err) + require.Equal(t, i+1, int(v)) + require.Equal(t, RefHashes[i], hash) + } +} + +func TestNewKey(t *testing.T) { + tree := New(0) + + for i := 0; i < 4; i++ { + tree.set([]byte(fmt.Sprintf("key-%d", i)), []byte{1}) + } + _, _, err := tree.SaveVersion(true) + require.NoError(t, err) + + // the smallest key in the right half of the tree + require.Equal(t, tree.root.Key(), []byte("key-2")) + + // remove this key + tree.remove([]byte("key-2")) + + // check root node's key is changed + require.Equal(t, []byte("key-3"), tree.root.Key()) +} + +func TestEmptyTree(t *testing.T) { + tree := New(0) + require.Equal(t, emptyHash, tree.RootHash()) +} + +func TestTreeCopy(t *testing.T) { + tree := New(0) + + tree.ApplyChangeSet(ChangeSet{Pairs: []*KVPair{ + {Key: []byte("hello"), Value: []byte("world")}, + }}) + _, _, err := tree.SaveVersion(true) + require.NoError(t, err) + + snapshot := tree.Copy(0) + + tree.ApplyChangeSet(ChangeSet{Pairs: []*KVPair{ + {Key: []byte("hello"), Value: []byte("world1")}, + }}) + _, _, err = tree.SaveVersion(true) + require.NoError(t, err) + + require.Equal(t, []byte("world1"), tree.Get([]byte("hello"))) + require.Equal(t, []byte("world"), snapshot.Get([]byte("hello"))) + + // check that normal copy don't work + fakeSnapshot := *tree + + tree.ApplyChangeSet(ChangeSet{Pairs: []*KVPair{ + {Key: []byte("hello"), Value: []byte("world2")}, + }}) + _, _, err = tree.SaveVersion(true) + require.NoError(t, err) + + // get modified in-place + require.Equal(t, []byte("world2"), tree.Get([]byte("hello"))) + require.Equal(t, []byte("world2"), fakeSnapshot.Get([]byte("hello"))) +} + +func TestChangeSetMarshal(t *testing.T) { + for _, changes := range ChangeSets { + bz, err := changes.Marshal() + require.NoError(t, err) + + var cs ChangeSet + require.NoError(t, cs.Unmarshal(bz)) + require.Equal(t, changes, cs) + } +} + +func TestGetByIndex(t *testing.T) { + changes := ChangeSet{} + for i := 0; i < 20; i++ { + changes.Pairs = append(changes.Pairs, &KVPair{Key: []byte(fmt.Sprintf("hello%02d", i)), Value: []byte(strconv.Itoa(i))}) + } + + tree := New(0) + tree.ApplyChangeSet(changes) + _, _, err := tree.SaveVersion(true) + require.NoError(t, err) + + for i, pair := range changes.Pairs { + idx, v := tree.GetWithIndex(pair.Key) + require.Equal(t, pair.Value, v) + require.Equal(t, int64(i), idx) + + k, v := tree.GetByIndex(idx) + require.Equal(t, pair.Key, k) + require.Equal(t, pair.Value, v) + } + + // test persisted tree + dir := t.TempDir() + require.NoError(t, tree.WriteSnapshot(dir)) + snapshot, err := OpenSnapshot(dir) + require.NoError(t, err) + ptree := NewFromSnapshot(snapshot, true, 0) + defer ptree.Close() + + for i, pair := range changes.Pairs { + idx, v := ptree.GetWithIndex(pair.Key) + require.Equal(t, pair.Value, v) + require.Equal(t, int64(i), idx) + + k, v := ptree.GetByIndex(idx) + require.Equal(t, pair.Key, k) + require.Equal(t, pair.Value, v) + } +} diff --git a/memiavl/types.go b/memiavl/types.go new file mode 100644 index 0000000..16c82f1 --- /dev/null +++ b/memiavl/types.go @@ -0,0 +1,34 @@ +package memiavl + +import fmt "fmt" + +// Logger is what any CometBFT library should take. +type Logger interface { + Debug(msg string, keyvals ...interface{}) + Info(msg string, keyvals ...interface{}) + Error(msg string, keyvals ...interface{}) +} + +type nopLogger struct{} + +// Interface assertions +var _ Logger = (*nopLogger)(nil) + +// NewNopLogger returns a logger that doesn't do anything. +func NewNopLogger() Logger { return &nopLogger{} } + +func (nopLogger) Info(string, ...interface{}) {} +func (nopLogger) Debug(string, ...interface{}) {} +func (nopLogger) Error(string, ...interface{}) {} + +// ExportNode contains exported node data. +type ExportNode struct { + Key []byte + Value []byte + Version int64 + Height int8 +} + +func (cid CommitID) String() string { + return fmt.Sprintf("CommitID{%v:%X}", cid.Hash, cid.Version) +} diff --git a/memiavl/wal.go b/memiavl/wal.go new file mode 100644 index 0000000..cb5b0d8 --- /dev/null +++ b/memiavl/wal.go @@ -0,0 +1,100 @@ +package memiavl + +import ( + "bytes" + "encoding/binary" + "fmt" + "os" + "path/filepath" + "unsafe" + + "github.com/tidwall/gjson" + "github.com/tidwall/wal" +) + +// OpenWAL opens the write ahead log, try to truncate the corrupted tail if there's any +// TODO fix in upstream: https://github.com/tidwall/wal/pull/22 +func OpenWAL(dir string, opts *wal.Options) (*wal.Log, error) { + log, err := wal.Open(dir, opts) + if err == wal.ErrCorrupt { + // try to truncate corrupted tail + var fis []os.DirEntry + fis, err = os.ReadDir(dir) + if err != nil { + return nil, fmt.Errorf("read wal dir fail: %w", err) + } + var lastSeg string + for _, fi := range fis { + if fi.IsDir() || len(fi.Name()) < 20 { + continue + } + lastSeg = fi.Name() + } + + if len(lastSeg) == 0 { + return nil, err + } + if err = truncateCorruptedTail(filepath.Join(dir, lastSeg), opts.LogFormat); err != nil { + return nil, fmt.Errorf("truncate corrupted tail fail: %w", err) + } + + // try again + return wal.Open(dir, opts) + } + + return log, err +} + +func truncateCorruptedTail(path string, format wal.LogFormat) error { + data, err := os.ReadFile(path) + if err != nil { + return err + } + var pos int + for len(data) > 0 { + var n int + if format == wal.JSON { + n, err = loadNextJSONEntry(data) + } else { + n, err = loadNextBinaryEntry(data) + } + if err == wal.ErrCorrupt { + break + } + if err != nil { + return err + } + data = data[n:] + pos += n + } + if pos != len(data) { + return os.Truncate(path, int64(pos)) + } + return nil +} + +func loadNextJSONEntry(data []byte) (n int, err error) { + // {"index":number,"data":string} + idx := bytes.IndexByte(data, '\n') + if idx == -1 { + return 0, wal.ErrCorrupt + } + line := data[:idx] + dres := gjson.Get(*(*string)(unsafe.Pointer(&line)), "data") + if dres.Type != gjson.String { + return 0, wal.ErrCorrupt + } + return idx + 1, nil +} + +func loadNextBinaryEntry(data []byte) (n int, err error) { + // data_size + data + size, n := binary.Uvarint(data) + if n <= 0 { + return 0, wal.ErrCorrupt + } + if uint64(len(data)-n) < size { + return 0, wal.ErrCorrupt + } + return n + int(size), nil +} diff --git a/memiavl/wal.pb.go b/memiavl/wal.pb.go new file mode 100644 index 0000000..68f6f52 --- /dev/null +++ b/memiavl/wal.pb.go @@ -0,0 +1,1109 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: memiavl/wal.proto + +package memiavl + +import ( + fmt "fmt" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// NamedChangeSet combine a tree name with the changeset +type NamedChangeSet struct { + Changeset ChangeSet `protobuf:"bytes,1,opt,name=changeset,proto3" json:"changeset"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` +} + +func (m *NamedChangeSet) Reset() { *m = NamedChangeSet{} } +func (m *NamedChangeSet) String() string { return proto.CompactTextString(m) } +func (*NamedChangeSet) ProtoMessage() {} +func (*NamedChangeSet) Descriptor() ([]byte, []int) { + return fileDescriptor_3a36f610a0003eaf, []int{0} +} +func (m *NamedChangeSet) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *NamedChangeSet) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_NamedChangeSet.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *NamedChangeSet) XXX_Merge(src proto.Message) { + xxx_messageInfo_NamedChangeSet.Merge(m, src) +} +func (m *NamedChangeSet) XXX_Size() int { + return m.Size() +} +func (m *NamedChangeSet) XXX_DiscardUnknown() { + xxx_messageInfo_NamedChangeSet.DiscardUnknown(m) +} + +var xxx_messageInfo_NamedChangeSet proto.InternalMessageInfo + +func (m *NamedChangeSet) GetChangeset() ChangeSet { + if m != nil { + return m.Changeset + } + return ChangeSet{} +} + +func (m *NamedChangeSet) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +// TreeNameUpgrade defines upgrade of tree names: +// - New tree: { name: "tree" } +// - Delete tree: { name: "tree", delete: true } +// - Rename tree: { name: "new-tree", rename_from: "old-tree" } +type TreeNameUpgrade struct { + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + RenameFrom string `protobuf:"bytes,2,opt,name=rename_from,json=renameFrom,proto3" json:"rename_from,omitempty"` + Delete bool `protobuf:"varint,3,opt,name=delete,proto3" json:"delete,omitempty"` +} + +func (m *TreeNameUpgrade) Reset() { *m = TreeNameUpgrade{} } +func (m *TreeNameUpgrade) String() string { return proto.CompactTextString(m) } +func (*TreeNameUpgrade) ProtoMessage() {} +func (*TreeNameUpgrade) Descriptor() ([]byte, []int) { + return fileDescriptor_3a36f610a0003eaf, []int{1} +} +func (m *TreeNameUpgrade) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TreeNameUpgrade) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TreeNameUpgrade.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *TreeNameUpgrade) XXX_Merge(src proto.Message) { + xxx_messageInfo_TreeNameUpgrade.Merge(m, src) +} +func (m *TreeNameUpgrade) XXX_Size() int { + return m.Size() +} +func (m *TreeNameUpgrade) XXX_DiscardUnknown() { + xxx_messageInfo_TreeNameUpgrade.DiscardUnknown(m) +} + +var xxx_messageInfo_TreeNameUpgrade proto.InternalMessageInfo + +func (m *TreeNameUpgrade) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *TreeNameUpgrade) GetRenameFrom() string { + if m != nil { + return m.RenameFrom + } + return "" +} + +func (m *TreeNameUpgrade) GetDelete() bool { + if m != nil { + return m.Delete + } + return false +} + +// WALEntry is a single Write-Ahead-Log entry +type WALEntry struct { + Changesets []*NamedChangeSet `protobuf:"bytes,1,rep,name=changesets,proto3" json:"changesets,omitempty"` + Upgrades []*TreeNameUpgrade `protobuf:"bytes,2,rep,name=upgrades,proto3" json:"upgrades,omitempty"` +} + +func (m *WALEntry) Reset() { *m = WALEntry{} } +func (m *WALEntry) String() string { return proto.CompactTextString(m) } +func (*WALEntry) ProtoMessage() {} +func (*WALEntry) Descriptor() ([]byte, []int) { + return fileDescriptor_3a36f610a0003eaf, []int{2} +} +func (m *WALEntry) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *WALEntry) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_WALEntry.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *WALEntry) XXX_Merge(src proto.Message) { + xxx_messageInfo_WALEntry.Merge(m, src) +} +func (m *WALEntry) XXX_Size() int { + return m.Size() +} +func (m *WALEntry) XXX_DiscardUnknown() { + xxx_messageInfo_WALEntry.DiscardUnknown(m) +} + +var xxx_messageInfo_WALEntry proto.InternalMessageInfo + +func (m *WALEntry) GetChangesets() []*NamedChangeSet { + if m != nil { + return m.Changesets + } + return nil +} + +func (m *WALEntry) GetUpgrades() []*TreeNameUpgrade { + if m != nil { + return m.Upgrades + } + return nil +} + +// MultiTreeMetadata stores the metadata for MultiTree +type MultiTreeMetadata struct { + CommitInfo *CommitInfo `protobuf:"bytes,1,opt,name=commit_info,json=commitInfo,proto3" json:"commit_info,omitempty"` + InitialVersion int64 `protobuf:"varint,2,opt,name=initial_version,json=initialVersion,proto3" json:"initial_version,omitempty"` +} + +func (m *MultiTreeMetadata) Reset() { *m = MultiTreeMetadata{} } +func (m *MultiTreeMetadata) String() string { return proto.CompactTextString(m) } +func (*MultiTreeMetadata) ProtoMessage() {} +func (*MultiTreeMetadata) Descriptor() ([]byte, []int) { + return fileDescriptor_3a36f610a0003eaf, []int{3} +} +func (m *MultiTreeMetadata) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MultiTreeMetadata) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MultiTreeMetadata.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MultiTreeMetadata) XXX_Merge(src proto.Message) { + xxx_messageInfo_MultiTreeMetadata.Merge(m, src) +} +func (m *MultiTreeMetadata) XXX_Size() int { + return m.Size() +} +func (m *MultiTreeMetadata) XXX_DiscardUnknown() { + xxx_messageInfo_MultiTreeMetadata.DiscardUnknown(m) +} + +var xxx_messageInfo_MultiTreeMetadata proto.InternalMessageInfo + +func (m *MultiTreeMetadata) GetCommitInfo() *CommitInfo { + if m != nil { + return m.CommitInfo + } + return nil +} + +func (m *MultiTreeMetadata) GetInitialVersion() int64 { + if m != nil { + return m.InitialVersion + } + return 0 +} + +func init() { + proto.RegisterType((*NamedChangeSet)(nil), "memiavl.NamedChangeSet") + proto.RegisterType((*TreeNameUpgrade)(nil), "memiavl.TreeNameUpgrade") + proto.RegisterType((*WALEntry)(nil), "memiavl.WALEntry") + proto.RegisterType((*MultiTreeMetadata)(nil), "memiavl.MultiTreeMetadata") +} + +func init() { proto.RegisterFile("memiavl/wal.proto", fileDescriptor_3a36f610a0003eaf) } + +var fileDescriptor_3a36f610a0003eaf = []byte{ + // 391 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x5c, 0x51, 0x4d, 0x8b, 0xd3, 0x40, + 0x18, 0xce, 0x6c, 0x97, 0xb5, 0xfb, 0x06, 0x76, 0xd9, 0x51, 0xdc, 0xd8, 0x43, 0x1a, 0x72, 0x31, + 0x08, 0x4d, 0xa0, 0x16, 0x3d, 0x5b, 0x3f, 0x40, 0xb0, 0x1e, 0xe2, 0x17, 0x88, 0x58, 0xa6, 0xc9, + 0x34, 0x1d, 0xc8, 0xcc, 0x94, 0xc9, 0xb4, 0xd2, 0x7f, 0xe1, 0xcf, 0xea, 0xb1, 0x47, 0x4f, 0x22, + 0xed, 0x1f, 0x91, 0x24, 0xe3, 0xb4, 0xee, 0xed, 0xc9, 0xf3, 0xf1, 0xe6, 0xc9, 0x13, 0xb8, 0xe1, + 0x94, 0x33, 0xb2, 0x2e, 0x93, 0x1f, 0xa4, 0x8c, 0x97, 0x4a, 0x6a, 0x89, 0xef, 0x19, 0xaa, 0xf7, + 0xa0, 0x90, 0x85, 0x6c, 0xb8, 0xa4, 0x46, 0xad, 0xdc, 0xbb, 0xfd, 0x97, 0xc8, 0x16, 0x44, 0x14, + 0xb4, 0xa2, 0xda, 0x08, 0x8f, 0xac, 0x20, 0x39, 0x67, 0x7a, 0xca, 0xc4, 0xdc, 0x64, 0xc2, 0x6f, + 0x70, 0xf5, 0x9e, 0x70, 0x9a, 0xbf, 0x6c, 0x22, 0x1f, 0xa8, 0xc6, 0xcf, 0xe0, 0xd2, 0xe6, 0x3d, + 0x14, 0xa0, 0xc8, 0x1d, 0xe2, 0xd8, 0x1c, 0x88, 0xad, 0x6d, 0x7c, 0xbe, 0xfd, 0xdd, 0x77, 0xd2, + 0xa3, 0x15, 0x63, 0x38, 0x17, 0x84, 0x53, 0xef, 0x2c, 0x40, 0xd1, 0x65, 0xda, 0xe0, 0xf0, 0x3b, + 0x5c, 0x7f, 0x54, 0x94, 0xd6, 0x6f, 0xf8, 0xb4, 0x2c, 0x14, 0xc9, 0xa9, 0xb5, 0xa1, 0xa3, 0x0d, + 0xf7, 0xc1, 0x55, 0xb4, 0x46, 0xd3, 0xb9, 0x92, 0xdc, 0x5c, 0x80, 0x96, 0x7a, 0xa3, 0x24, 0xc7, + 0x0f, 0xe1, 0x22, 0xa7, 0x25, 0xd5, 0xd4, 0xeb, 0x04, 0x28, 0xea, 0xa6, 0xe6, 0x29, 0xdc, 0x40, + 0xf7, 0xcb, 0x8b, 0x77, 0xaf, 0x85, 0x56, 0x1b, 0xfc, 0x1c, 0xc0, 0x96, 0xa9, 0x3c, 0x14, 0x74, + 0x22, 0x77, 0x78, 0x6b, 0x8b, 0xff, 0xff, 0x91, 0xe9, 0x89, 0x15, 0x8f, 0xa0, 0xbb, 0x6a, 0xcb, + 0x55, 0xde, 0x59, 0x13, 0xf3, 0x6c, 0xec, 0x4e, 0xfb, 0xd4, 0x3a, 0x43, 0x05, 0x37, 0x93, 0x55, + 0xa9, 0x59, 0xed, 0x98, 0x50, 0x4d, 0x72, 0xa2, 0x09, 0x1e, 0x81, 0x7b, 0x32, 0xb1, 0x59, 0xef, + 0xfe, 0x71, 0xbd, 0x46, 0x7b, 0x2b, 0xe6, 0x32, 0x85, 0xcc, 0x62, 0xfc, 0x18, 0xae, 0x99, 0x60, + 0x9a, 0x91, 0x72, 0xba, 0xa6, 0xaa, 0x62, 0x52, 0x34, 0x13, 0x74, 0xd2, 0x2b, 0x43, 0x7f, 0x6e, + 0xd9, 0xf1, 0xab, 0xed, 0xde, 0x47, 0xbb, 0xbd, 0x8f, 0xfe, 0xec, 0x7d, 0xf4, 0xf3, 0xe0, 0x3b, + 0xbb, 0x83, 0xef, 0xfc, 0x3a, 0xf8, 0xce, 0xd7, 0x27, 0x05, 0xd3, 0x8b, 0xd5, 0x2c, 0xce, 0x24, + 0x4f, 0x32, 0xb5, 0x59, 0x6a, 0x39, 0x90, 0xaa, 0x18, 0x64, 0x0b, 0xc2, 0x44, 0x92, 0x29, 0x29, + 0x64, 0x95, 0x98, 0x16, 0xb3, 0x8b, 0xe6, 0xcf, 0x3f, 0xfd, 0x1b, 0x00, 0x00, 0xff, 0xff, 0x0d, + 0x15, 0x5d, 0xa9, 0x61, 0x02, 0x00, 0x00, +} + +func (m *NamedChangeSet) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *NamedChangeSet) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *NamedChangeSet) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintWal(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0x12 + } + { + size, err := m.Changeset.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintWal(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *TreeNameUpgrade) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *TreeNameUpgrade) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TreeNameUpgrade) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Delete { + i-- + if m.Delete { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x18 + } + if len(m.RenameFrom) > 0 { + i -= len(m.RenameFrom) + copy(dAtA[i:], m.RenameFrom) + i = encodeVarintWal(dAtA, i, uint64(len(m.RenameFrom))) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintWal(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *WALEntry) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *WALEntry) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *WALEntry) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Upgrades) > 0 { + for iNdEx := len(m.Upgrades) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Upgrades[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintWal(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.Changesets) > 0 { + for iNdEx := len(m.Changesets) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Changesets[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintWal(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *MultiTreeMetadata) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MultiTreeMetadata) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MultiTreeMetadata) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.InitialVersion != 0 { + i = encodeVarintWal(dAtA, i, uint64(m.InitialVersion)) + i-- + dAtA[i] = 0x10 + } + if m.CommitInfo != nil { + { + size, err := m.CommitInfo.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintWal(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintWal(dAtA []byte, offset int, v uint64) int { + offset -= sovWal(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *NamedChangeSet) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Changeset.Size() + n += 1 + l + sovWal(uint64(l)) + l = len(m.Name) + if l > 0 { + n += 1 + l + sovWal(uint64(l)) + } + return n +} + +func (m *TreeNameUpgrade) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + sovWal(uint64(l)) + } + l = len(m.RenameFrom) + if l > 0 { + n += 1 + l + sovWal(uint64(l)) + } + if m.Delete { + n += 2 + } + return n +} + +func (m *WALEntry) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Changesets) > 0 { + for _, e := range m.Changesets { + l = e.Size() + n += 1 + l + sovWal(uint64(l)) + } + } + if len(m.Upgrades) > 0 { + for _, e := range m.Upgrades { + l = e.Size() + n += 1 + l + sovWal(uint64(l)) + } + } + return n +} + +func (m *MultiTreeMetadata) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.CommitInfo != nil { + l = m.CommitInfo.Size() + n += 1 + l + sovWal(uint64(l)) + } + if m.InitialVersion != 0 { + n += 1 + sovWal(uint64(m.InitialVersion)) + } + return n +} + +func sovWal(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozWal(x uint64) (n int) { + return sovWal(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *NamedChangeSet) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowWal + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: NamedChangeSet: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: NamedChangeSet: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Changeset", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowWal + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthWal + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthWal + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Changeset.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowWal + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthWal + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthWal + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipWal(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthWal + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TreeNameUpgrade) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowWal + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: TreeNameUpgrade: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TreeNameUpgrade: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowWal + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthWal + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthWal + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RenameFrom", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowWal + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthWal + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthWal + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.RenameFrom = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Delete", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowWal + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Delete = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := skipWal(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthWal + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *WALEntry) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowWal + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: WALEntry: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: WALEntry: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Changesets", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowWal + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthWal + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthWal + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Changesets = append(m.Changesets, &NamedChangeSet{}) + if err := m.Changesets[len(m.Changesets)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Upgrades", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowWal + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthWal + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthWal + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Upgrades = append(m.Upgrades, &TreeNameUpgrade{}) + if err := m.Upgrades[len(m.Upgrades)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipWal(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthWal + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MultiTreeMetadata) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowWal + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MultiTreeMetadata: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MultiTreeMetadata: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CommitInfo", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowWal + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthWal + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthWal + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.CommitInfo == nil { + m.CommitInfo = &CommitInfo{} + } + if err := m.CommitInfo.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field InitialVersion", wireType) + } + m.InitialVersion = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowWal + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.InitialVersion |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipWal(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthWal + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipWal(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowWal + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowWal + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowWal + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthWal + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupWal + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthWal + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthWal = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowWal = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupWal = fmt.Errorf("proto: unexpected end of group") +) diff --git a/memiavl/wal_test.go b/memiavl/wal_test.go new file mode 100644 index 0000000..5c24d8d --- /dev/null +++ b/memiavl/wal_test.go @@ -0,0 +1,45 @@ +package memiavl + +import ( + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/require" + "github.com/tidwall/wal" +) + +func TestCorruptedTail(t *testing.T) { + opts := &wal.Options{ + LogFormat: wal.JSON, + } + dir := t.TempDir() + + testCases := []struct { + name string + logs []byte + lastIndex uint64 + }{ + {"failure-1", []byte("\n"), 0}, + {"failure-2", []byte(`{}` + "\n"), 0}, + {"failure-3", []byte(`{"index":"1"}` + "\n"), 0}, + {"failure-4", []byte(`{"index":"1","data":"?"}`), 0}, + {"failure-5", []byte(`{"index":1,"data":"?"}` + "\n" + `{"index":"1","data":"?"}`), 1}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + os.WriteFile(filepath.Join(dir, "00000000000000000001"), tc.logs, 0o600) + + _, err := wal.Open(dir, opts) + require.Equal(t, wal.ErrCorrupt, err) + + log, err := OpenWAL(dir, opts) + require.NoError(t, err) + + lastIndex, err := log.LastIndex() + require.NoError(t, err) + require.Equal(t, tc.lastIndex, lastIndex) + }) + } +} diff --git a/store/cachemulti/store.go b/store/cachemulti/store.go new file mode 100644 index 0000000..57912c5 --- /dev/null +++ b/store/cachemulti/store.go @@ -0,0 +1,37 @@ +package cachemulti + +import ( + "io" + + "cosmossdk.io/store/cachemulti" + "cosmossdk.io/store/types" +) + +var NoopCloser io.Closer = CloserFunc(func() error { return nil }) + +type CloserFunc func() error + +func (fn CloserFunc) Close() error { + return fn() +} + +// Store wraps sdk's cachemulti.Store to add io.Closer interface +type Store struct { + cachemulti.Store + io.Closer +} + +func NewStore( + stores map[types.StoreKey]types.CacheWrapper, + traceWriter io.Writer, traceContext types.TraceContext, + closer io.Closer, +) Store { + if closer == nil { + closer = NoopCloser + } + store := cachemulti.NewStore(nil, stores, nil, traceWriter, traceContext) + return Store{ + Store: store, + Closer: closer, + } +} diff --git a/store/config/config.go b/store/config/config.go new file mode 100644 index 0000000..03f4c8c --- /dev/null +++ b/store/config/config.go @@ -0,0 +1,32 @@ +package config + +import "github.com/crypto-org-chain/cronos/memiavl" + +const DefaultCacheSize = 1000 + +type MemIAVLConfig struct { + // Enable defines if the memiavl should be enabled. + Enable bool `mapstructure:"enable"` + // ZeroCopy defines if the memiavl should return slices pointing to mmap-ed buffers directly (zero-copy), + // the zero-copied slices must not be retained beyond current block's execution. + // the sdk address cache will be disabled if zero-copy is enabled. + ZeroCopy bool `mapstructure:"zero-copy"` + // AsyncCommitBuffer defines the size of asynchronous commit queue, this greatly improve block catching-up + // performance, -1 means synchronous commit. + AsyncCommitBuffer int `mapstructure:"async-commit-buffer"` + // SnapshotKeepRecent defines what many old snapshots (excluding the latest one) to keep after new snapshots are + // taken, defaults to 1 to make sure ibc relayers work. + SnapshotKeepRecent uint32 `mapstructure:"snapshot-keep-recent"` + // SnapshotInterval defines the block interval the memiavl snapshot is taken, default to 1000. + SnapshotInterval uint32 `mapstructure:"snapshot-interval"` + // CacheSize defines the size of the cache for each memiavl store. + CacheSize int `mapstructure:"cache-size"` +} + +func DefaultMemIAVLConfig() MemIAVLConfig { + return MemIAVLConfig{ + CacheSize: DefaultCacheSize, + SnapshotInterval: memiavl.DefaultSnapshotInterval, + SnapshotKeepRecent: 1, + } +} diff --git a/store/config/toml.go b/store/config/toml.go new file mode 100644 index 0000000..2848e22 --- /dev/null +++ b/store/config/toml.go @@ -0,0 +1,32 @@ +package config + +// DefaultConfigTemplate defines the configuration template for the memiavl configuration +const DefaultConfigTemplate = ` +############################################################################### +### MemIAVL Configuration ### +############################################################################### + +[memiavl] + +# Enable defines if the memiavl should be enabled. +enable = {{ .MemIAVL.Enable }} + +# ZeroCopy defines if the memiavl should return slices pointing to mmap-ed buffers directly (zero-copy), +# the zero-copied slices must not be retained beyond current block's execution. +# the sdk address cache will be disabled if zero-copy is enabled. +zero-copy = {{ .MemIAVL.ZeroCopy }} + +# AsyncCommitBuffer defines the size of asynchronous commit queue, this greatly improve block catching-up +# performance, -1 means synchronous commit. +async-commit-buffer = {{ .MemIAVL.AsyncCommitBuffer }} + +# SnapshotKeepRecent defines what many old snapshots (excluding the latest one) to keep after new snapshots are +# taken, defaults to 1 to make sure ibc relayers work. +snapshot-keep-recent = {{ .MemIAVL.SnapshotKeepRecent }} + +# SnapshotInterval defines the block interval the memiavl snapshot is taken, default to 1000. +snapshot-interval = {{ .MemIAVL.SnapshotInterval }} + +# CacheSize defines the size of the cache for each memiavl store, default to 1000. +cache-size = {{ .MemIAVL.CacheSize }} +` diff --git a/store/go.mod b/store/go.mod new file mode 100644 index 0000000..871ece1 --- /dev/null +++ b/store/go.mod @@ -0,0 +1,170 @@ +module github.com/crypto-org-chain/cronos/store + +go 1.22 + +toolchain go1.22.0 + +require ( + cosmossdk.io/errors v1.0.1 + cosmossdk.io/log v1.3.1 + cosmossdk.io/store v1.1.0 + github.com/cometbft/cometbft v0.38.11 + github.com/cosmos/cosmos-db v1.0.2 + github.com/cosmos/cosmos-sdk v0.50.4 + github.com/cosmos/gogoproto v1.4.12 + github.com/cosmos/ics23/go v0.10.0 + github.com/crypto-org-chain/cronos/memiavl v0.0.4 + github.com/spf13/cast v1.6.0 + github.com/stretchr/testify v1.9.0 +) + +require ( + cosmossdk.io/api v0.7.5 // indirect + cosmossdk.io/collections v0.4.0 // indirect + cosmossdk.io/core v0.11.0 // indirect + cosmossdk.io/depinject v1.0.0-alpha.4 // indirect + cosmossdk.io/math v1.3.0 // indirect + cosmossdk.io/x/tx v0.13.3 // indirect + filippo.io/edwards25519 v1.0.0 // indirect + github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect + github.com/99designs/keyring v1.2.1 // indirect + github.com/DataDog/datadog-go v3.2.0+incompatible // indirect + github.com/DataDog/zstd v1.5.5 // indirect + github.com/alitto/pond v1.8.3 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect + github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect + github.com/cenkalti/backoff/v4 v4.1.3 // indirect + github.com/cespare/xxhash v1.1.0 // 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/cometbft/cometbft-db v0.9.1 // indirect + github.com/cosmos/btcutil v1.0.5 // indirect + github.com/cosmos/cosmos-proto v1.0.0-beta.5 // indirect + github.com/cosmos/go-bip39 v1.0.0 // indirect + github.com/cosmos/gogogateway v1.2.0 // indirect + github.com/cosmos/iavl v1.1.2 // indirect + github.com/cosmos/ledger-cosmos-go v0.13.3 // indirect + github.com/danieljoos/wincred v1.1.2 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect + github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect + github.com/dgraph-io/badger/v2 v2.2007.4 // indirect + github.com/dgraph-io/ristretto v0.1.1 // indirect + github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/dvsekhvalnov/jose2go v1.6.0 // indirect + github.com/emicklei/dot v1.6.1 // indirect + github.com/fatih/color v1.15.0 // indirect + github.com/felixge/httpsnoop v1.0.2 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/getsentry/sentry-go v0.27.0 // indirect + github.com/go-kit/kit v0.12.0 // indirect + github.com/go-kit/log v0.2.1 // indirect + github.com/go-logfmt/logfmt v0.6.0 // indirect + github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect + github.com/gogo/googleapis v1.4.1 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/glog v1.2.0 // indirect + github.com/golang/protobuf v1.5.4 // 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/gorilla/handlers v1.5.1 // indirect + github.com/gorilla/mux v1.8.0 // indirect + github.com/gorilla/websocket v1.5.0 // indirect + github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect + github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect + github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect + github.com/hashicorp/go-hclog v1.5.0 // indirect + github.com/hashicorp/go-immutable-radix v1.3.1 // indirect + github.com/hashicorp/go-metrics v0.5.3 // indirect + github.com/hashicorp/go-plugin v1.5.2 // indirect + github.com/hashicorp/golang-lru v1.0.2 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/hashicorp/yamux v0.1.1 // indirect + github.com/hdevalence/ed25519consensus v0.1.0 // indirect + github.com/huandu/skiplist v1.2.0 // indirect + github.com/iancoleman/strcase v0.3.0 // indirect + github.com/improbable-eng/grpc-web v0.15.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/jmhodges/levigo v1.0.0 // indirect + github.com/klauspost/compress v1.17.7 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/ledgerwatch/erigon-lib v0.0.0-20230210071639-db0e7ed11263 // indirect + github.com/linxGnu/grocksdb v1.8.14 // 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/mitchellh/go-testing-interface v1.14.1 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/mtibben/percent v0.2.1 // indirect + github.com/oasisprotocol/curve25519-voi v0.0.0-20230904125328-1f23a7beb09a // indirect + github.com/oklog/run v1.1.0 // indirect + github.com/pelletier/go-toml/v2 v2.1.0 // indirect + github.com/petermattis/goid v0.0.0-20231207134359-e60b3f734c67 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/prometheus/client_golang v1.19.0 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.52.2 // indirect + github.com/prometheus/procfs v0.13.0 // indirect + github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect + github.com/rs/cors v1.8.3 // indirect + github.com/rs/zerolog v1.32.0 // indirect + github.com/sagikazarmark/locafero v0.4.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/sasha-s/go-deadlock v0.3.1 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.11.0 // indirect + github.com/spf13/cobra v1.8.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/spf13/viper v1.18.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 + github.com/tidwall/gjson v1.10.2 // indirect + github.com/tidwall/match v1.1.1 // indirect + github.com/tidwall/pretty v1.2.0 // indirect + github.com/tidwall/tinylru v1.1.0 // indirect + github.com/tidwall/wal v1.1.7 // indirect + github.com/zbiljic/go-filelock v0.0.0-20170914061330-1dbf7103ab7d // indirect + github.com/zondax/hid v0.9.2 // indirect + github.com/zondax/ledger-go v0.14.3 // indirect + go.etcd.io/bbolt v1.3.8 // indirect + go.uber.org/multierr v1.10.0 // indirect + golang.org/x/crypto v0.22.0 // indirect + golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0 // indirect + golang.org/x/net v0.24.0 // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/sys v0.19.0 // indirect + golang.org/x/term v0.19.0 // indirect + golang.org/x/text v0.14.0 // indirect + google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda // indirect + google.golang.org/grpc v1.63.2 // indirect + google.golang.org/protobuf v1.33.0 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + gotest.tools/v3 v3.5.1 // indirect + nhooyr.io/websocket v1.8.6 // indirect + pgregory.net/rapid v1.1.0 // indirect + sigs.k8s.io/yaml v1.4.0 // indirect +) + +// release/v0.50.x +replace ( + cosmossdk.io/store => github.com/crypto-org-chain/cosmos-sdk/store v0.0.0-20240722033504-50f1fa0c49d1 + cosmossdk.io/x/tx => github.com/crypto-org-chain/cosmos-sdk/x/tx v0.0.0-20240722033504-50f1fa0c49d1 + github.com/cosmos/cosmos-sdk => github.com/crypto-org-chain/cosmos-sdk v0.50.6-0.20240722033504-50f1fa0c49d1 +) + +replace github.com/crypto-org-chain/cronos/memiavl => ../memiavl diff --git a/store/go.sum b/store/go.sum new file mode 100644 index 0000000..b74fb22 --- /dev/null +++ b/store/go.sum @@ -0,0 +1,1002 @@ +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/api v0.7.5 h1:eMPTReoNmGUm8DeiQL9DyM8sYDjEhWzL1+nLbI9DqtQ= +cosmossdk.io/api v0.7.5/go.mod h1:IcxpYS5fMemZGqyYtErK7OqvdM0C8kdW3dq8Q/XIG38= +cosmossdk.io/collections v0.4.0 h1:PFmwj2W8szgpD5nOd8GWH6AbYNi1f2J6akWXJ7P5t9s= +cosmossdk.io/collections v0.4.0/go.mod h1:oa5lUING2dP+gdDquow+QjlF45eL1t4TJDypgGd+tv0= +cosmossdk.io/core v0.11.0 h1:vtIafqUi+1ZNAE/oxLOQQ7Oek2n4S48SWLG8h/+wdbo= +cosmossdk.io/core v0.11.0/go.mod h1:LaTtayWBSoacF5xNzoF8tmLhehqlA9z1SWiPuNC6X1w= +cosmossdk.io/depinject v1.0.0-alpha.4 h1:PLNp8ZYAMPTUKyG9IK2hsbciDWqna2z1Wsl98okJopc= +cosmossdk.io/depinject v1.0.0-alpha.4/go.mod h1:HeDk7IkR5ckZ3lMGs/o91AVUc7E596vMaOmslGFM3yU= +cosmossdk.io/errors v1.0.1 h1:bzu+Kcr0kS/1DuPBtUFdWjzLqyUuCiyHjyJB6srBV/0= +cosmossdk.io/errors v1.0.1/go.mod h1:MeelVSZThMi4bEakzhhhE/CKqVv3nOJDA25bIqRDu/U= +cosmossdk.io/log v1.3.1 h1:UZx8nWIkfbbNEWusZqzAx3ZGvu54TZacWib3EzUYmGI= +cosmossdk.io/log v1.3.1/go.mod h1:2/dIomt8mKdk6vl3OWJcPk2be3pGOS8OQaLUM/3/tCM= +cosmossdk.io/math v1.3.0 h1:RC+jryuKeytIiictDslBP9i1fhkVm6ZDmZEoNP316zE= +cosmossdk.io/math v1.3.0/go.mod h1:vnRTxewy+M7BtXBNFybkuhSH4WfedVAAnERHgVFhp3k= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek= +filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= +github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs= +github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4/go.mod h1:hN7oaIRCjzsZ2dE+yG5k+rsdt3qcwykqK6HVGcKwsw4= +github.com/99designs/keyring v1.2.1 h1:tYLp1ULvO7i3fI5vE21ReQuj99QFSs7lGm0xWyJo87o= +github.com/99designs/keyring v1.2.1/go.mod h1:fc+wB5KTk9wQ9sDx0kFXB3A0MaeGHM9AwRStKOQ5vOA= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/DataDog/datadog-go v3.2.0+incompatible h1:qSG2N4FghB1He/r2mFrWKCaL7dXCilEuNEeAn20fdD4= +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/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= +github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= +github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= +github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= +github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/alitto/pond v1.8.3 h1:ydIqygCLVPqIX/USe5EaV/aSRXTRXDEI9JwuDdu+/xs= +github.com/alitto/pond v1.8.3/go.mod h1:CmvIIGd5jKLasGI3D87qDkQxjzChdKMmnXMg3fG6M6Q= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= +github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= +github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +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/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 h1:41iFGWnSlI2gVpmOtVTJZNodLdLQLn/KsJqFvXwnd/s= +github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= +github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= +github.com/btcsuite/btcd/btcutil v1.1.3 h1:xfbtw8lwpp0G6NwSHb+UE67ryTFHJAiNuipusjXSohQ= +github.com/btcsuite/btcd/btcutil v1.1.3/go.mod h1:UR7dsSJzJUfMmFiiLlIrMq1lS9jh9EdCV7FStZSnpi0= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/bufbuild/protocompile v0.6.0 h1:Uu7WiSQ6Yj9DbkdnOe7U4mNKp58y9WDMKDn28/ZlunY= +github.com/bufbuild/protocompile v0.6.0/go.mod h1:YNP35qEYoYGme7QMtz5SBCoN4kL4g12jTtjuzRNdjpE= +github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= +github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= +github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.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/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI= +github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk= +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/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/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 v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +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/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= +github.com/cometbft/cometbft v0.38.11 h1:6bNDUB8/xq4uYonYwIfGc9OqK1ZH4NkdaMmR1LZIJqk= +github.com/cometbft/cometbft v0.38.11/go.mod h1:jHPx9vQpWzPHEAiYI/7EDKaB1NXhK6o3SArrrY8ExKc= +github.com/cometbft/cometbft-db v0.9.1 h1:MIhVX5ja5bXNHF8EYrThkG9F7r9kSfv8BX4LWaxWJ4M= +github.com/cometbft/cometbft-db v0.9.1/go.mod h1:iliyWaoV0mRwBJoizElCwwRA9Tf7jZJOURcRZF9m60U= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cosmos/btcutil v1.0.5 h1:t+ZFcX77LpKtDBhjucvnOH8C2l2ioGsBNEQ3jef8xFk= +github.com/cosmos/btcutil v1.0.5/go.mod h1:IyB7iuqZMJlthe2tkIFL33xPyzbFYP0XVdS8P5lUPis= +github.com/cosmos/cosmos-db v1.0.2 h1:hwMjozuY1OlJs/uh6vddqnk9j7VamLv+0DBlbEXbAKs= +github.com/cosmos/cosmos-db v1.0.2/go.mod h1:Z8IXcFJ9PqKK6BIsVOB3QXtkKoqUOp1vRvPT39kOXEA= +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/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY= +github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4xuwvCdJw= +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.4.12 h1:vB6Lbe/rtnYGjQuFxkPiPYiCybqFT8QvLipDZP8JpFE= +github.com/cosmos/gogoproto v1.4.12/go.mod h1:LnZob1bXRdUoqMMtwYlcR3wjiElmlC+FkjaZRv1/eLY= +github.com/cosmos/iavl v1.1.2 h1:zL9FK7C4L/P4IF1Dm5fIwz0WXCnn7Bp1M2FxH0ayM7Y= +github.com/cosmos/iavl v1.1.2/go.mod h1:jLeUvm6bGT1YutCaL2fIar/8vGUE8cPZvh/gXEWDaDM= +github.com/cosmos/ics23/go v0.10.0 h1:iXqLLgp2Lp+EdpIuwXTYIQU+AiHj9mOC2X9ab++bZDM= +github.com/cosmos/ics23/go v0.10.0/go.mod h1:ZfJSmng/TBNTBkFemHHHj5YY7VAU/MBU980F4VU1NG0= +github.com/cosmos/ledger-cosmos-go v0.13.3 h1:7ehuBGuyIytsXbd4MP43mLeoN2LTOEnk5nvue4rK+yM= +github.com/cosmos/ledger-cosmos-go v0.13.3/go.mod h1:HENcEP+VtahZFw38HZ3+LS3Iv5XV6svsnkk9vdJtLr8= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/crypto-org-chain/cosmos-sdk v0.50.6-0.20240722033504-50f1fa0c49d1 h1:xahHemSiT79xgh8Ig8zOTeHSLHt9FfPzViK7rATWhUM= +github.com/crypto-org-chain/cosmos-sdk v0.50.6-0.20240722033504-50f1fa0c49d1/go.mod h1:Rb43DdB0i/rKcCN69Tg2X3+zA4WhJ7MC8K3a6Ezh38E= +github.com/crypto-org-chain/cosmos-sdk/store v0.0.0-20240722033504-50f1fa0c49d1 h1:ZlezTiQu9pYpVO+6sB9+W3fvthIpV1GgSI8kPjw+v5s= +github.com/crypto-org-chain/cosmos-sdk/store v0.0.0-20240722033504-50f1fa0c49d1/go.mod h1:gjE3DZe4t/+VeIk6CmrouyqiuDbZ7QOVDDq3nLqBTpg= +github.com/crypto-org-chain/cosmos-sdk/x/tx v0.0.0-20240722033504-50f1fa0c49d1 h1:r0ALP31Wnw19FqEmqzsK2SFNqdMetHshnM/X/FeJRIo= +github.com/crypto-org-chain/cosmos-sdk/x/tx v0.0.0-20240722033504-50f1fa0c49d1/go.mod h1:RTiTs4hkXG6IvYGknvB8p79YgjYJdcbzLUOGJChsPnY= +github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0= +github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0= +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/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= +github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= +github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f h1:U5y3Y5UE0w7amNe7Z5G/twsBW0KEalRQXZzf8ufSh9I= +github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE= +github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o= +github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk= +github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= +github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= +github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= +github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/dvsekhvalnov/jose2go v1.6.0 h1:Y9gnSnP4qEI0+/uQkHvFXeD2PLPJeXEL+ySMEA2EjTY= +github.com/dvsekhvalnov/jose2go v1.6.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB/mPZadG+mhXU= +github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= +github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/emicklei/dot v1.6.1 h1:ujpDlBkkwgWUY+qPId5IwapRW/xEoligRSYjioR6DFI= +github.com/emicklei/dot v1.6.1/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s= +github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= +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.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= +github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= +github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/felixge/httpsnoop v1.0.2 h1:+nS9g82KMXccJ/wp0zyRW9ZBHFETmMGtkk+2CTTrW4o= +github.com/felixge/httpsnoop v1.0.2/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= +github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= +github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= +github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/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/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= +github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8= +github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= +github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= +github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= +github.com/go-kit/kit v0.12.0 h1:e4o3o3IsBfAKQh5Qbbiqyfu97Ku7jrO/JbohvztANh4= +github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEaizzs= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= +github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= +github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= +github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= +github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= +github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= +github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ= +github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= +github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +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/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0= +github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= +github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8= +github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo= +github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= +github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk= +github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0= +github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= +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.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68= +github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= +github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +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.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +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 v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/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.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/orderedcode v0.0.1 h1:UzfcAexk9Vhv8+9pNOgRu41f16lHq725vPwnSeiG/Us= +github.com/google/orderedcode v0.0.1/go.mod h1:iVyU4/qPKHY5h/wSd6rZZCDcLJNxiWO6dvsYES2Sb20= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= +github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= +github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= +github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= +github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +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/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8ZofjG1Y75iExal34USq5p+wiN1tpie8IrU= +github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NMPJylDgVpX0MLRlPy15sqSwOFv/U1GZ2m21JhFfek0= +github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= +github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= +github.com/hashicorp/go-hclog v1.5.0/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-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-plugin v1.5.2 h1:aWv8eimFqWlsEiMrYZdPYl+FdHaBJSN4AWwGWfT1G2Y= +github.com/hashicorp/go-plugin v1.5.2/go.mod h1:w1sAEES3g3PuV/RzUrgow20W2uErMly84hhD3um1WL4= +github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= +github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= +github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= +github.com/hdevalence/ed25519consensus v0.1.0 h1:jtBwzzcHuTmFrQN6xQZn6CQEO/V9f7HsjsjeEZ6auqU= +github.com/hdevalence/ed25519consensus v0.1.0/go.mod h1:w3BHWjwJbFU29IRHL1Iqkw3sus+7FctEyM4RqDxYNzo= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/huandu/go-assert v1.1.5 h1:fjemmA7sSfYHJD7CUqs9qTwwfdNAx7/j2/ZlHXzNB3c= +github.com/huandu/go-assert v1.1.5/go.mod h1:yOLvuqZwmcHIC5rIzrBhT7D3Q9c3GFnd0JrPVhn/06U= +github.com/huandu/skiplist v1.2.0 h1:gox56QD77HzSC0w+Ws3MH3iie755GBJU1OER3h5VsYw= +github.com/huandu/skiplist v1.2.0/go.mod h1:7v3iFjLcSAzO4fN5B8dvebvo/qsfumiLiDXMrPiHF9w= +github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= +github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI= +github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/improbable-eng/grpc-web v0.15.0 h1:BN+7z6uNXZ1tQGcNAuaU1YjsLTApzkjt2tzCixLaUPQ= +github.com/improbable-eng/grpc-web v0.15.0/go.mod h1:1sy9HKV4Jt9aEs9JSnkWlRJPuPtwNr0l57L4f878wP8= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= +github.com/jhump/protoreflect v1.15.3 h1:6SFRuqU45u9hIZPJAoZ8c28T3nK64BNdp9w6jFonzls= +github.com/jhump/protoreflect v1.15.3/go.mod h1:4ORHmSBmlCW8fh3xHmJMGyul1zNqZK4Elxc8qKP+p1k= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U= +github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= +github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg= +github.com/klauspost/compress v1.17.7/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/konsorten/go-windows-terminal-sequences v1.0.3/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/ledgerwatch/erigon-lib v0.0.0-20230210071639-db0e7ed11263 h1:LGEzZvf33Y1NhuP5+jI/ni9l1TFS6oYPDilgy74NusM= +github.com/ledgerwatch/erigon-lib v0.0.0-20230210071639-db0e7ed11263/go.mod h1:OXgMDuUo2lZ3NpH29ZvMYbk+LxFd5ffDl2Z2mGMuY/I= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= +github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= +github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= +github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= +github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= +github.com/linxGnu/grocksdb v1.8.14 h1:HTgyYalNwBSG/1qCQUIott44wU5b2Y9Kr3z7SK5OfGQ= +github.com/linxGnu/grocksdb v1.8.14/go.mod h1:QYiYypR2d4v63Wj1adOOfzglnoII0gLj3PNh4fZkcFA= +github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA= +github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.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-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= +github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= +github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.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 h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mtibben/percent v0.2.1 h1:5gssi8Nqo8QU/r2pynCm+hBQHpkB/uNK7BJCFogWdzs= +github.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ibNBTZrns= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/grpc-proxy v0.0.0-20181017164139-0f1106ef9c76/go.mod h1:x5OoJHDHqxHS801UIuhqGl6QdSAEJvtausosHSdazIo= +github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= +github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= +github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= +github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= +github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/oasisprotocol/curve25519-voi v0.0.0-20230904125328-1f23a7beb09a h1:dlRvE5fWabOchtH7znfiFCcOvmIYgOeAS5ifBXBlh9Q= +github.com/oasisprotocol/curve25519-voi v0.0.0-20230904125328-1f23a7beb09a/go.mod h1:hVoHR2EVESiICEMbg137etN/Lx+lSrHPTD39Z/uE+2s= +github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= +github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +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/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.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.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +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/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= +github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= +github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= +github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= +github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= +github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= +github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= +github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= +github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o= +github.com/petermattis/goid v0.0.0-20231207134359-e60b3f734c67 h1:jik8PHtAIsPlCRJjJzl4udgEf7hawInF9texMeO2jrU= +github.com/petermattis/goid v0.0.0-20231207134359-e60b3f734c67/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= +github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/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/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= +github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU= +github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/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.1.0/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.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= +github.com/prometheus/common v0.52.2 h1:LW8Vk7BccEdONfrJBDffQGRtpSzi5CQaRZGtboOO2ck= +github.com/prometheus/common v0.52.2/go.mod h1:lrWtQx+iDfn2mbH5GUzlH9TSHyfZpHkSiG1W7y3sF2Q= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/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.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.3.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.13.0 h1:GqzLlQyfsPbaEHaQkO7tbDlriv/4o5Hudv6OXHGKX7o= +github.com/prometheus/procfs v0.13.0/go.mod h1:cd4PFCR54QLnGKPaKGA6l+cfuNXtht43ZKY6tow0Y1g= +github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= +github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.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/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/rs/cors v1.8.3 h1:O+qNyWn7Z+F9M0ILBHgMVPuB1xTOucVd5gtaYyXBpRo= +github.com/rs/cors v1.8.3/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.32.0 h1:keLypqrlIjaFsbmJOBdB/qvyF8KEtCWHwobLp5l/mQ0= +github.com/rs/zerolog v1.32.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= +github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= +github.com/sasha-s/go-deadlock v0.3.1 h1:sqv7fDNShgjcaxkO0JNcOAlr8B9+cV5Ey/OB71efZx0= +github.com/sasha-s/go-deadlock v0.3.1/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= +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/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +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.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= +github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= +github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= +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.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/subosito/gotenv v1.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/tidwall/gjson v1.10.2 h1:APbLGOM0rrEkd8WBw9C24nllro4ajFuJu0Sc9hRz8Bo= +github.com/tidwall/gjson v1.10.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/tinylru v1.1.0 h1:XY6IUfzVTU9rpwdhKUF6nQdChgCdGjkMfLzbWyiau6I= +github.com/tidwall/tinylru v1.1.0/go.mod h1:3+bX+TJ2baOLMWTnlyNWHh4QMnFyARg2TLTQ6OFbzw8= +github.com/tidwall/wal v1.1.7 h1:emc1TRjIVsdKKSnpwGBAcsAGg0767SvUk8+ygx7Bb+4= +github.com/tidwall/wal v1.1.7/go.mod h1:r6lR1j27W9EPalgHiB7zLJDYu3mzW5BQP5KrzBpYY/E= +github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= +github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= +github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/zbiljic/go-filelock v0.0.0-20170914061330-1dbf7103ab7d h1:XQyeLr7N9iY9mi+TGgsBFkj54+j3fdoo8e2u6zrGP5A= +github.com/zbiljic/go-filelock v0.0.0-20170914061330-1dbf7103ab7d/go.mod h1:hoMeDjlNXTNqVwrCk8YDyaBS2g5vFfEX2ezMi4vb6CY= +github.com/zondax/hid v0.9.2 h1:WCJFnEDMiqGF64nlZz28E9qLVZ0KSJ7xpc5DLEyma2U= +github.com/zondax/hid v0.9.2/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= +github.com/zondax/ledger-go v0.14.3 h1:wEpJt2CEcBJ428md/5MgSLsXLBos98sBOyxNmCjfUCw= +github.com/zondax/ledger-go v0.14.3/go.mod h1:IKKaoxupuB43g4NxeQmbLXv7T9AlQyie1UpHb342ycI= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA= +go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= +go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= +go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= +go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/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-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= +golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= +golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0 h1:985EYyeCOxTpcgOTJpflJUwOeEz0CQOdPt73OzpE9F8= +golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/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-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-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-20190813141303-74dc4d7220e7/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-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-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.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/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-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/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-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-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-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210819135213-f52c844e1c1c/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-20211025201205-69cdffdb9359/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.0.0-20221010170243-090e33056c14/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.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= +golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.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.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-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-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-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/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.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-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +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-20210126160654-44e461bb6506/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +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-20240227224415-6ceb2ff114de h1:jFNzHPIeuzhdRwVhbZdiym9q0ory/xY3sA+v2wPg8I0= +google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:5iCWqnniDlqZHrd3neWVTOwvh/v6s3232omMecelax8= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda h1:LI5DOvAxUPMv/50agcLLoo+AdWc1irS9Rzz4vPuD1V4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.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.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= +google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.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.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +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-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +nhooyr.io/websocket v1.8.6 h1:s+C3xAMLwGmlI31Nyn/eAehUlZPwfYZu2JXM621Q5/k= +nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= +pgregory.net/rapid v1.1.0 h1:CMa0sjHSru3puNx+J0MIAuiiEV4N0qj8/cMWGBBCsjw= +pgregory.net/rapid v1.1.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= +sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= diff --git a/store/memiavlstore/store.go b/store/memiavlstore/store.go new file mode 100644 index 0000000..bd6cdeb --- /dev/null +++ b/store/memiavlstore/store.go @@ -0,0 +1,213 @@ +package memiavlstore + +import ( + "fmt" + "io" + + "cosmossdk.io/errors" + "cosmossdk.io/log" + "cosmossdk.io/store/tracekv" + cmtprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" + ics23 "github.com/cosmos/ics23/go" + "github.com/crypto-org-chain/cronos/memiavl" + + "cosmossdk.io/store/cachekv" + pruningtypes "cosmossdk.io/store/pruning/types" + "cosmossdk.io/store/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +var ( + _ types.KVStore = (*Store)(nil) + _ types.CommitStore = (*Store)(nil) + _ types.CommitKVStore = (*Store)(nil) + _ types.Queryable = (*Store)(nil) +) + +// Store Implements types.KVStore and CommitKVStore. +type Store struct { + tree *memiavl.Tree + logger log.Logger + + changeSet memiavl.ChangeSet +} + +func New(tree *memiavl.Tree, logger log.Logger) *Store { + return &Store{tree: tree, logger: logger} +} + +func (st *Store) SetTree(tree *memiavl.Tree) { + st.tree = tree +} + +func (st *Store) Commit() types.CommitID { + panic("memiavl store is not supposed to be committed alone") +} + +func (st *Store) LastCommitID() types.CommitID { + hash := st.tree.RootHash() + return types.CommitID{ + Version: st.tree.Version(), + Hash: hash, + } +} + +// SetPruning panics as pruning options should be provided at initialization +// since IAVl accepts pruning options directly. +func (st *Store) SetPruning(_ pruningtypes.PruningOptions) { + panic("cannot set pruning options on an initialized IAVL store") +} + +// SetPruning panics as pruning options should be provided at initialization +// since IAVl accepts pruning options directly. +func (st *Store) GetPruning() pruningtypes.PruningOptions { + panic("cannot get pruning options on an initialized IAVL store") +} + +// Implements Store. +func (st *Store) GetStoreType() types.StoreType { + return types.StoreTypeIAVL +} + +func (st *Store) CacheWrap() types.CacheWrap { + return cachekv.NewStore(st) +} + +// CacheWrapWithTrace implements the Store interface. +func (st *Store) CacheWrapWithTrace(w io.Writer, tc types.TraceContext) types.CacheWrap { + return cachekv.NewStore(tracekv.NewStore(st, w, tc)) +} + +// Implements types.KVStore. +// +// we assume Set is only called in `Commit`, so the written state is only visible after commit. +func (st *Store) Set(key, value []byte) { + st.changeSet.Pairs = append(st.changeSet.Pairs, &memiavl.KVPair{ + Key: key, Value: value, + }) +} + +// Implements types.KVStore. +func (st *Store) Get(key []byte) []byte { + return st.tree.Get(key) +} + +// Implements types.KVStore. +func (st *Store) Has(key []byte) bool { + return st.tree.Has(key) +} + +// Implements types.KVStore. +// +// we assume Delete is only called in `Commit`, so the written state is only visible after commit. +func (st *Store) Delete(key []byte) { + st.changeSet.Pairs = append(st.changeSet.Pairs, &memiavl.KVPair{ + Key: key, Delete: true, + }) +} + +func (st *Store) Iterator(start, end []byte) types.Iterator { + return st.tree.Iterator(start, end, true) +} + +func (st *Store) ReverseIterator(start, end []byte) types.Iterator { + return st.tree.Iterator(start, end, false) +} + +// SetInitialVersion sets the initial version of the IAVL tree. It is used when +// starting a new chain at an arbitrary height. +// implements interface StoreWithInitialVersion +func (st *Store) SetInitialVersion(version int64) { + panic("memiavl store's SetInitialVersion is not supposed to be called directly") +} + +// PopChangeSet returns the change set and clear it +func (st *Store) PopChangeSet() memiavl.ChangeSet { + cs := st.changeSet + st.changeSet = memiavl.ChangeSet{} + return cs +} + +func (st *Store) Query(req *types.RequestQuery) (res *types.ResponseQuery, err error) { + if len(req.Data) == 0 { + return nil, errors.Wrap(types.ErrTxDecode, "query cannot be zero length") + } + + if req.Height > 0 && req.Height != st.tree.Version() { + return nil, errors.Wrap(sdkerrors.ErrInvalidHeight, "invalid height") + } + + res = &types.ResponseQuery{ + Height: st.tree.Version(), + } + + switch req.Path { + case "/key": // get by key + res.Key = req.Data // data holds the key bytes + res.Value = st.tree.Get(res.Key) + + if !req.Prove { + break + } + + // get proof from tree and convert to merkle.Proof before adding to result + res.ProofOps = getProofFromTree(st.tree, req.Data, res.Value != nil) + case "/subspace": + pairs := memiavl.Pairs{ + Pairs: make([]memiavl.Pair, 0), + } + + subspace := req.Data + res.Key = subspace + + iterator := types.KVStorePrefixIterator(st, subspace) + for ; iterator.Valid(); iterator.Next() { + pairs.Pairs = append(pairs.Pairs, memiavl.Pair{Key: iterator.Key(), Value: iterator.Value()}) + } + iterator.Close() + + bz, err := pairs.Marshal() + if err != nil { + return nil, errors.Wrapf(err, "failed to marshal KV pairs") + } + + res.Value = bz + default: + return nil, errors.Wrapf(sdkerrors.ErrUnknownRequest, "unexpected query path: %v", req.Path) + } + + return res, nil +} + +func (st *Store) WorkingHash() []byte { + return st.tree.RootHash() +} + +// Takes a MutableTree, a key, and a flag for creating existence or absence proof and returns the +// appropriate merkle.Proof. Since this must be called after querying for the value, this function should never error +// Thus, it will panic on error rather than returning it +func getProofFromTree(tree *memiavl.Tree, key []byte, exists bool) *cmtprotocrypto.ProofOps { + var ( + commitmentProof *ics23.CommitmentProof + err error + ) + + if exists { + // value was found + commitmentProof, err = tree.GetMembershipProof(key) + if err != nil { + // sanity check: If value was found, membership proof must be creatable + panic(fmt.Sprintf("unexpected value for empty proof: %s", err.Error())) + } + } else { + // value wasn't found + commitmentProof, err = tree.GetNonMembershipProof(key) + if err != nil { + // sanity check: If value wasn't found, nonmembership proof must be creatable + panic(fmt.Sprintf("unexpected error for nonexistence proof: %s", err.Error())) + } + } + + op := types.NewIavlCommitmentOp(key, commitmentProof) + return &cmtprotocrypto.ProofOps{Ops: []cmtprotocrypto.ProofOp{op.ProofOp()}} +} diff --git a/store/rootmulti/import.go b/store/rootmulti/import.go new file mode 100644 index 0000000..eb17f06 --- /dev/null +++ b/store/rootmulti/import.go @@ -0,0 +1,91 @@ +package rootmulti + +import ( + "fmt" + "io" + "math" + + "cosmossdk.io/errors" + "cosmossdk.io/store/snapshots/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + protoio "github.com/cosmos/gogoproto/io" + + "github.com/crypto-org-chain/cronos/memiavl" +) + +// Implements interface Snapshotter +func (rs *Store) Restore( + height uint64, format uint32, protoReader protoio.Reader, +) (types.SnapshotItem, error) { + if rs.db != nil { + if err := rs.db.Close(); err != nil { + return types.SnapshotItem{}, fmt.Errorf("failed to close db: %w", err) + } + rs.db = nil + } + + item, err := rs.restore(height, format, protoReader) + if err != nil { + return types.SnapshotItem{}, err + } + + return item, rs.LoadLatestVersion() +} + +func (rs *Store) restore( + height uint64, format uint32, protoReader protoio.Reader, +) (types.SnapshotItem, error) { + importer, err := memiavl.NewMultiTreeImporter(rs.dir, height) + if err != nil { + return types.SnapshotItem{}, err + } + defer importer.Close() + + var snapshotItem types.SnapshotItem +loop: + for { + snapshotItem = types.SnapshotItem{} + err := protoReader.ReadMsg(&snapshotItem) + if err == io.EOF { + break + } else if err != nil { + return types.SnapshotItem{}, errors.Wrap(err, "invalid protobuf message") + } + + switch item := snapshotItem.Item.(type) { + case *types.SnapshotItem_Store: + if err := importer.AddTree(item.Store.Name); err != nil { + return types.SnapshotItem{}, err + } + case *types.SnapshotItem_IAVL: + if item.IAVL.Height > math.MaxInt8 { + return types.SnapshotItem{}, errors.Wrapf(sdkerrors.ErrLogic, "node height %v cannot exceed %v", + item.IAVL.Height, math.MaxInt8) + } + node := &memiavl.ExportNode{ + Key: item.IAVL.Key, + Value: item.IAVL.Value, + Height: int8(item.IAVL.Height), + Version: item.IAVL.Version, + } + // Protobuf does not differentiate between []byte{} as 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 && node.Value == nil { + node.Value = []byte{} + } + importer.AddNode(node) + default: + // unknown element, could be an extension + break loop + } + } + + if err := importer.Finalize(); err != nil { + return types.SnapshotItem{}, err + } + + return snapshotItem, nil +} diff --git a/store/rootmulti/objstore.go b/store/rootmulti/objstore.go new file mode 100644 index 0000000..074b662 --- /dev/null +++ b/store/rootmulti/objstore.go @@ -0,0 +1,33 @@ +//go:build objstore +// +build objstore + +package rootmulti + +import ( + "fmt" + + "cosmossdk.io/store/transient" + "cosmossdk.io/store/types" + "github.com/crypto-org-chain/cronos/memiavl" +) + +// Implements interface MultiStore +func (rs *Store) GetObjKVStore(key types.StoreKey) types.ObjKVStore { + s, ok := rs.stores[key].(types.ObjKVStore) + if !ok { + panic(fmt.Sprintf("store with key %v is not ObjKVStore", key)) + } + return s +} + +func (rs *Store) loadExtraStore(db *memiavl.DB, key types.StoreKey, params storeParams) (types.CommitStore, error) { + if params.typ == types.StoreTypeObject { + if _, ok := key.(*types.ObjectStoreKey); !ok { + return nil, fmt.Errorf("unexpected key type for a ObjectStoreKey; got: %s, %T", key.String(), key) + } + + return transient.NewObjStore(), nil + } + + panic(fmt.Sprintf("unrecognized store type %v", params.typ)) +} diff --git a/store/rootmulti/objstore_placeholder.go b/store/rootmulti/objstore_placeholder.go new file mode 100644 index 0000000..ce3d57c --- /dev/null +++ b/store/rootmulti/objstore_placeholder.go @@ -0,0 +1,15 @@ +//go:build !objstore +// +build !objstore + +package rootmulti + +import ( + "fmt" + + "cosmossdk.io/store/types" + "github.com/crypto-org-chain/cronos/memiavl" +) + +func (rs *Store) loadExtraStore(db *memiavl.DB, key types.StoreKey, params storeParams) (types.CommitStore, error) { + panic(fmt.Sprintf("unrecognized store type %v", params.typ)) +} diff --git a/store/rootmulti/snapshot.go b/store/rootmulti/snapshot.go new file mode 100644 index 0000000..f3202a9 --- /dev/null +++ b/store/rootmulti/snapshot.go @@ -0,0 +1,70 @@ +package rootmulti + +import ( + "errors" + "fmt" + "math" + + "cosmossdk.io/store/snapshots/types" + protoio "github.com/cosmos/gogoproto/io" + + "github.com/crypto-org-chain/cronos/memiavl" +) + +// Implements interface Snapshotter +func (rs *Store) Snapshot(height uint64, protoWriter protoio.Writer) (returnErr error) { + if height > math.MaxUint32 { + return fmt.Errorf("height overflows uint32: %d", height) + } + version := uint32(height) + + exporter, err := memiavl.NewMultiTreeExporter(rs.dir, version, rs.supportExportNonSnapshotVersion) + if err != nil { + return err + } + + defer func() { + returnErr = errors.Join(returnErr, exporter.Close()) + }() + + for { + item, err := exporter.Next() + if err != nil { + if err == memiavl.ErrorExportDone { + break + } + + return err + } + + switch item := item.(type) { + case *memiavl.ExportNode: + if err := protoWriter.WriteMsg(&types.SnapshotItem{ + Item: &types.SnapshotItem_IAVL{ + IAVL: &types.SnapshotIAVLItem{ + Key: item.Key, + Value: item.Value, + Height: int32(item.Height), + Version: item.Version, + }, + }, + }); err != nil { + return err + } + case string: + if err := protoWriter.WriteMsg(&types.SnapshotItem{ + Item: &types.SnapshotItem_Store{ + Store: &types.SnapshotStoreItem{ + Name: item, + }, + }, + }); err != nil { + return err + } + default: + return fmt.Errorf("unknown item type %T", item) + } + } + + return nil +} diff --git a/store/rootmulti/store.go b/store/rootmulti/store.go new file mode 100644 index 0000000..205f2f4 --- /dev/null +++ b/store/rootmulti/store.go @@ -0,0 +1,662 @@ +package rootmulti + +import ( + "fmt" + "io" + "math" + "sort" + "strings" + + "cosmossdk.io/errors" + "cosmossdk.io/log" + "cosmossdk.io/store/listenkv" + "cosmossdk.io/store/mem" + "cosmossdk.io/store/metrics" + pruningtypes "cosmossdk.io/store/pruning/types" + "cosmossdk.io/store/rootmulti" + "cosmossdk.io/store/transient" + "cosmossdk.io/store/types" + dbm "github.com/cosmos/cosmos-db" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + + "github.com/crypto-org-chain/cronos/memiavl" + "github.com/crypto-org-chain/cronos/store/cachemulti" + "github.com/crypto-org-chain/cronos/store/memiavlstore" +) + +const CommitInfoFileName = "commit_infos" + +var ( + _ types.CommitMultiStore = (*Store)(nil) + _ types.Queryable = (*Store)(nil) +) + +type Store struct { + dir string + db *memiavl.DB + logger log.Logger + + // to keep it comptaible with cosmos-sdk 0.46, merge the memstores into commit info + lastCommitInfo *types.CommitInfo + + storesParams map[types.StoreKey]storeParams + keysByName map[string]types.StoreKey + stores map[types.StoreKey]types.CommitStore + listeners map[types.StoreKey]*types.MemoryListener + + opts memiavl.Options + + // sdk46Compact defines if the root hash is compatible with cosmos-sdk 0.46 and before. + sdk46Compact bool + // it's more efficient to export snapshot versions, we can filter out the non-snapshot versions + supportExportNonSnapshotVersion bool +} + +func NewStore(dir string, logger log.Logger, sdk46Compact bool, supportExportNonSnapshotVersion bool) *Store { + return &Store{ + dir: dir, + logger: logger, + sdk46Compact: sdk46Compact, + supportExportNonSnapshotVersion: supportExportNonSnapshotVersion, + + storesParams: make(map[types.StoreKey]storeParams), + keysByName: make(map[string]types.StoreKey), + stores: make(map[types.StoreKey]types.CommitStore), + listeners: make(map[types.StoreKey]*types.MemoryListener), + } +} + +// flush writes all the pending change sets to memiavl tree. +func (rs *Store) flush() error { + var changeSets []*memiavl.NamedChangeSet + for key := range rs.stores { + // it'll unwrap the inter-block cache + store := rs.GetCommitStore(key) + if memiavlStore, ok := store.(*memiavlstore.Store); ok { + cs := memiavlStore.PopChangeSet() + if len(cs.Pairs) > 0 { + changeSets = append(changeSets, &memiavl.NamedChangeSet{ + Name: key.Name(), + Changeset: cs, + }) + } + } + } + sort.SliceStable(changeSets, func(i, j int) bool { + return changeSets[i].Name < changeSets[j].Name + }) + + return rs.db.ApplyChangeSets(changeSets) +} + +// WorkingHash returns the app hash of the working tree, +// +// Implements interface Committer. +func (rs *Store) WorkingHash() []byte { + if err := rs.flush(); err != nil { + panic(err) + } + commitInfo := convertCommitInfo(rs.db.WorkingCommitInfo()) + if rs.sdk46Compact { + commitInfo = amendCommitInfo(commitInfo, rs.storesParams) + } + return commitInfo.Hash() +} + +// Implements interface Committer +func (rs *Store) Commit() types.CommitID { + if err := rs.flush(); err != nil { + panic(err) + } + + for _, store := range rs.stores { + if store.GetStoreType() != types.StoreTypeIAVL { + _ = store.Commit() + } + } + + _, err := rs.db.Commit() + if err != nil { + panic(err) + } + + // the underlying memiavl tree might be reloaded, update the tree. + for key := range rs.stores { + store := rs.stores[key] + if store.GetStoreType() == types.StoreTypeIAVL { + store.(*memiavlstore.Store).SetTree(rs.db.TreeByName(key.Name())) + } + } + + rs.lastCommitInfo = convertCommitInfo(rs.db.LastCommitInfo()) + if rs.sdk46Compact { + rs.lastCommitInfo = amendCommitInfo(rs.lastCommitInfo, rs.storesParams) + } + return rs.lastCommitInfo.CommitID() +} + +func (rs *Store) Close() error { + return rs.db.Close() +} + +// Implements interface Committer +func (rs *Store) LastCommitID() types.CommitID { + if rs.lastCommitInfo == nil { + v, err := memiavl.GetLatestVersion(rs.dir) + if err != nil { + panic(fmt.Errorf("failed to get latest version: %w", err)) + } + return types.CommitID{Version: v} + } + + return rs.lastCommitInfo.CommitID() +} + +// Implements interface Committer +func (rs *Store) SetPruning(pruningtypes.PruningOptions) { +} + +// SetMetrics sets the metrics gatherer for the store package +func (rs *Store) SetMetrics(metrics metrics.StoreMetrics) { +} + +// Implements interface Committer +func (rs *Store) GetPruning() pruningtypes.PruningOptions { + return pruningtypes.NewPruningOptions(pruningtypes.PruningDefault) +} + +// Implements interface Store +func (rs *Store) GetStoreType() types.StoreType { + return types.StoreTypeMulti +} + +// Implements interface CacheWrapper +func (rs *Store) CacheWrap() types.CacheWrap { + return rs.CacheMultiStore().(types.CacheWrap) +} + +// Implements interface CacheWrapper +func (rs *Store) CacheWrapWithTrace(_ io.Writer, _ types.TraceContext) types.CacheWrap { + return rs.CacheWrap() +} + +// Implements interface MultiStore +func (rs *Store) CacheMultiStore() types.CacheMultiStore { + stores := make(map[types.StoreKey]types.CacheWrapper) + for k, v := range rs.stores { + store := types.CacheWrapper(v) + if kv, ok := store.(types.KVStore); ok { + // Wire the listenkv.Store to allow listeners to observe the writes from the cache store, + // set same listeners on cache store will observe duplicated writes. + if rs.ListeningEnabled(k) { + store = listenkv.NewStore(kv, k, rs.listeners[k]) + } + } + stores[k] = store + } + return cachemulti.NewStore(stores, nil, nil, nil) +} + +// Implements interface MultiStore +// used to createQueryContext, abci_query or grpc query service. +func (rs *Store) CacheMultiStoreWithVersion(version int64) (types.CacheMultiStore, error) { + if version == 0 || (rs.lastCommitInfo != nil && version == rs.lastCommitInfo.Version) { + return rs.CacheMultiStore(), nil + } + opts := rs.opts + opts.TargetVersion = uint32(version) + opts.ReadOnly = true + db, err := memiavl.Load(rs.dir, opts) + if err != nil { + return nil, err + } + + stores := make(map[types.StoreKey]types.CacheWrapper) + + // add the transient/mem stores registered in current app. + for k, store := range rs.stores { + if store.GetStoreType() != types.StoreTypeIAVL { + stores[k] = store + } + } + + // add all the iavl stores at the target version. + for _, tree := range db.Trees() { + stores[rs.keysByName[tree.Name]] = memiavlstore.New(tree.Tree, rs.logger) + } + + return cachemulti.NewStore(stores, nil, nil, nil), nil +} + +// Implements interface MultiStore +func (rs *Store) GetStore(key types.StoreKey) types.Store { + s, ok := rs.stores[key] + if !ok { + panic(fmt.Sprintf("store does not exist for key: %s", key.Name())) + } + return s +} + +// Implements interface MultiStore +func (rs *Store) GetKVStore(key types.StoreKey) types.KVStore { + s, ok := rs.GetStore(key).(types.KVStore) + if !ok { + panic(fmt.Sprintf("store with key %v is not KVStore", key)) + } + return s +} + +// Implements interface MultiStore +func (rs *Store) TracingEnabled() bool { + return false +} + +// Implements interface MultiStore +func (rs *Store) SetTracer(w io.Writer) types.MultiStore { + return nil +} + +// Implements interface MultiStore +func (rs *Store) SetTracingContext(types.TraceContext) types.MultiStore { + return nil +} + +// Implements interface MultiStore +func (rs *Store) LatestVersion() int64 { + return rs.db.Version() +} + +// Implements interface Snapshotter +// not needed, memiavl manage its own snapshot/pruning strategy +func (rs *Store) PruneSnapshotHeight(height int64) { +} + +// Implements interface Snapshotter +// not needed, memiavl manage its own snapshot/pruning strategy +func (rs *Store) SetSnapshotInterval(snapshotInterval uint64) { +} + +// Implements interface CommitMultiStore +func (rs *Store) MountStoreWithDB(key types.StoreKey, typ types.StoreType, _ dbm.DB) { + if key == nil { + panic("MountIAVLStore() key cannot be nil") + } + if _, ok := rs.storesParams[key]; ok { + panic(fmt.Sprintf("store duplicate store key %v", key)) + } + if _, ok := rs.keysByName[key.Name()]; ok { + panic(fmt.Sprintf("store duplicate store key name %v", key)) + } + rs.storesParams[key] = newStoreParams(key, typ) + rs.keysByName[key.Name()] = key +} + +// Implements interface CommitMultiStore +func (rs *Store) GetCommitStore(key types.StoreKey) types.CommitStore { + return rs.stores[key] +} + +// Implements interface CommitMultiStore +func (rs *Store) GetCommitKVStore(key types.StoreKey) types.CommitKVStore { + store, ok := rs.GetCommitStore(key).(types.CommitKVStore) + if !ok { + panic(fmt.Sprintf("store with key %v is not CommitKVStore", key)) + } + + return store +} + +// Implements interface CommitMultiStore +// used by normal node startup. +func (rs *Store) LoadLatestVersion() error { + return rs.LoadVersionAndUpgrade(0, nil) +} + +// Implements interface CommitMultiStore +func (rs *Store) LoadLatestVersionAndUpgrade(upgrades *types.StoreUpgrades) error { + return rs.LoadVersionAndUpgrade(0, upgrades) +} + +// Implements interface CommitMultiStore +// used by node startup with UpgradeStoreLoader +func (rs *Store) LoadVersionAndUpgrade(version int64, upgrades *types.StoreUpgrades) error { + if version > math.MaxUint32 { + return fmt.Errorf("version overflows uint32: %d", version) + } + + storesKeys := make([]types.StoreKey, 0, len(rs.storesParams)) + for key := range rs.storesParams { + storesKeys = append(storesKeys, key) + } + // deterministic iteration order for upgrades + sort.Slice(storesKeys, func(i, j int) bool { + return storesKeys[i].Name() < storesKeys[j].Name() + }) + + initialStores := make([]string, 0, len(storesKeys)) + for _, key := range storesKeys { + if rs.storesParams[key].typ == types.StoreTypeIAVL { + initialStores = append(initialStores, key.Name()) + } + } + + opts := rs.opts + opts.CreateIfMissing = true + opts.InitialStores = initialStores + opts.TargetVersion = uint32(version) + db, err := memiavl.Load(rs.dir, opts) + if err != nil { + return errors.Wrapf(err, "fail to load memiavl at %s", rs.dir) + } + + var treeUpgrades []*memiavl.TreeNameUpgrade + if upgrades != nil { + for _, name := range upgrades.Deleted { + treeUpgrades = append(treeUpgrades, &memiavl.TreeNameUpgrade{Name: name, Delete: true}) + } + for _, name := range upgrades.Added { + treeUpgrades = append(treeUpgrades, &memiavl.TreeNameUpgrade{Name: name}) + } + for _, rename := range upgrades.Renamed { + treeUpgrades = append(treeUpgrades, &memiavl.TreeNameUpgrade{Name: rename.NewKey, RenameFrom: rename.OldKey}) + } + } + + if len(treeUpgrades) > 0 { + if err := db.ApplyUpgrades(treeUpgrades); err != nil { + return err + } + } + + newStores := make(map[types.StoreKey]types.CommitStore, len(storesKeys)) + for _, key := range storesKeys { + newStores[key], err = rs.loadCommitStoreFromParams(db, key, rs.storesParams[key]) + if err != nil { + return err + } + } + + rs.db = db + rs.stores = newStores + // to keep the root hash compatible with cosmos-sdk 0.46 + if db.Version() != 0 { + rs.lastCommitInfo = convertCommitInfo(db.LastCommitInfo()) + if rs.sdk46Compact { + rs.lastCommitInfo = amendCommitInfo(rs.lastCommitInfo, rs.storesParams) + } + } else { + rs.lastCommitInfo = &types.CommitInfo{} + } + + return nil +} + +func (rs *Store) loadCommitStoreFromParams(db *memiavl.DB, key types.StoreKey, params storeParams) (types.CommitStore, error) { + switch params.typ { + case types.StoreTypeMulti: + panic("recursive MultiStores not yet supported") + case types.StoreTypeIAVL: + tree := db.TreeByName(key.Name()) + if tree == nil { + return nil, fmt.Errorf("new store is not added in upgrades: %s", key.Name()) + } + return types.CommitStore(memiavlstore.New(tree, rs.logger)), nil + case types.StoreTypeDB: + panic("recursive MultiStores not yet supported") + case types.StoreTypeTransient: + if _, ok := key.(*types.TransientStoreKey); !ok { + return nil, fmt.Errorf("unexpected key type for a TransientStoreKey; got: %s, %T", key.String(), key) + } + + return transient.NewStore(), nil + + case types.StoreTypeMemory: + if _, ok := key.(*types.MemoryStoreKey); !ok { + return nil, fmt.Errorf("unexpected key type for a MemoryStoreKey; got: %s", key.String()) + } + + return mem.NewStore(), nil + + default: + return rs.loadExtraStore(db, key, params) + } +} + +// Implements interface CommitMultiStore +// used by export cmd +func (rs *Store) LoadVersion(ver int64) error { + return rs.LoadVersionAndUpgrade(ver, nil) +} + +// SetInterBlockCache is a noop here because memiavl do caching on it's own, which works well with zero-copy. +func (rs *Store) SetInterBlockCache(c types.MultiStorePersistentCache) {} + +// Implements interface CommitMultiStore +// used by InitChain when the initial height is bigger than 1 +func (rs *Store) SetInitialVersion(version int64) error { + return rs.db.SetInitialVersion(version) +} + +// Implements interface CommitMultiStore +func (rs *Store) SetIAVLCacheSize(size int) { +} + +// Implements interface CommitMultiStore +func (rs *Store) SetIAVLDisableFastNode(disable bool) { +} + +// Implements interface CommitMultiStore +func (rs *Store) SetLazyLoading(lazyLoading bool) { +} + +func (rs *Store) SetMemIAVLOptions(opts memiavl.Options) { + if opts.Logger == nil { + opts.Logger = memiavl.Logger(rs.logger.With("module", "memiavl")) + } + rs.opts = opts +} + +// RollbackToVersion delete the versions after `target` and update the latest version. +// it should only be called in standalone cli commands. +func (rs *Store) RollbackToVersion(target int64) error { + if target <= 0 { + return fmt.Errorf("invalid rollback height target: %d", target) + } + + if target > math.MaxUint32 { + return fmt.Errorf("rollback height target %d exceeds max uint32", target) + } + + if rs.db != nil { + if err := rs.db.Close(); err != nil { + return err + } + } + + opts := rs.opts + opts.TargetVersion = uint32(target) + opts.LoadForOverwriting = true + + var err error + rs.db, err = memiavl.Load(rs.dir, opts) + + return err +} + +// Implements interface CommitMultiStore +func (rs *Store) ListeningEnabled(key types.StoreKey) bool { + if ls, ok := rs.listeners[key]; ok { + return ls != nil + } + return false +} + +// Implements interface CommitMultiStore +func (rs *Store) AddListeners(keys []types.StoreKey) { + for i := range keys { + listener := rs.listeners[keys[i]] + if listener == nil { + rs.listeners[keys[i]] = types.NewMemoryListener() + } + } +} + +// PopStateCache returns the accumulated state change messages from the CommitMultiStore +// Calling PopStateCache destroys only the currently accumulated state in each listener +// not the state in the store itself. This is a mutating and destructive operation. +// This method has been synchronized. +func (rs *Store) PopStateCache() []*types.StoreKVPair { + var cache []*types.StoreKVPair + for key := range rs.listeners { + ls := rs.listeners[key] + if ls != nil { + cache = append(cache, ls.PopStateCache()...) + } + } + sort.SliceStable(cache, func(i, j int) bool { + return cache[i].StoreKey < cache[j].StoreKey + }) + return cache +} + +// getStoreByName performs a lookup of a StoreKey given a store name typically +// provided in a path. The StoreKey is then used to perform a lookup and return +// a Store. If the Store is wrapped in an inter-block cache, it will be unwrapped +// prior to being returned. If the StoreKey does not exist, nil is returned. +func (rs *Store) GetStoreByName(name string) types.Store { + key := rs.keysByName[name] + if key == nil { + return nil + } + + return rs.GetCommitStore(key) +} + +// Implements interface Queryable +func (rs *Store) Query(req *types.RequestQuery) (*types.ResponseQuery, error) { + version := req.Height + if version == 0 { + version = rs.db.Version() + } + + // If the request's height is the latest height we've committed, then utilize + // the store's lastCommitInfo as this commit info may not be flushed to disk. + // Otherwise, we query for the commit info from disk. + db := rs.db + if version != rs.lastCommitInfo.Version { + var err error + db, err = memiavl.Load(rs.dir, memiavl.Options{TargetVersion: uint32(version), ReadOnly: true}) + if err != nil { + return nil, err + } + defer db.Close() + } + + path := req.Path + storeName, subpath, err := parsePath(path) + if err != nil { + return nil, err + } + + store := types.Queryable(memiavlstore.New(db.TreeByName(storeName), rs.logger)) + + // trim the path and make the query + req.Path = subpath + res, err := store.Query(req) + if err != nil { + return nil, err + } + + if !req.Prove || !rootmulti.RequireProof(subpath) { + return res, nil + } + + if res.ProofOps == nil || len(res.ProofOps.Ops) == 0 { + return nil, errors.Wrap(sdkerrors.ErrInvalidRequest, "proof is unexpectedly empty; ensure height has not been pruned") + } + + commitInfo := convertCommitInfo(db.LastCommitInfo()) + if rs.sdk46Compact { + commitInfo = amendCommitInfo(commitInfo, rs.storesParams) + } + + // Restore origin path and append proof op. + res.ProofOps.Ops = append(res.ProofOps.Ops, commitInfo.ProofOp(storeName)) + + return res, nil +} + +// parsePath expects a format like /[/] +// Must start with /, subpath may be empty +// Returns error if it doesn't start with / +func parsePath(path string) (storeName string, subpath string, err error) { + if !strings.HasPrefix(path, "/") { + return storeName, subpath, errors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid path: %s", path) + } + + paths := strings.SplitN(path[1:], "/", 2) + storeName = paths[0] + + if len(paths) == 2 { + subpath = "/" + paths[1] + } + + return storeName, subpath, nil +} + +type storeParams struct { + key types.StoreKey + typ types.StoreType +} + +func newStoreParams(key types.StoreKey, typ types.StoreType) storeParams { + return storeParams{ + key: key, + typ: typ, + } +} + +func mergeStoreInfos(commitInfo *types.CommitInfo, storeInfos []types.StoreInfo) *types.CommitInfo { + infos := make([]types.StoreInfo, 0, len(commitInfo.StoreInfos)+len(storeInfos)) + infos = append(infos, commitInfo.StoreInfos...) + infos = append(infos, storeInfos...) + sort.SliceStable(infos, func(i, j int) bool { + return infos[i].Name < infos[j].Name + }) + return &types.CommitInfo{ + Version: commitInfo.Version, + StoreInfos: infos, + } +} + +// amendCommitInfo add mem stores commit infos to keep it compatible with cosmos-sdk 0.46 +func amendCommitInfo(commitInfo *types.CommitInfo, storeParams map[types.StoreKey]storeParams) *types.CommitInfo { + var extraStoreInfos []types.StoreInfo + for key := range storeParams { + typ := storeParams[key].typ + if typ != types.StoreTypeIAVL && typ != types.StoreTypeTransient { + extraStoreInfos = append(extraStoreInfos, types.StoreInfo{ + Name: key.Name(), + CommitId: types.CommitID{}, + }) + } + } + return mergeStoreInfos(commitInfo, extraStoreInfos) +} + +func convertCommitInfo(commitInfo *memiavl.CommitInfo) *types.CommitInfo { + storeInfos := make([]types.StoreInfo, len(commitInfo.StoreInfos)) + for i, storeInfo := range commitInfo.StoreInfos { + storeInfos[i] = types.StoreInfo{ + Name: storeInfo.Name, + CommitId: types.CommitID{ + Version: storeInfo.CommitId.Version, + Hash: storeInfo.CommitId.Hash, + }, + } + } + return &types.CommitInfo{ + Version: commitInfo.Version, + StoreInfos: storeInfos, + } +} diff --git a/store/rootmulti/store_test.go b/store/rootmulti/store_test.go new file mode 100644 index 0000000..5bb13f7 --- /dev/null +++ b/store/rootmulti/store_test.go @@ -0,0 +1,14 @@ +package rootmulti + +import ( + "testing" + + "cosmossdk.io/log" + "cosmossdk.io/store/types" + "github.com/stretchr/testify/require" +) + +func TestLastCommitID(t *testing.T) { + store := NewStore(t.TempDir(), log.NewNopLogger(), false, false) + require.Equal(t, types.CommitID{}, store.LastCommitID()) +} diff --git a/store/setup.go b/store/setup.go new file mode 100644 index 0000000..ee4bd9f --- /dev/null +++ b/store/setup.go @@ -0,0 +1,71 @@ +package store + +import ( + "path/filepath" + + "cosmossdk.io/log" + "github.com/spf13/cast" + + "github.com/cosmos/cosmos-sdk/baseapp" + servertypes "github.com/cosmos/cosmos-sdk/server/types" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/crypto-org-chain/cronos/memiavl" + "github.com/crypto-org-chain/cronos/store/rootmulti" +) + +const ( + FlagMemIAVL = "memiavl.enable" + FlagAsyncCommitBuffer = "memiavl.async-commit-buffer" + FlagZeroCopy = "memiavl.zero-copy" + FlagSnapshotKeepRecent = "memiavl.snapshot-keep-recent" + FlagSnapshotInterval = "memiavl.snapshot-interval" + FlagCacheSize = "memiavl.cache-size" + FlagSnapshotWriterLimit = "memiavl.snapshot-writer-limit" +) + +// SetupMemIAVL insert the memiavl setter in front of baseapp options, so that +// the default rootmulti store is replaced by memiavl store, +func SetupMemIAVL( + logger log.Logger, + homePath string, + appOpts servertypes.AppOptions, + sdk46Compact bool, + supportExportNonSnapshotVersion bool, + cacheSize int, + baseAppOptions []func(*baseapp.BaseApp), +) []func(*baseapp.BaseApp) { + if cast.ToBool(appOpts.Get(FlagMemIAVL)) { + opts := memiavl.Options{ + AsyncCommitBuffer: cast.ToInt(appOpts.Get(FlagAsyncCommitBuffer)), + ZeroCopy: cast.ToBool(appOpts.Get(FlagZeroCopy)), + SnapshotKeepRecent: cast.ToUint32(appOpts.Get(FlagSnapshotKeepRecent)), + SnapshotInterval: cast.ToUint32(appOpts.Get(FlagSnapshotInterval)), + CacheSize: cacheSize, + SnapshotWriterLimit: cast.ToInt(appOpts.Get(FlagSnapshotWriterLimit)), + } + + if opts.ZeroCopy { + // it's unsafe to cache zero-copied byte slices without copying them + sdk.SetAddrCacheEnabled(false) + } + + // cms must be overridden before the other options, because they may use the cms, + // make sure the cms aren't be overridden by the other options later on. + baseAppOptions = append([]func(*baseapp.BaseApp){setMemIAVL(homePath, logger, opts, sdk46Compact, supportExportNonSnapshotVersion)}, baseAppOptions...) + } + + return baseAppOptions +} + +func setMemIAVL(homePath string, logger log.Logger, opts memiavl.Options, sdk46Compact bool, supportExportNonSnapshotVersion bool) func(*baseapp.BaseApp) { + return func(bapp *baseapp.BaseApp) { + // trigger state-sync snapshot creation by memiavl + opts.TriggerStateSyncExport = func(height int64) { + go bapp.SnapshotManager().SnapshotIfApplicable(height) + } + cms := rootmulti.NewStore(filepath.Join(homePath, "data", "memiavl.db"), logger, sdk46Compact, supportExportNonSnapshotVersion) + cms.SetMemIAVLOptions(opts) + bapp.SetCMS(cms) + } +} diff --git a/versiondb/README.md b/versiondb/README.md new file mode 100644 index 0000000..0643667 --- /dev/null +++ b/versiondb/README.md @@ -0,0 +1,109 @@ +# VersionDB + +VersionDB is a solution for the size issue of IAVL database, aka. `application.db`, at current stage, it's only recommended for archive and non-validator nodes to try (validator nodes are recommended to do pruning anyway). + +VersionDB stores multiple versions of on-chain state key-value pairs directly, without using a merklized tree structure like IAVL tree, both db size and query performance are much better than IAVL tree. The major lacking feature compared to IAVL tree is root hash and merkle proof generation, so we still need IAVL tree for those tasks. + +Currently grpc query service don't need to support proof generation, so versiondb alone is enough to support grpc query service, there's already a `--grpc-only` flag for one to start a standalone grpc query service. + +There could be different implementations for the idea of versiondb, the current implementation we delivered is based on rocksdb v7's experimental user-defined timestamp[^1], it stores the data in a standalone rocksdb instance, it don't support other db backend yet, but the other databases in the node still support multiple backends as before. + +After versiondb is enabled, there's no point to keep the full the archived IAVL tree anymore, it's recommended to prune the IAVL tree to keep only recent versions, for example versions within the unbonding period or even less. + +## Configuration + +To enable versiondb, set the `versiondb.enable` to `true` in `app.toml`: + +```toml +[versiondb] +enable = true +``` + +On startup, the node will create a `StreamingService` to subscribe to latest state changes in realtime and save them to versiondb, the db instance is placed at `$NODE_HOME/data/versiondb` directory, there's no way to customize the db path currently. It'll also switch grpc query service's backing store to versiondb from IAVL tree, you should migrate the legacy states in advance to make the transition smooth, otherwise, the grpc queries can't see the legacy versions. + +If the versiondb is not empty and it's latest version doesn't match the IAVL db's last committed version, the startup will fail with error message `"versiondb lastest version %d doesn't match iavl latest version %d"`, that's to avoid creating gaps in versiondb accidentally. When this error happens, you just need to update versiondb to the latest version in iavl tree manually, or restore IAVL db to the same version as versiondb (see [](#catch-up-with-iavl-tree)). + +## Migration + +Since our chain is pretty big now, a lot of efforts have been put to make sure the transition process can finish in practical time. The migration process will try to parallelize the tasks as much as possible, and use significant ram, but there's flags for user to control the concurrency level and ram usage to make it runnable on different machine specs. + +The legacy state migration process is done in two main steps: + +- Extract state change sets from existing archived IAVL tree. +- Feed the change set files to versiondb. + +### Extract Change Sets + +```bash +$ cronosd changeset dump data --home /chain/.cronosd +``` + +`dump` command will extract the change sets from the IAVL tree, and store each store in separate directories, it use the store list registered in current version of `App` by default, you can customize that with `--stores` parameter. The change set files are segmented into different block chunks and compressed with zlib level 6 by default, the chunk size defaults to 1m blocks, the result `data` directly looks like this: + +``` +data/acc/block-0.zz +data/acc/block-1000000.zz +data/acc/block-2000000.zz +... +data/authz/block-0.zz +data/authz/block-1000000.zz +data/authz/block-2000000.zz +... +``` + +The extraction is the slowest step, the test run on testnet archive node takes around 11 hours on a 8core ssd machine, but fortunately, the change set files can be verified pretty fast(a few minutes), so they can be share on CDN in a trustless manner, normal users should just download them from CDN and verify the correctness locally, should be much faster than extract by yourself. + +For rocksdb backend, `dump` command opens the db in readonly mode, it can run on live node's db, but goleveldb backend don't support this feature yet. + +#### Verify Change Sets + +```bash +$ cronosd changeset verify data +35b85a775ff51cbcc48537247eb786f98fc6a178531d48560126e00f545251be +{"version":"189","storeInfos":[{"name":"acc","commitId":{"version":"189" ... +``` + +`verify` command will replay all the change sets and rebuild the target IAVL tree and output the app hash and commit info of the target version (defaults to latest version in the change sets), then user can manually check the app hash against the block headers. + +`verify` command takes several minutes and several gigabytes of ram to run, if ram usage is a problem, it can also run incrementally, you can export the snapshot for a middle version, then verify the remaining versions start from that snapshot: + +```bash +$ cronosd changeset verify data --save-snapshot snapshot --target-version 3000000 +$ cronosd changeset verify data --load-snapshot snapshot +``` + +The format of change set files are documented [here](memiavl/README.md#change-set-file). + +### Build VersionDB + +To maximize the speed of initial data ingestion speed into rocksdb, we take advantage of the sst file writer feature to write out sst files first, then ingest them into final db, the sst files for each store can be written out in parallel. We also developed an external sorting algorithm to sort the data before writing the sst files, so the sst files don't have overlaps and can be ingested into the bottom-most level in final db. + +```bash +$ cronosd changeset build-versiondb-sst ./data ./sst +$ cronosd changeset ingest-versiondb-sst /home/.cronosd/data/versiondb sst/*.sst --move-files --maximum-version 189 +``` + +User can control the peak ram usage by controlling the `--concurrency` and `--sorter-chunk-size`. + +With default parameters it can finish in around 12minutes for testnet archive node on our test node (8cores, peak RSS 2G). + +### Restore IAVL Tree + +When migrating an existing archive node to versiondb, it's recommended to rebuild the `application.db` from scratch to reclaim disk space faster, we provide a command to restore a single version of IAVL trees from memiavl snapshot. + +```bash +$ # create memiavl snapshot +$ cronosd changeset verify data --save-snapshot snapshot +$ # restore application.db +$ cronosd changeset restore-app-db snapshot application.db +``` + +Then replace the whole `application.db` in the node with the newly generated one. + +It only takes a few minutes to run on our testnet archive node, it only suppot generating rocksdb `application.db` right now, so please set `app-db-backend="rocksdb"` in `app.toml`. + +### Catch Up With IAVL Tree + +If an non-empty versiondb lags behind from the current `application.db`, the node will refuse to startup, in this case user can either sync versiondb to catch up with `application.db`, or simply restore the `application.db` with the correct version of snapshot. To catch up, you can follow the similar procedure as migrating from genesis, just passing the block range in change set dump command. + +[^1]: https://github.com/facebook/rocksdb/wiki/User-defined-Timestamp-%28Experimental%29 diff --git a/versiondb/backend_test_utils.go b/versiondb/backend_test_utils.go new file mode 100644 index 0000000..3b08057 --- /dev/null +++ b/versiondb/backend_test_utils.go @@ -0,0 +1,276 @@ +package versiondb + +import ( + "fmt" + "testing" + + dbm "github.com/cosmos/cosmos-db" + "github.com/stretchr/testify/require" + + "cosmossdk.io/store/types" +) + +var ( + key1 = []byte("key1") + value1 = []byte("value1") + key1Subkey = []byte("key1/subkey") +) + +func SetupTestDB(t *testing.T, store VersionStore) { + changeSets := [][]*types.StoreKVPair{ + { + {StoreKey: "evm", Key: []byte("delete-in-block2"), Value: []byte("1")}, + {StoreKey: "evm", Key: []byte("re-add-in-block3"), Value: []byte("1")}, + {StoreKey: "evm", Key: []byte("z-genesis-only"), Value: []byte("2")}, + {StoreKey: "evm", Key: []byte("modify-in-block2"), Value: []byte("1")}, + {StoreKey: "staking", Key: []byte("key1"), Value: []byte("value1")}, + {StoreKey: "staking", Key: []byte("key1/subkey"), Value: []byte("value1")}, + }, + { + {StoreKey: "evm", Key: []byte("re-add-in-block3"), Delete: true}, + {StoreKey: "evm", Key: []byte("add-in-block1"), Value: []byte("1")}, + {StoreKey: "staking", Key: []byte("key1"), Delete: true}, + }, + { + {StoreKey: "evm", Key: []byte("add-in-block2"), Value: []byte("1")}, + {StoreKey: "evm", Key: []byte("delete-in-block2"), Delete: true}, + {StoreKey: "evm", Key: []byte("modify-in-block2"), Value: []byte("2")}, + {StoreKey: "evm", Key: []byte("key2"), Delete: true}, + {StoreKey: "staking", Key: []byte("key1"), Value: []byte("value2")}, + }, + { + {StoreKey: "evm", Key: []byte("re-add-in-block3"), Value: []byte("2")}, + }, + { + {StoreKey: "evm", Key: []byte("re-add-in-block3"), Delete: true}, + }, + } + for i, changeSet := range changeSets { + require.NoError(t, store.PutAtVersion(int64(i), changeSet)) + } +} + +func Run(t *testing.T, storeCreator func() VersionStore) { + testBasics(t, storeCreator()) + testIterator(t, storeCreator()) + testHeightInFuture(t, storeCreator()) + + // test delete in genesis, noop + store := storeCreator() + err := store.PutAtVersion(0, []*types.StoreKVPair{ + {StoreKey: "evm", Key: []byte{1}, Delete: true}, + }) + require.NoError(t, err) +} + +func testBasics(t *testing.T, store VersionStore) { + var v int64 + + SetupTestDB(t, store) + + value, err := store.GetAtVersion("evm", []byte("z-genesis-only"), nil) + require.NoError(t, err) + require.Equal(t, []byte("2"), value) + + v = 4 + ok, err := store.HasAtVersion("evm", []byte("z-genesis-only"), &v) + require.NoError(t, err) + require.True(t, ok) + value, err = store.GetAtVersion("evm", []byte("z-genesis-only"), &v) + require.NoError(t, err) + require.Equal(t, value, []byte("2")) + + value, err = store.GetAtVersion("evm", []byte("re-add-in-block3"), nil) + require.NoError(t, err) + require.Empty(t, value) + + ok, err = store.HasAtVersion("staking", key1, nil) + require.NoError(t, err) + require.True(t, ok) + + value, err = store.GetAtVersion("staking", key1, nil) + require.NoError(t, err) + require.Equal(t, value, []byte("value2")) + + v = 2 + value, err = store.GetAtVersion("staking", key1, &v) + require.NoError(t, err) + require.Equal(t, value, []byte("value2")) + + ok, err = store.HasAtVersion("staking", key1, &v) + require.NoError(t, err) + require.True(t, ok) + + v = 0 + value, err = store.GetAtVersion("staking", key1, &v) + require.NoError(t, err) + require.Equal(t, value, []byte("value1")) + + v = 1 + value, err = store.GetAtVersion("staking", key1, &v) + require.NoError(t, err) + require.Empty(t, value) + + ok, err = store.HasAtVersion("staking", key1, &v) + require.NoError(t, err) + require.False(t, ok) + + v = 0 + value, err = store.GetAtVersion("staking", key1, &v) + require.NoError(t, err) + require.Equal(t, value1, value) + value, err = store.GetAtVersion("staking", key1Subkey, &v) + require.NoError(t, err) + require.Equal(t, value1, value) +} + +type kvPair struct { + Key []byte + Value []byte +} + +func testIterator(t *testing.T, store VersionStore) { + SetupTestDB(t, store) + + expItems := [][]kvPair{ + { + kvPair{[]byte("delete-in-block2"), []byte("1")}, + kvPair{[]byte("modify-in-block2"), []byte("1")}, + kvPair{[]byte("re-add-in-block3"), []byte("1")}, + kvPair{[]byte("z-genesis-only"), []byte("2")}, + }, + { + kvPair{[]byte("add-in-block1"), []byte("1")}, + kvPair{[]byte("delete-in-block2"), []byte("1")}, + kvPair{[]byte("modify-in-block2"), []byte("1")}, + kvPair{[]byte("z-genesis-only"), []byte("2")}, + }, + { + kvPair{[]byte("add-in-block1"), []byte("1")}, + kvPair{[]byte("add-in-block2"), []byte("1")}, + kvPair{[]byte("modify-in-block2"), []byte("2")}, + kvPair{[]byte("z-genesis-only"), []byte("2")}, + }, + { + kvPair{[]byte("add-in-block1"), []byte("1")}, + kvPair{[]byte("add-in-block2"), []byte("1")}, + kvPair{[]byte("modify-in-block2"), []byte("2")}, + kvPair{[]byte("re-add-in-block3"), []byte("2")}, + kvPair{[]byte("z-genesis-only"), []byte("2")}, + }, + { + kvPair{[]byte("add-in-block1"), []byte("1")}, + kvPair{[]byte("add-in-block2"), []byte("1")}, + kvPair{[]byte("modify-in-block2"), []byte("2")}, + kvPair{[]byte("z-genesis-only"), []byte("2")}, + }, + } + for i, exp := range expItems { + t.Run(fmt.Sprintf("block-%d", i), func(t *testing.T) { + v := int64(i) + it, err := store.IteratorAtVersion("evm", nil, nil, &v) + require.NoError(t, err) + require.Equal(t, exp, consumeIterator(it)) + + it, err = store.ReverseIteratorAtVersion("evm", nil, nil, &v) + require.NoError(t, err) + actual := consumeIterator(it) + require.Equal(t, len(exp), len(actual)) + require.Equal(t, reversed(exp), actual) + }) + } + + it, err := store.IteratorAtVersion("evm", nil, nil, nil) + require.NoError(t, err) + require.Equal(t, expItems[len(expItems)-1], consumeIterator(it)) + + it, err = store.ReverseIteratorAtVersion("evm", nil, nil, nil) + require.NoError(t, err) + require.Equal(t, reversed(expItems[len(expItems)-1]), consumeIterator(it)) + + // with start parameter + v := int64(2) + it, err = store.IteratorAtVersion("evm", []byte("\xff"), nil, &v) + require.NoError(t, err) + require.Empty(t, consumeIterator(it)) + it, err = store.ReverseIteratorAtVersion("evm", nil, []byte("\x00"), &v) + require.NoError(t, err) + require.Empty(t, consumeIterator(it)) + + it, err = store.IteratorAtVersion("evm", []byte("modify-in-block2"), nil, &v) + require.NoError(t, err) + require.Equal(t, expItems[2][len(expItems[2])-2:], consumeIterator(it)) + + it, err = store.ReverseIteratorAtVersion("evm", nil, []byte("mp"), &v) + require.NoError(t, err) + require.Equal(t, + reversed(expItems[2][:len(expItems[2])-1]), + consumeIterator(it), + ) + + it, err = store.ReverseIteratorAtVersion("evm", nil, []byte("modify-in-block3"), &v) + require.NoError(t, err) + require.Equal(t, + reversed(expItems[2][:len(expItems[2])-1]), + consumeIterator(it), + ) + + // delete the last key, cover some edge cases + v = int64(len(expItems)) + err = store.PutAtVersion( + v, + []*types.StoreKVPair{ + {StoreKey: "evm", Key: []byte("z-genesis-only"), Delete: true}, + }, + ) + require.NoError(t, err) + it, err = store.IteratorAtVersion("evm", nil, nil, &v) + require.NoError(t, err) + require.Equal(t, + expItems[v-1][:len(expItems[v-1])-1], + consumeIterator(it), + ) + v-- + it, err = store.IteratorAtVersion("evm", nil, nil, &v) + require.NoError(t, err) + require.Equal(t, + expItems[v], + consumeIterator(it), + ) +} + +func testHeightInFuture(t *testing.T, store VersionStore) { + SetupTestDB(t, store) + + latest, err := store.GetLatestVersion() + require.NoError(t, err) + + // query in future height is the same as latest height. + v := latest + 1 + _, err = store.GetAtVersion("staking", key1, &v) + require.NoError(t, err) + _, err = store.HasAtVersion("staking", key1, &v) + require.NoError(t, err) + _, err = store.IteratorAtVersion("staking", nil, nil, &v) + require.NoError(t, err) + _, err = store.ReverseIteratorAtVersion("staking", nil, nil, &v) + require.NoError(t, err) +} + +func consumeIterator(it dbm.Iterator) []kvPair { + var result []kvPair + for ; it.Valid(); it.Next() { + result = append(result, kvPair{it.Key(), it.Value()}) + } + it.Close() + return result +} + +// reversed clone and reverse the slice +func reversed[S ~[]E, E any](s S) []E { + r := make([]E, len(s)) + for i, j := 0, len(s)-1; i <= j; i, j = i+1, j-1 { + r[i], r[j] = s[j], s[i] + } + return r +} diff --git a/versiondb/client/changeset.go b/versiondb/client/changeset.go new file mode 100644 index 0000000..24c5220 --- /dev/null +++ b/versiondb/client/changeset.go @@ -0,0 +1,363 @@ +package client + +import ( + "bufio" + "compress/zlib" + "encoding/binary" + "fmt" + "io" + "math/bits" + "os" + "sort" + "strings" + + "github.com/cosmos/iavl" + "github.com/golang/snappy" +) + +const ( + ZlibFileSuffix = ".zz" + SnappyFileSuffix = ".snappy" +) + +// WriteChangeSet writes a version of change sets to writer. +// +// Change set file format: +// ``` +// version: int64 +// size: int64 // size of whole payload +// payload: +// +// delete: int8 +// keyLen: varint-uint64 +// key +// [ // if delete is false +// valueLen: varint-uint64 +// value +// ] +// repeat with next key-value pair +// +// repeat with next version +// ``` +func WriteChangeSet(writer io.Writer, version int64, cs *iavl.ChangeSet) error { + var size int + items := make([][]byte, 0, len(cs.Pairs)) + for _, pair := range cs.Pairs { + buf, err := encodeKVPair(pair) + if err != nil { + return err + } + size += len(buf) + items = append(items, buf) + } + + var versionHeader [16]byte + binary.LittleEndian.PutUint64(versionHeader[:], uint64(version)) + binary.LittleEndian.PutUint64(versionHeader[8:], uint64(size)) + + if _, err := writer.Write(versionHeader[:]); err != nil { + return err + } + for _, item := range items { + if _, err := writer.Write(item); err != nil { + return err + } + } + return nil +} + +// ReadChangeSet decode a version of change set from reader. +// if parseChangeset is false, it'll skip change set payload directly. +// +// returns (version, number of bytes read, changeSet, err) +func ReadChangeSet(reader Reader, parseChangeset bool) (int64, int64, *iavl.ChangeSet, error) { + var versionHeader [16]byte + if _, err := io.ReadFull(reader, versionHeader[:]); err != nil { + return 0, 0, nil, err + } + version := binary.LittleEndian.Uint64(versionHeader[:8]) + size := int64(binary.LittleEndian.Uint64(versionHeader[8:16])) + var changeSet iavl.ChangeSet + + if size == 0 { + return int64(version), 16, &changeSet, nil + } + + if parseChangeset { + var offset int64 + for offset < size { + pair, err := readKVPair(reader) + if err != nil { + return 0, 0, nil, err + } + offset += int64(encodedSizeOfKVPair(pair)) + changeSet.Pairs = append(changeSet.Pairs, pair) + } + if offset != size { + return 0, 0, nil, fmt.Errorf("read beyond payload size limit, size: %d, offset: %d", size, offset) + } + } else { + if _, err := io.CopyN(io.Discard, reader, size); err != nil { + return 0, 0, nil, err + } + } + return int64(version), size + 16, &changeSet, nil +} + +// encodedSizeOfKVPair returns the encoded length of a key-value pair +// +// layout: deletion(1) + keyLen(varint) + key + [ valueLen(varint) + value ] +func encodedSizeOfKVPair(pair *iavl.KVPair) int { + keyLen := len(pair.Key) + size := 1 + uvarintSize(uint64(keyLen)) + keyLen + if pair.Delete { + return size + } + + valueLen := len(pair.Value) + return size + uvarintSize(uint64(valueLen)) + valueLen +} + +// encodeKVPair encode a key-value pair in change set. +// see godoc of `encodedSizeOfKVPair` for layout description, +// returns error if key/value length overflows. +func encodeKVPair(pair *iavl.KVPair) ([]byte, error) { + buf := make([]byte, encodedSizeOfKVPair(pair)) + + offset := 1 + keyLen := len(pair.Key) + offset += binary.PutUvarint(buf[offset:], uint64(keyLen)) + + copy(buf[offset:], pair.Key) + if pair.Delete { + buf[0] = 1 + return buf, nil + } + + offset += keyLen + offset += binary.PutUvarint(buf[offset:], uint64(len(pair.Value))) + copy(buf[offset:], pair.Value) + return buf, nil +} + +// Reader combines `io.Reader` and `io.ByteReader`. +type Reader interface { + io.Reader + io.ByteReader +} + +// ReadCloser combines `Reader` and `io.Closer`. +type ReadCloser interface { + Reader + io.Closer +} + +// readKVPair decode a key-value pair from reader +// +// see godoc of `encodedSizeOfKVPair` for layout description +func readKVPair(reader Reader) (*iavl.KVPair, error) { + deletion, err := reader.ReadByte() + if err != nil { + return nil, err + } + + keyLen, err := binary.ReadUvarint(reader) + if err != nil { + return nil, err + } + + pair := iavl.KVPair{ + Delete: deletion == 1, + Key: make([]byte, keyLen), + } + if _, err := io.ReadFull(reader, pair.Key); err != nil { + return nil, err + } + + if pair.Delete { + return &pair, nil + } + + valueLen, err := binary.ReadUvarint(reader) + if err != nil { + return nil, err + } + pair.Value = make([]byte, valueLen) + if _, err := io.ReadFull(reader, pair.Value); err != nil { + return nil, err + } + + return &pair, nil +} + +// openChangeSetFile opens change set file, +// it handles compressed files automatically, +// also supports special name "-" to specify stdin. +func openChangeSetFile(fileName string) (ReadCloser, error) { + if fileName == "-" { + return WrapReader(bufio.NewReader(os.Stdin), nil), nil + } + + var reader Reader + fp, err := os.Open(fileName) + if err != nil { + return nil, err + } + switch { + case strings.HasSuffix(fileName, ZlibFileSuffix): + zreader, err := zlib.NewReader(fp) + if err != nil { + _ = fp.Close() + return nil, err + } + reader = bufio.NewReader(zreader) + case strings.HasSuffix(fileName, SnappyFileSuffix): + reader = snappy.NewReader(fp) + default: + reader = bufio.NewReader(fp) + } + return WrapReader(reader, fp), nil +} + +// withChangeSetFile opens change set file and pass the reader to callback, +// it closes the file immediately after callback returns. +func withChangeSetFile(fileName string, fn func(Reader) error) error { + reader, err := openChangeSetFile(fileName) + if err != nil { + return err + } + defer reader.Close() + return fn(reader) +} + +// IterateChangeSets iterate the change set files, +func IterateChangeSets( + reader Reader, + fn func(version int64, changeSet *iavl.ChangeSet) (bool, error), +) (int64, error) { + var ( + cont = true + lastCompleteOffset int64 + version, offset int64 + changeSet *iavl.ChangeSet + err error + ) + + for cont { + version, offset, changeSet, err = ReadChangeSet(reader, true) + if err != nil { + break + } + cont, err = fn(version, changeSet) + if err != nil { + break + } + + lastCompleteOffset += offset + } + + if err == io.EOF { + // it's not easy to distinguish normal EOF or unexpected EOF, + // there could be potential corrupted end of file and the err is a normal io.EOF here, + // user should verify the change set files in advance, using the verify command. + err = nil + } + + return lastCompleteOffset, err +} + +// IterateVersions iterate the version numbers in change set files, skipping the change set payloads. +func IterateVersions( + reader Reader, + fn func(version int64) (bool, error), +) (int64, error) { + var ( + cont = true + lastCompleteOffset int64 + version, offset int64 + err error + ) + + for cont { + version, offset, _, err = ReadChangeSet(reader, false) + if err != nil { + break + } + cont, err = fn(version) + if err != nil { + break + } + + lastCompleteOffset += offset + } + + if err == io.EOF { + // it's not easy to distinguish normal EOF or unexpected EOF, + // there could be potential corrupted end of file and the err is a normal io.EOF here, + // user should verify the change set files in advance, using the verify command. + err = nil + } + + return lastCompleteOffset, err +} + +type FileWithVersion struct { + FileName string + Version uint64 +} + +// SortFilesByFirstVerson parse the first version of the change set files and associate with the file name, +// then sort them by version number, also filter out empty files. +func SortFilesByFirstVerson(files []string) ([]FileWithVersion, error) { + nonEmptyFiles := make([]FileWithVersion, 0, len(files)) + + for _, fileName := range files { + version, err := ReadFirstVersion(fileName) + if err != nil { + if err == io.EOF { + // skipping empty files + continue + } + return nil, err + } + + nonEmptyFiles = append(nonEmptyFiles, FileWithVersion{ + FileName: fileName, + Version: version, + }) + } + + sort.Slice(nonEmptyFiles, func(i, j int) bool { + return nonEmptyFiles[i].Version < nonEmptyFiles[j].Version + }) + return nonEmptyFiles, nil +} + +// ReadFirstVersion parse the first version number in the change set file +func ReadFirstVersion(fileName string) (uint64, error) { + fp, err := openChangeSetFile(fileName) + if err != nil { + return 0, err + } + defer fp.Close() + + // parse the first version number + var versionBuf [8]byte + _, err = io.ReadFull(fp, versionBuf[:]) + if err != nil { + return 0, err + } + + return binary.LittleEndian.Uint64(versionBuf[:]), nil +} + +// uvarintSize returns the size (in bytes) of uint64 encoded with the `binary.PutUvarint`. +func uvarintSize(num uint64) int { + bits := bits.Len64(num) + q, r := bits/7, bits%7 + size := q + if r > 0 || size == 0 { + size++ + } + return size +} diff --git a/versiondb/client/changeset_test.go b/versiondb/client/changeset_test.go new file mode 100644 index 0000000..8ff210f --- /dev/null +++ b/versiondb/client/changeset_test.go @@ -0,0 +1,187 @@ +package client + +import ( + "bytes" + "encoding/binary" + "fmt" + "math" + "math/rand" + "testing" + + "github.com/cosmos/iavl" + "github.com/stretchr/testify/require" +) + +var ChangeSets []*iavl.ChangeSet + +func init() { + ChangeSets = append(ChangeSets, + &iavl.ChangeSet{Pairs: []*iavl.KVPair{ + {Key: []byte("hello"), Value: []byte("world")}, + }}, + &iavl.ChangeSet{Pairs: []*iavl.KVPair{ + {Key: []byte("hello"), Value: []byte("world1")}, + {Key: []byte("hello1"), Value: []byte("world1")}, + }}, + &iavl.ChangeSet{Pairs: []*iavl.KVPair{ + {Key: []byte("hello2"), Value: []byte("world1")}, + {Key: []byte("hello3"), Value: []byte("world1")}, + }}, + ) + + var changeSet iavl.ChangeSet + for i := 0; i < 1; i++ { + changeSet.Pairs = append(changeSet.Pairs, &iavl.KVPair{Key: []byte(fmt.Sprintf("hello%02d", i)), Value: []byte("world1")}) + } + + ChangeSets = append(ChangeSets, &changeSet) + ChangeSets = append(ChangeSets, &iavl.ChangeSet{Pairs: []*iavl.KVPair{ + {Key: []byte("hello"), Delete: true}, + {Key: []byte("hello19"), Delete: true}, + }}) + + changeSet = iavl.ChangeSet{} + for i := 0; i < 21; i++ { + changeSet.Pairs = append(changeSet.Pairs, &iavl.KVPair{Key: []byte(fmt.Sprintf("aello%02d", i)), Value: []byte("world1")}) + } + ChangeSets = append(ChangeSets, &changeSet) + + changeSet = iavl.ChangeSet{} + for i := 0; i < 21; i++ { + changeSet.Pairs = append(changeSet.Pairs, &iavl.KVPair{Key: []byte(fmt.Sprintf("aello%02d", i)), Delete: true}) + } + for i := 0; i < 19; i++ { + changeSet.Pairs = append(changeSet.Pairs, &iavl.KVPair{Key: []byte(fmt.Sprintf("hello%02d", i)), Delete: true}) + } + ChangeSets = append(ChangeSets, &changeSet) +} + +func TestChangeSetEncoding(t *testing.T) { + var buf bytes.Buffer + for i, changeSet := range ChangeSets { + version := int64(i + 1) + require.NoError(t, WriteChangeSet(&buf, version, changeSet)) + } + + bufLen := buf.Len() + offset, err := IterateChangeSets(&buf, func(version int64, changeSet *iavl.ChangeSet) (bool, error) { + require.Equal(t, ChangeSets[version-1], changeSet) + return true, nil + }) + require.Equal(t, bufLen, int(offset)) + require.NoError(t, err) +} + +func TestUvarintSize(t *testing.T) { + var buf [binary.MaxVarintLen64]byte + r := rand.New(rand.NewSource(0)) + for i := 0; i < 100; i++ { + v := r.Uint64() + n := binary.PutUvarint(buf[:], v) + require.Equal(t, n, uvarintSize(v)) + } + + // test edge cases + edgeCases := []uint64{0, math.MaxUint64} + for _, v := range edgeCases { + n := binary.PutUvarint(buf[:], v) + require.Equal(t, n, uvarintSize(v)) + } +} + +func TestKVPairRoundTrip(t *testing.T) { + testCases := []struct { + name string + pair *iavl.KVPair + pass bool + }{ + { + "update", + &iavl.KVPair{Key: []byte("hello"), Value: []byte("world")}, + true, + }, + { + "delete", + &iavl.KVPair{Key: []byte("hello"), Delete: true}, + true, + }, + { + "empty value", + &iavl.KVPair{Key: []byte("hello"), Value: []byte{}}, + true, + }, + { + "uint16 key length don't overflow", + &iavl.KVPair{Key: make([]byte, math.MaxUint16+1), Value: []byte{}}, + true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + buf, err := encodeKVPair(tc.pair) + if tc.pass { + require.NoError(t, err) + require.Equal(t, encodedSizeOfKVPair(tc.pair), len(buf)) + + pair, err := readKVPair(bytes.NewBuffer(buf)) + require.NoError(t, err) + require.Equal(t, tc.pair, pair) + } else { + require.Error(t, err) + } + }) + } +} + +func TestChangeSetRoundTrip(t *testing.T) { + testCases := []struct { + name string + version int64 + changeSet *iavl.ChangeSet + pass bool + }{ + { + "normal", + 1, + &iavl.ChangeSet{ + Pairs: []*iavl.KVPair{ + {Key: []byte("hello"), Value: []byte("world")}, + {Key: []byte("hello"), Value: []byte{}}, + {Key: []byte("hello"), Value: []byte{}}, + }, + }, + true, + }, + { + "uint16 key length don't overflow", + 1, + &iavl.ChangeSet{ + Pairs: []*iavl.KVPair{ + {Key: []byte("hello"), Value: []byte{}}, + {Key: []byte("hello"), Value: []byte{}}, + {Key: make([]byte, math.MaxUint16+1), Value: []byte{}}, + }, + }, + true, + }, + } + // TODO test key/value length overflow need to allocate + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + buf := bytes.NewBuffer([]byte{}) + err := WriteChangeSet(buf, tc.version, tc.changeSet) + encoded := buf.Bytes() + if tc.pass { + require.NoError(t, err) + version, offset, changeSet, err := ReadChangeSet(bytes.NewReader(encoded), true) + require.NoError(t, err) + require.Equal(t, tc.version, version) + require.Equal(t, tc.changeSet, changeSet) + require.Equal(t, len(encoded), int(offset)) + } else { + require.Error(t, err) + } + }) + } +} diff --git a/versiondb/client/cmd.go b/versiondb/client/cmd.go new file mode 100644 index 0000000..48165c4 --- /dev/null +++ b/versiondb/client/cmd.go @@ -0,0 +1,33 @@ +package client + +import ( + dbm "github.com/cosmos/cosmos-db" + "github.com/linxGnu/grocksdb" + "github.com/spf13/cobra" +) + +// Options defines the customizable settings of ChangeSetGroupCmd +type Options struct { + DefaultStores []string + OpenReadOnlyDB func(home string, backend dbm.BackendType) (dbm.DB, error) + AppRocksDBOptions func(sstFileWriter bool) *grocksdb.Options +} + +func ChangeSetGroupCmd(opts Options) *cobra.Command { + cmd := &cobra.Command{ + Use: "changeset", + Short: "dump and manage change sets files and ingest into versiondb", + } + cmd.AddCommand( + ListDefaultStoresCmd(opts.DefaultStores), + DumpChangeSetCmd(opts), + PrintChangeSetCmd(), + VerifyChangeSetCmd(opts.DefaultStores), + BuildVersionDBSSTCmd(opts.DefaultStores), + IngestVersionDBSSTCmd(), + ChangeSetToVersionDBCmd(), + RestoreAppDBCmd(opts), + RestoreVersionDBCmd(), + ) + return cmd +} diff --git a/versiondb/client/convert_to_sst.go b/versiondb/client/convert_to_sst.go new file mode 100644 index 0000000..9559fdf --- /dev/null +++ b/versiondb/client/convert_to_sst.go @@ -0,0 +1,291 @@ +package client + +import ( + "bytes" + "context" + "encoding/binary" + "fmt" + "os" + "path/filepath" + "runtime" + + "github.com/alitto/pond" + "github.com/cosmos/iavl" + "github.com/linxGnu/grocksdb" + "github.com/spf13/cobra" + + "github.com/crypto-org-chain/cronos/versiondb/extsort" + "github.com/crypto-org-chain/cronos/versiondb/tsrocksdb" +) + +const ( + SSTFileExtension = ".sst" + DefaultSSTFileSize = 128 * 1024 * 1024 + DefaultSorterChunkSize = 256 * 1024 * 1024 + + // SizeKeyLength is the number of bytes used to encode key length in sort payload + SizeKeyLength = 4 +) + +func BuildVersionDBSSTCmd(defaultStores []string) *cobra.Command { + cmd := &cobra.Command{ + Use: "build-versiondb-sst changeSetDir sstDir", + Short: "Build versiondb rocksdb sst files from changesets, different stores can run in parallel, the sst files are used to rebuild versiondb later", + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + sstFileSize, err := cmd.Flags().GetUint64(flagSSTFileSize) + if err != nil { + return err + } + + sorterChunkSize, err := cmd.Flags().GetInt64(flagSorterChunkSize) + if err != nil { + return err + } + concurrency, err := cmd.Flags().GetInt(flagConcurrency) + if err != nil { + return err + } + stores, err := GetStoresOrDefault(cmd, defaultStores) + if err != nil { + return err + } + + changeSetDir := args[0] + sstDir := args[1] + + if err := os.MkdirAll(sstDir, os.ModePerm); err != nil { + return err + } + + // create fixed size task pool with big enough buffer. + pool := pond.New(concurrency, 0) + defer pool.StopAndWait() + + group, _ := pool.GroupContext(context.Background()) + for _, store := range stores { + // https://github.com/golang/go/wiki/CommonMistakes#using-goroutines-on-loop-iterator-variables + store := store + group.Submit(func() error { + return convertSingleStore(store, changeSetDir, sstDir, sstFileSize, sorterChunkSize) + }) + } + + return group.Wait() + }, + } + + cmd.Flags().Uint64(flagSSTFileSize, DefaultSSTFileSize, "the target sst file size, note the actual file size may be larger because sst files must be split on different key names") + cmd.Flags().String(flagStores, "", "list of store names, default to the current store list in application") + cmd.Flags().Int64(flagSorterChunkSize, DefaultSorterChunkSize, "uncompressed chunk size for external sorter, it decides the peak ram usage, on disk it'll be snappy compressed") + cmd.Flags().Int(flagConcurrency, runtime.NumCPU(), "Number concurrent goroutines to parallelize the work") + + return cmd +} + +// convertSingleStore handles a single store, can run in parallel with other stores, +// it starts extra goroutines for parallel pipeline. +func convertSingleStore(store string, changeSetDir, sstDir string, sstFileSize uint64, sorterChunkSize int64) error { + csFiles, err := scanChangeSetFiles(changeSetDir, store) + if err != nil { + return err + } + if len(csFiles) == 0 { + return nil + } + + prefix := []byte(fmt.Sprintf(tsrocksdb.StorePrefixTpl, store)) + isEmpty := true + + inputChan, outputChan := extsort.Spawn(sstDir, extsort.Options{ + MaxChunkSize: sorterChunkSize, + LesserFunc: compareSorterItem, + DeltaEncoding: true, + SnappyCompression: true, + }, PipelineBufferSize) + + for _, file := range csFiles { + if err = withChangeSetFile(file.FileName, func(reader Reader) error { + _, err := IterateChangeSets(reader, func(version int64, changeSet *iavl.ChangeSet) (bool, error) { + for _, pair := range changeSet.Pairs { + inputChan <- encodeSorterItem(uint64(version), pair) + isEmpty = false + } + return true, nil + }) + + return err + }); err != nil { + break + } + } + close(inputChan) + if err != nil { + return err + } + + if isEmpty { + // SSTFileWriter don't support writing empty files, so we stop early here. + return nil + } + + sstWriter := newSSTFileWriter() + defer sstWriter.Destroy() + + sstSeq := 0 + openNextFile := func() error { + if err := sstWriter.Open(filepath.Join(sstDir, sstFileName(store, sstSeq))); err != nil { + return err + } + sstSeq++ + return nil + } + + if err := openNextFile(); err != nil { + return err + } + + var lastKey []byte + for item := range outputChan { + ts, pair := decodeSorterItem(item) + + // Only breakup sst file when the user-key(without timestamp) is different, + // because the rocksdb ingestion logic checks for overlap in keys without the timestamp part currently. + if sstWriter.FileSize() >= sstFileSize && !bytes.Equal(lastKey, pair.Key) { + if err := sstWriter.Finish(); err != nil { + return err + } + if err := openNextFile(); err != nil { + return err + } + } + + key := cloneAppend(prefix, pair.Key) + if pair.Delete { + err = sstWriter.DeleteWithTS(key, ts) + } else { + err = sstWriter.PutWithTS(key, ts, pair.Value) + } + if err != nil { + return err + } + + lastKey = pair.Key + } + return sstWriter.Finish() +} + +// scanChangeSetFiles find change set files from the directory and sort them by the first version included, filter out +// empty files. +func scanChangeSetFiles(changeSetDir, store string) ([]FileWithVersion, error) { + // scan directory to find the change set files + storeDir := filepath.Join(changeSetDir, store) + entries, err := os.ReadDir(storeDir) + if err != nil { + // assume the change set files are taken from older versions, don't include all stores. + if os.IsNotExist(err) { + return nil, nil + } + return nil, err + } + fileNames := make([]string, len(entries)) + for i, entry := range entries { + fileNames[i] = filepath.Join(storeDir, entry.Name()) + } + return SortFilesByFirstVerson(fileNames) +} + +// sstFileName inserts the seq integer into the base file name +func sstFileName(store string, seq int) string { + return fmt.Sprintf("%s-%d%s", store, seq, SSTFileExtension) +} + +func newSSTFileWriter() *grocksdb.SSTFileWriter { + envOpts := grocksdb.NewDefaultEnvOptions() + return grocksdb.NewSSTFileWriter(envOpts, tsrocksdb.NewVersionDBOpts(true)) +} + +// encodeSorterItem encode kv-pair for use in external sorter. +// +// layout: key + version(8) + delete(1) + [ value ] + key length(SizeKeyLength) +// we put the key and version in the front of payload so it can take advantage of the delta encoding in the `ExtSorter`. +func encodeSorterItem(version uint64, pair *iavl.KVPair) []byte { + item := make([]byte, sizeOfSorterItem(pair)) + copy(item, pair.Key) + offset := len(pair.Key) + + binary.LittleEndian.PutUint64(item[offset:], version) + offset += tsrocksdb.TimestampSize + + if pair.Delete { + item[offset] = 1 + offset++ + } else { + copy(item[offset+1:], pair.Value) + offset += len(pair.Value) + 1 + } + binary.LittleEndian.PutUint32(item[offset:], uint32(len(pair.Key))) + return item +} + +// sizeOfSorterItem compute the encoded size of pair +// +// see godoc of `encodeSorterItem` for layout +func sizeOfSorterItem(pair *iavl.KVPair) int { + size := len(pair.Key) + tsrocksdb.TimestampSize + 1 + SizeKeyLength + if !pair.Delete { + size += len(pair.Value) + } + return size +} + +// decodeSorterItem decode the kv-pair from external sorter. +// +// see godoc of `encodeSorterItem` for layout +func decodeSorterItem(item []byte) ([]byte, iavl.KVPair) { + var value []byte + keyLen := binary.LittleEndian.Uint32(item[len(item)-SizeKeyLength:]) + key := item[:keyLen] + + offset := keyLen + version := item[offset : offset+tsrocksdb.TimestampSize] + + offset += tsrocksdb.TimestampSize + delete := item[offset] == 1 + + if !delete { + offset++ + value = item[offset : len(item)-SizeKeyLength] + } + + return version, iavl.KVPair{ + Delete: delete, + Key: key, + Value: value, + } +} + +// compareSorterItem compare encoded kv-pairs return if a < b. +func compareSorterItem(a, b []byte) bool { + // decode key and version + aKeyLen := binary.LittleEndian.Uint32(a[len(a)-SizeKeyLength:]) + bKeyLen := binary.LittleEndian.Uint32(b[len(b)-SizeKeyLength:]) + ret := bytes.Compare(a[:aKeyLen], b[:bKeyLen]) + if ret != 0 { + return ret == -1 + } + + aVersion := binary.LittleEndian.Uint64(a[aKeyLen:]) + bVersion := binary.LittleEndian.Uint64(b[bKeyLen:]) + // Compare version. + // For the same user key with different timestamps, larger (newer) timestamp + // comes first. + return aVersion > bVersion +} + +func cloneAppend(bz []byte, tail []byte) (res []byte) { + res = make([]byte, len(bz)+len(tail)) + copy(res, bz) + copy(res[len(bz):], tail) + return +} diff --git a/versiondb/client/convert_to_sst_test.go b/versiondb/client/convert_to_sst_test.go new file mode 100644 index 0000000..4a23467 --- /dev/null +++ b/versiondb/client/convert_to_sst_test.go @@ -0,0 +1,56 @@ +package client + +import ( + "encoding/binary" + "testing" + + "github.com/cosmos/iavl" + "github.com/stretchr/testify/require" +) + +func TestSorterItemEncoding(t *testing.T) { + testCases := []struct { + name string + version uint64 + pair *iavl.KVPair + }{ + {"default", 1, &iavl.KVPair{Delete: false, Key: []byte{1}, Value: []byte{1}}}, + {"delete", 1, &iavl.KVPair{Delete: true, Key: []byte{1}}}, + {"empty value", 1, &iavl.KVPair{Delete: false, Key: []byte{1}, Value: []byte{}}}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + buf := encodeSorterItem(tc.version, tc.pair) + ts, p := decodeSorterItem(buf) + v := binary.LittleEndian.Uint64(ts) + require.Equal(t, tc.version, v) + require.Equal(t, tc.pair, &p) + }) + } +} + +func TestSorterItemCompare(t *testing.T) { + testCases := []struct { + name string + version1 uint64 + pair1 *iavl.KVPair + version2 uint64 + pair2 *iavl.KVPair + result bool + }{ + {"lesser", 1, &iavl.KVPair{Key: []byte{1}}, 2, &iavl.KVPair{Key: []byte{2}}, true}, + {"equal", 1, &iavl.KVPair{Key: []byte{1}}, 1, &iavl.KVPair{Key: []byte{1}}, false}, + {"greater", 1, &iavl.KVPair{Key: []byte{2}}, 2, &iavl.KVPair{Key: []byte{1}}, false}, + {"smaller version", 1, &iavl.KVPair{Key: []byte{1}}, 2, &iavl.KVPair{Key: []byte{1}}, false}, + {"bigger version", 2, &iavl.KVPair{Key: []byte{1}}, 1, &iavl.KVPair{Key: []byte{1}}, true}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + bz1 := encodeSorterItem(tc.version1, tc.pair1) + bz2 := encodeSorterItem(tc.version2, tc.pair2) + require.Equal(t, tc.result, compareSorterItem(bz1, bz2)) + }) + } +} diff --git a/versiondb/client/dump.go b/versiondb/client/dump.go new file mode 100644 index 0000000..8abb546 --- /dev/null +++ b/versiondb/client/dump.go @@ -0,0 +1,342 @@ +package client + +import ( + "bufio" + "compress/zlib" + "context" + "encoding/binary" + "fmt" + "io" + "math" + "os" + "path/filepath" + "runtime" + "sync" + "time" + + log "cosmossdk.io/log" + "github.com/alitto/pond" + dbm "github.com/cosmos/cosmos-db" + "github.com/cosmos/iavl" + "github.com/golang/snappy" + "github.com/spf13/cast" + "github.com/spf13/cobra" + + "cosmossdk.io/store/wrapper" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/server" + + "github.com/crypto-org-chain/cronos/versiondb/tsrocksdb" +) + +const DefaultChunkSize = 1000000 + +func DumpChangeSetCmd(opts Options) *cobra.Command { + cmd := &cobra.Command{ + Use: "dump outDir", + Short: "Extract changesets from iavl versions, and save to plain file format", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + ctx := server.GetServerContextFromCmd(cmd) + if err := ctx.Viper.BindPFlags(cmd.Flags()); err != nil { + return err + } + + db, err := opts.OpenReadOnlyDB(ctx.Viper.GetString(flags.FlagHome), server.GetAppDBBackend(ctx.Viper)) + if err != nil { + return err + } + + cacheSize := cast.ToInt(ctx.Viper.Get(server.FlagIAVLCacheSize)) + + startVersion, err := cmd.Flags().GetInt64(flagStartVersion) + if err != nil { + return err + } + endVersion, err := cmd.Flags().GetInt64(flagEndVersion) + if err != nil { + return err + } + concurrency, err := cmd.Flags().GetInt(flagConcurrency) + if err != nil { + return err + } + chunkSize, err := cmd.Flags().GetInt(flagChunkSize) + if err != nil { + return err + } + zlibLevel, err := cmd.Flags().GetInt(flagZlibLevel) + if err != nil { + return err + } + stores, err := GetStoresOrDefault(cmd, opts.DefaultStores) + if err != nil { + return err + } + outDir := args[0] + if err := os.MkdirAll(outDir, os.ModePerm); err != nil { + return err + } + iavlVersion, err := cmd.Flags().GetInt(flagIAVLVersion) + if err != nil { + return err + } + + if endVersion == 0 { + // use the latest version of the first store for all stores + prefix := []byte(fmt.Sprintf(tsrocksdb.StorePrefixTpl, stores[0])) + tree := iavl.NewMutableTree(wrapper.NewDBWrapper((dbm.NewPrefixDB(db, prefix))), 0, true, log.NewNopLogger()) + latestVersion, err := tree.LoadVersion(0) + if err != nil { + return err + } + endVersion = latestVersion + 1 + fmt.Println("end version not specified, default to latest version + 1,", endVersion) + } + + // create fixed size task pool with big enough buffer. + pool := pond.New(concurrency, 1024) + defer pool.StopAndWait() + + // we handle multiple stores sequentially, because different stores don't share much in db, handle concurrently reduces cache efficiency. + for _, store := range stores { + fmt.Println("begin store", store, time.Now().Format(time.RFC3339)) + + // find the first version in the db, reading raw db because no public api for it. + prefix := []byte(fmt.Sprintf(tsrocksdb.StorePrefixTpl, store)) + storeStartVersion, err := getFirstVersion(dbm.NewPrefixDB(db, prefix), iavlVersion) + if err != nil { + return err + } + if storeStartVersion == 0 { + // store not exists + fmt.Println("skip empty store") + continue + } + if startVersion > storeStartVersion { + storeStartVersion = startVersion + } + + // share the iavl tree between tasks to reuse the node cache + iavlTreePool := sync.Pool{ + New: func() any { + // use separate prefixdb and iavl tree in each task to maximize concurrency performance + return iavl.NewImmutableTree(wrapper.NewDBWrapper(dbm.NewPrefixDB(db, prefix)), cacheSize, true, log.NewNopLogger()) + }, + } + + // first split work load into chunks + var chunks []chunk + for i := storeStartVersion; i < endVersion; i += int64(chunkSize) { + end := i + int64(chunkSize) + if end > endVersion { + end = endVersion + } + + var taskFiles []string + group, _ := pool.GroupContext(context.Background()) + // then split each chunk according to number of workers, the results will be concatenated into a single chunk file + for _, workRange := range splitWorkLoad(concurrency, Range{Start: i, End: end}) { + // https://github.com/golang/go/wiki/CommonMistakes#using-goroutines-on-loop-iterator-variables + workRange := workRange + taskFile := filepath.Join(outDir, fmt.Sprintf("tmp-%s-%d.snappy", store, workRange.Start)) + group.Submit(func() error { + tree := iavlTreePool.Get().(*iavl.ImmutableTree) + defer iavlTreePool.Put(tree) + return dumpRangeBlocks(taskFile, tree, workRange) + }) + + taskFiles = append(taskFiles, taskFile) + } + + chunks = append(chunks, chunk{ + store: store, beginVersion: i, taskFiles: taskFiles, taskGroup: group, + }) + } + + // for each chunk, wait for related tasks to finish, and concatenate the result files in order + for _, chunk := range chunks { + if err := chunk.collect(outDir, zlibLevel); err != nil { + return err + } + } + } + + return nil + }, + } + cmd.Flags().Int64(flagStartVersion, 0, "The start version") + cmd.Flags().Int64(flagEndVersion, 0, "The end version, exclusive, default to latestVersion+1") + cmd.Flags().Int(flagConcurrency, runtime.NumCPU(), "Number concurrent goroutines to parallelize the work") + cmd.Flags().Int(server.FlagIAVLCacheSize, 781250, "size of the iavl tree cache") + cmd.Flags().Int(flagChunkSize, DefaultChunkSize, "size of the block chunk") + cmd.Flags().Int(flagZlibLevel, 6, "level of zlib compression, 0: plain data, 1: fast, 9: best, default: 6, if not 0 the output file name will have .zz extension") + cmd.Flags().String(flagStores, "", "list of store names, default to the current store list in application") + cmd.Flags().Int(flagIAVLVersion, IAVLV1, "IAVL version, 0: v0, 1: v1") + return cmd +} + +// Range represents a range `[start, end)` +type Range struct { + Start, End int64 +} + +func splitWorkLoad(workers int, full Range) []Range { + var chunks []Range + chunkSize := (full.End - full.Start + int64(workers) - 1) / int64(workers) + for i := full.Start; i < full.End; i += chunkSize { + end := i + chunkSize + if end > full.End { + end = full.End + } + chunks = append(chunks, Range{Start: i, End: end}) + } + return chunks +} + +func dumpRangeBlocks(outputFile string, tree *iavl.ImmutableTree, blockRange Range) (returnErr error) { + fp, err := createFile(outputFile) + if err != nil { + return err + } + defer func() { + if err := fp.Close(); returnErr == nil { + returnErr = err + } + }() + + writer := snappy.NewBufferedWriter(fp) + + // TraverseStateChanges becomes inclusive on end since iavl `v1.x.x`, while the blockRange is exclusive on end + if err := tree.TraverseStateChanges(blockRange.Start, blockRange.End-1, func(version int64, changeSet *iavl.ChangeSet) error { + return WriteChangeSet(writer, version, changeSet) + }); err != nil { + return err + } + + return writer.Flush() +} + +type chunk struct { + store string + beginVersion int64 + taskFiles []string + taskGroup *pond.TaskGroupWithContext +} + +// collect wait for the tasks to complete and concatenate the files into a single output file. +func (c *chunk) collect(outDir string, zlibLevel int) (returnErr error) { + storeDir := filepath.Join(outDir, c.store) + if err := os.MkdirAll(storeDir, os.ModePerm); err != nil { + return err + } + + output := filepath.Join(storeDir, fmt.Sprintf("block-%d", c.beginVersion)) + if zlibLevel > 0 { + output += ZlibFileSuffix + } + + if err := c.taskGroup.Wait(); err != nil { + return err + } + + fp, err := createFile(output) + if err != nil { + return err + } + defer func() { + if err := fp.Close(); returnErr == nil { + returnErr = err + } + }() + + bufWriter := bufio.NewWriter(fp) + writer := io.Writer(bufWriter) + + var zwriter *zlib.Writer + if zlibLevel > 0 { + var err error + zwriter, err = zlib.NewWriterLevel(bufWriter, zlibLevel) + if err != nil { + return err + } + + writer = zwriter + } + + for _, taskFile := range c.taskFiles { + if err := copyTmpFile(writer, taskFile); err != nil { + return err + } + if err := os.Remove(taskFile); err != nil { + return err + } + } + + if zwriter != nil { + if err := zwriter.Close(); err != nil { + return err + } + } + + return bufWriter.Flush() +} + +// copyTmpFile append the snappy compressed temporary file to writer +func copyTmpFile(writer io.Writer, tmpFile string) error { + fp, err := os.Open(tmpFile) + if err != nil { + return err + } + defer fp.Close() + + _, err = io.Copy(writer, snappy.NewReader(fp)) + return err +} + +func createFile(name string) (*os.File, error) { + return os.OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o600) +} + +func getFirstVersion(db dbm.DB, iavlVersion int) (int64, error) { + if iavlVersion == IAVLV0 { + itr, err := db.Iterator( + rootKeyFormat.Key(uint64(1)), + rootKeyFormat.Key(uint64(math.MaxInt64)), + ) + if err != nil { + return 0, err + } + defer itr.Close() + + var version int64 + for ; itr.Valid(); itr.Next() { + rootKeyFormat.Scan(itr.Key(), &version) + return version, nil + } + + return 0, itr.Error() + } + + itr, err := db.Iterator( + nodeKeyV1Format.KeyInt64(1), + nodeKeyV1Format.KeyInt64(math.MaxInt64), + ) + if err != nil { + return 0, err + } + defer itr.Close() + + for ; itr.Valid(); itr.Next() { + var nk []byte + nodeKeyV1Format.Scan(itr.Key(), &nk) + version := int64(binary.BigEndian.Uint64(nk)) + nonce := binary.BigEndian.Uint32(nk[8:]) + if nonce == 1 { + // root key is normal node key with nonce 1 + return version, nil + } + } + + return 0, itr.Error() +} diff --git a/versiondb/client/flags.go b/versiondb/client/flags.go new file mode 100644 index 0000000..49d2755 --- /dev/null +++ b/versiondb/client/flags.go @@ -0,0 +1,30 @@ +package client + +const ( + IAVLV0 = iota + IAVLV1 +) + +const ( + flagStartVersion = "start-version" + flagEndVersion = "end-version" + flagOutput = "output" + flagConcurrency = "concurrency" + flagCheck = "check" + flagSave = "save" + flagNoParseChangeset = "no-parse-changeset" + flagChunkSize = "chunk-size" + flagZlibLevel = "zlib-level" + flagSSTFileSize = "sst-file-size" + flagMoveFiles = "move-files" + flagStore = "store" + flagStores = "stores" + flagMaximumVersion = "maximum-version" + flagTargetVersion = "target-version" + flagSaveSnapshot = "save-snapshot" + flagLoadSnapshot = "load-snapshot" + flagSorterChunkSize = "sorter-chunk-size" + flagInitialVersion = "initial-version" + flagSDK64Compact = "sdk64-compact" + flagIAVLVersion = "iavl-version" +) diff --git a/versiondb/client/ingest_sst.go b/versiondb/client/ingest_sst.go new file mode 100644 index 0000000..13b136a --- /dev/null +++ b/versiondb/client/ingest_sst.go @@ -0,0 +1,60 @@ +package client + +import ( + "fmt" + + "github.com/linxGnu/grocksdb" + "github.com/spf13/cobra" + + "github.com/crypto-org-chain/cronos/versiondb/tsrocksdb" +) + +func IngestVersionDBSSTCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "ingest-versiondb-sst versiondb-path [file1.sst file2.sst ...]", + Short: "Ingest sst files into versiondb and update the latest version", + Args: cobra.MinimumNArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + dbPath := args[0] + moveFiles, err := cmd.Flags().GetBool(flagMoveFiles) + if err != nil { + return err + } + + db, cfHandle, err := tsrocksdb.OpenVersionDB(dbPath) + if err != nil { + return err + } + if len(args) > 1 { + ingestOpts := grocksdb.NewDefaultIngestExternalFileOptions() + ingestOpts.SetMoveFiles(moveFiles) + if err := db.IngestExternalFileCF(cfHandle, args[1:], ingestOpts); err != nil { + return err + } + } + + maxVersion, err := cmd.Flags().GetInt64(flagMaximumVersion) + if err != nil { + return err + } + if maxVersion > 0 { + // update latest version + store := tsrocksdb.NewStoreWithDB(db, cfHandle) + latestVersion, err := store.GetLatestVersion() + if err != nil { + return err + } + if maxVersion > latestVersion { + fmt.Println("update latest version to", maxVersion) + if err := store.SetLatestVersion(maxVersion); err != nil { + return err + } + } + } + return nil + }, + } + cmd.Flags().Bool(flagMoveFiles, false, "move sst files instead of copy them") + cmd.Flags().Int64(flagMaximumVersion, 0, "Specify the maximum version covered by the ingested files, if it's bigger than existing recorded latest version, will update it.") + return cmd +} diff --git a/versiondb/client/print.go b/versiondb/client/print.go new file mode 100644 index 0000000..00b48ce --- /dev/null +++ b/versiondb/client/print.go @@ -0,0 +1,73 @@ +package client + +import ( + "encoding/json" + "fmt" + + "github.com/cosmos/iavl" + "github.com/spf13/cobra" +) + +func PrintChangeSetCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "print [plain-file]", + Short: "Pretty-print the content of change set file", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + noParseChangeset, err := cmd.Flags().GetBool(flagNoParseChangeset) + if err != nil { + return err + } + + startVersion, err := cmd.Flags().GetInt64(flagStartVersion) + if err != nil { + return err + } + endVersion, err := cmd.Flags().GetInt64(flagEndVersion) + if err != nil { + return err + } + + return withChangeSetFile(args[0], func(reader Reader) error { + if noParseChangeset { + // print the version numbers only + _, err := IterateVersions(reader, func(version int64) (bool, error) { + if version < startVersion { + return true, nil + } + if endVersion > 0 && version >= endVersion { + return false, nil + } + + fmt.Printf("version: %d\n", version) + return true, nil + }) + return err + } + + _, err := IterateChangeSets(reader, func(version int64, changeSet *iavl.ChangeSet) (bool, error) { + if version < startVersion { + return true, nil + } + if endVersion > 0 && version >= endVersion { + return false, nil + } + fmt.Printf("version: %d\n", version) + for _, pair := range changeSet.Pairs { + js, err := json.Marshal(pair) + if err != nil { + return false, err + } + fmt.Println(string(js)) + } + return true, nil + }) + return err + }) + }, + } + cmd.Flags().Bool(flagNoParseChangeset, false, "if parse and output the change set content, otherwise only version numbers are outputted") + cmd.Flags().Int64(flagStartVersion, 0, "Start of the version range to print") + cmd.Flags().Int64(flagEndVersion, 0, "End(exclusive) of the version range to print, 0 means no end") + return cmd +} diff --git a/versiondb/client/restore.go b/versiondb/client/restore.go new file mode 100644 index 0000000..1572d49 --- /dev/null +++ b/versiondb/client/restore.go @@ -0,0 +1,121 @@ +package client + +import ( + "fmt" + "io" + "math" + "path/filepath" + "strconv" + + "cosmossdk.io/errors" + protoio "github.com/cosmos/gogoproto/io" + "github.com/spf13/cobra" + + "cosmossdk.io/store/snapshots" + "cosmossdk.io/store/snapshots/types" + "github.com/cosmos/cosmos-sdk/server" + + "github.com/crypto-org-chain/cronos/versiondb" + "github.com/crypto-org-chain/cronos/versiondb/tsrocksdb" +) + +// RestoreVersionDBCmd returns a command to restore a versiondb from local snapshot +func RestoreVersionDBCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "restore-versiondb ", + Short: "Restore initial versiondb from local snapshot", + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + ctx := server.GetServerContextFromCmd(cmd) + + height, err := strconv.ParseUint(args[0], 10, 63) + if err != nil { + return err + } + format, err := strconv.ParseUint(args[1], 10, 32) + if err != nil { + return err + } + + store, err := server.GetSnapshotStore(ctx.Viper) + if err != nil { + return err + } + + snapshot, chChunks, err := store.Load(height, uint32(format)) + if err != nil { + return err + } + + if snapshot == nil { + return fmt.Errorf("snapshot doesn't exist, height: %d, format: %d", height, format) + } + + streamReader, err := snapshots.NewStreamReader(chChunks) + if err != nil { + return err + } + defer streamReader.Close() + + home := ctx.Config.RootDir + versionDB, err := tsrocksdb.NewStore(filepath.Join(home, "data", "versiondb")) + if err != nil { + return err + } + + ch := make(chan versiondb.ImportEntry, 128) + + go func() { + defer close(ch) + + if err := readSnapshotEntries(streamReader, ch); err != nil { + ctx.Logger.Error("failed to read snapshot entries", "err", err) + } + }() + + return versionDB.Import(int64(height), ch) + }, + } + return cmd +} + +// readSnapshotEntries reads key-value entries from protobuf reader and feed to the channel +func readSnapshotEntries(protoReader protoio.Reader, ch chan<- versiondb.ImportEntry) error { + var ( + snapshotItem types.SnapshotItem + storeKey string + ) + +loop: + for { + snapshotItem = types.SnapshotItem{} + err := protoReader.ReadMsg(&snapshotItem) + if err == io.EOF { + break + } else if err != nil { + return errors.Wrap(err, "invalid protobuf message") + } + + switch item := snapshotItem.Item.(type) { + case *types.SnapshotItem_Store: + storeKey = item.Store.Name + case *types.SnapshotItem_IAVL: + if storeKey == "" { + return errors.Wrap(err, "invalid protobuf message, store name is empty") + } + if item.IAVL.Height > math.MaxInt8 { + return fmt.Errorf("node height %v cannot exceed %v", + item.IAVL.Height, math.MaxInt8) + } + ch <- versiondb.ImportEntry{ + StoreKey: storeKey, + Key: item.IAVL.Key, + Value: item.IAVL.Value, + } + default: + break loop + } + } + + return nil +} diff --git a/versiondb/client/restore_app_db.go b/versiondb/client/restore_app_db.go new file mode 100644 index 0000000..78f7115 --- /dev/null +++ b/versiondb/client/restore_app_db.go @@ -0,0 +1,334 @@ +package client + +import ( + "bytes" + "context" + "encoding/binary" + "fmt" + "io" + "os" + "path/filepath" + "runtime" + "strings" + + "cosmossdk.io/errors" + "github.com/alitto/pond" + gogotypes "github.com/cosmos/gogoproto/types" + "github.com/cosmos/iavl/keyformat" + "github.com/linxGnu/grocksdb" + "github.com/spf13/cobra" + + storetypes "cosmossdk.io/store/types" + capabilitytypes "github.com/cosmos/ibc-go/modules/capability/types" + + "github.com/crypto-org-chain/cronos/memiavl" + "github.com/crypto-org-chain/cronos/versiondb/extsort" +) + +const ( + int64Size = 8 + int32Size = 4 + + storeKeyPrefix = "s/k:%s/" + latestVersionKey = "s/latest" + commitInfoKeyFmt = "s/%d" // s/ + + // We creates the temporary sst files in the target database to make sure the file renaming is cheap in ingestion + // part. + StoreSSTFileName = "tmp-%s-%d.sst" + + PipelineBufferSize = 1024 + DefaultSorterChunkSizeIAVL = 64 * 1024 * 1024 +) + +var ( + nodeKeyFormat = keyformat.NewKeyFormat('n', memiavl.SizeHash) // n + rootKeyFormat = keyformat.NewKeyFormat('r', int64Size) // r + nodeKeyV1Format = keyformat.NewFastPrefixFormatter('s', int64Size+int32Size) // s +) + +func RestoreAppDBCmd(opts Options) *cobra.Command { + cmd := &cobra.Command{ + Use: "restore-app-db snapshot-dir application.db", + Short: "Restore `application.db` from memiavl snapshots", + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + sstFileSizeTarget, err := cmd.Flags().GetUint64(flagSSTFileSize) + if err != nil { + return err + } + sorterChunkSize, err := cmd.Flags().GetUint64(flagSorterChunkSize) + if err != nil { + return err + } + concurrency, err := cmd.Flags().GetInt(flagConcurrency) + if err != nil { + return err + } + sdk64Compact, err := cmd.Flags().GetBool(flagSDK64Compact) + if err != nil { + return err + } + stores, err := GetStoresOrDefault(cmd, opts.DefaultStores) + if err != nil { + return err + } + + snapshotDir := args[0] + iavlDir := args[1] + if err := os.MkdirAll(iavlDir, os.ModePerm); err != nil { + return err + } + + // load the snapshots and compute commit info first + var lastestVersion int64 + var storeInfos []storetypes.StoreInfo + if sdk64Compact { + // https://github.com/cosmos/cosmos-sdk/issues/14916 + storeInfos = append(storeInfos, storetypes.StoreInfo{Name: capabilitytypes.MemStoreKey, CommitId: storetypes.CommitID{}}) + } + snapshots := make([]*memiavl.Snapshot, len(stores)) + for i, store := range stores { + path := filepath.Join(snapshotDir, store) + snapshot, err := memiavl.OpenSnapshot(path) + if err != nil { + return errors.Wrapf(err, "open snapshot fail: %s", path) + } + snapshots[i] = snapshot + + tree := memiavl.NewFromSnapshot(snapshot, true, 0) + commitID := lastCommitID(tree) + storeInfos = append(storeInfos, storetypes.StoreInfo{ + Name: store, + CommitId: commitID, + }) + + if commitID.Version > lastestVersion { + lastestVersion = commitID.Version + } + } + commitInfo := buildCommitInfo(storeInfos, lastestVersion) + + // create fixed size task pool with big enough buffer. + pool := pond.New(concurrency, 0) + defer pool.StopAndWait() + + group, _ := pool.GroupContext(context.Background()) + for i := 0; i < len(stores); i++ { + // https://github.com/golang/go/wiki/CommonMistakes#using-goroutines-on-loop-iterator-variables + store := stores[i] + snapshot := snapshots[i] + group.Submit(func() error { + defer snapshot.Close() + + sstWriter := newIAVLSSTFileWriter(opts.AppRocksDBOptions) + defer sstWriter.Destroy() + + return oneStore(sstWriter, store, snapshot, iavlDir, sstFileSizeTarget, sorterChunkSize) + }) + } + + if err := group.Wait(); err != nil { + return errors.Wrap(err, "worker pool wait fail") + } + + // collect the sst files + entries, err := os.ReadDir(iavlDir) + if err != nil { + return errors.Wrapf(err, "read directory fail: %s", iavlDir) + } + sstFiles := make([]string, 0, len(entries)) + for _, entry := range entries { + name := entry.Name() + if strings.HasPrefix(name, "tmp-") { + sstFiles = append(sstFiles, filepath.Join(iavlDir, name)) + } + } + + // sst files ingestion + ingestOpts := grocksdb.NewDefaultIngestExternalFileOptions() + defer ingestOpts.Destroy() + ingestOpts.SetMoveFiles(true) + + db, err := grocksdb.OpenDb(opts.AppRocksDBOptions(false), iavlDir) + if err != nil { + return errors.Wrap(err, "open iavl db fail") + } + defer db.Close() + + if err := db.IngestExternalFile(sstFiles, ingestOpts); err != nil { + return errors.Wrap(err, "ingset sst files fail") + } + + // write the metadata part separately, because it overlaps with the other sst files + if err := writeMetadata(db, &commitInfo); err != nil { + return errors.Wrap(err, "write metadata fail") + } + + fmt.Printf("version: %d, app hash: %X\n", commitInfo.Version, commitInfo.Hash()) + return nil + }, + } + + cmd.Flags().Uint64(flagSSTFileSize, DefaultSSTFileSize, "the target sst file size, note the actual file size may be larger because sst files must be split on different key names") + cmd.Flags().String(flagStores, "", "list of store names, default to the current store list in application") + cmd.Flags().Uint64(flagSorterChunkSize, DefaultSorterChunkSizeIAVL, "uncompressed chunk size for external sorter, it decides the peak ram usage, on disk it'll be snappy compressed") + cmd.Flags().Int(flagConcurrency, runtime.NumCPU(), "Number concurrent goroutines to parallelize the work") + cmd.Flags().Bool(flagSDK64Compact, false, "Should the app hash calculation be compatible with cosmos-sdk v0.46 and earlier") + + return cmd +} + +// oneStore process a single store, can run in parallel with other stores, +func oneStore(sstWriter *grocksdb.SSTFileWriter, store string, snapshot *memiavl.Snapshot, sstDir string, sstFileSizeTarget, sorterChunkSize uint64) error { + prefix := []byte(fmt.Sprintf(storeKeyPrefix, store)) + + inputChan, outputChan := extsort.Spawn(sstDir, extsort.Options{ + MaxChunkSize: int64(sorterChunkSize), + LesserFunc: compareSorterNode, + SnappyCompression: true, + }, PipelineBufferSize) + + err := snapshot.ScanNodes(func(node memiavl.PersistedNode) error { + bz, err := encodeSorterNode(node) + if err != nil { + return err + } + inputChan <- bz + return nil + }) + close(inputChan) + if err != nil { + return err + } + + sstSeq := 0 + openNextFile := func() error { + sstFileName := filepath.Join(sstDir, fmt.Sprintf(StoreSSTFileName, store, sstSeq)) + if err := sstWriter.Open(sstFileName); err != nil { + return errors.Wrapf(err, "open sst file fail: %s", sstFileName) + } + sstSeq++ + return nil + } + + if err := openNextFile(); err != nil { + return err + } + for item := range outputChan { + hash := item[:memiavl.SizeHash] + value := item[memiavl.SizeHash:] + key := cloneAppend(prefix, nodeKeyFormat.Key(hash)) + + if err := sstWriter.Put(key, value); err != nil { + return errors.Wrap(err, "sst write node fail") + } + + if sstWriter.FileSize() >= sstFileSizeTarget { + if err := sstWriter.Finish(); err != nil { + return errors.Wrap(err, "sst writer finish fail") + } + if err := openNextFile(); err != nil { + return err + } + } + } + + // root record, it use empty slice for root hash of empty tree + rootKey := cloneAppend(prefix, rootKeyFormat.Key(int64(snapshot.Version()))) + var rootHash []byte + if !snapshot.IsEmpty() { + rootHash = snapshot.RootNode().Hash() + } + if err := sstWriter.Put(rootKey, rootHash); err != nil { + return errors.Wrap(err, "sst write root fail") + } + + if err := sstWriter.Finish(); err != nil { + return errors.Wrap(err, "sst writer finish fail") + } + + return nil +} + +// writeMetadata writes the rootmulti commit info and latest version to the db +func writeMetadata(db *grocksdb.DB, cInfo *storetypes.CommitInfo) error { + writeOpts := grocksdb.NewDefaultWriteOptions() + + bz, err := cInfo.Marshal() + if err != nil { + return errors.Wrap(err, "marshal CommitInfo fail") + } + + cInfoKey := fmt.Sprintf(commitInfoKeyFmt, cInfo.Version) + if err := db.Put(writeOpts, []byte(cInfoKey), bz); err != nil { + return err + } + + bz, err = gogotypes.StdInt64Marshal(cInfo.Version) + if err != nil { + return err + } + + return db.Put(writeOpts, []byte(latestVersionKey), bz) +} + +func newIAVLSSTFileWriter(rocksdbOpts func(bool) *grocksdb.Options) *grocksdb.SSTFileWriter { + envOpts := grocksdb.NewDefaultEnvOptions() + return grocksdb.NewSSTFileWriter(envOpts, rocksdbOpts(true)) +} + +// encodeNode encodes the node in the same way as the existing iavl implementation. +func encodeNode(w io.Writer, node memiavl.PersistedNode) error { + var buf [binary.MaxVarintLen64]byte + + height := node.Height() + n := binary.PutVarint(buf[:], int64(height)) + if _, err := w.Write(buf[:n]); err != nil { + return err + } + n = binary.PutVarint(buf[:], node.Size()) + if _, err := w.Write(buf[:n]); err != nil { + return err + } + n = binary.PutVarint(buf[:], int64(node.Version())) + if _, err := w.Write(buf[:n]); err != nil { + return err + } + + // Unlike writeHashBytes, key is written for inner nodes. + if err := memiavl.EncodeBytes(w, node.Key()); err != nil { + return err + } + + if height == 0 { + if err := memiavl.EncodeBytes(w, node.Value()); err != nil { + return err + } + } else { + if err := memiavl.EncodeBytes(w, node.Left().Hash()); err != nil { + return err + } + if err := memiavl.EncodeBytes(w, node.Right().Hash()); err != nil { + return err + } + } + + return nil +} + +func encodeSorterNode(node memiavl.PersistedNode) ([]byte, error) { + var buf bytes.Buffer + if _, err := buf.Write(node.Hash()); err != nil { + return nil, err + } + if err := encodeNode(&buf, node); err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +// compareSorterNode compare the hash part +func compareSorterNode(a, b []byte) bool { + return bytes.Compare(a[:memiavl.SizeHash], b[:memiavl.SizeHash]) == -1 +} diff --git a/versiondb/client/stores.go b/versiondb/client/stores.go new file mode 100644 index 0000000..2578517 --- /dev/null +++ b/versiondb/client/stores.go @@ -0,0 +1,35 @@ +package client + +import ( + "fmt" + "strings" + + "github.com/spf13/cobra" +) + +func ListDefaultStoresCmd(stores []string) *cobra.Command { + cmd := &cobra.Command{ + Use: "default-stores", + Short: "List the store names in current binary version", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, args []string) error { + for _, name := range stores { + fmt.Println(name) + } + + return nil + }, + } + return cmd +} + +func GetStoresOrDefault(cmd *cobra.Command, defaultStores []string) ([]string, error) { + stores, err := cmd.Flags().GetString(flagStores) + if err != nil { + return nil, err + } + if len(stores) == 0 { + return defaultStores, nil + } + return strings.Split(stores, " "), nil +} diff --git a/versiondb/client/to_versiondb.go b/versiondb/client/to_versiondb.go new file mode 100644 index 0000000..59ce0d4 --- /dev/null +++ b/versiondb/client/to_versiondb.go @@ -0,0 +1,46 @@ +package client + +import ( + "github.com/cosmos/iavl" + "github.com/crypto-org-chain/cronos/versiondb/tsrocksdb" + "github.com/spf13/cobra" +) + +func ChangeSetToVersionDBCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "to-versiondb versiondb-path plain-1 [plain-2] ...", + Short: "Feed change set files into versiondb", + Args: cobra.MinimumNArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + store, err := cmd.Flags().GetString(flagStore) + if err != nil { + return err + } + + versionDB, err := tsrocksdb.NewStore(args[0]) + if err != nil { + return err + } + + for _, plainFile := range args[1:] { + if err := withChangeSetFile(plainFile, func(reader Reader) error { + _, err := IterateChangeSets(reader, func(version int64, changeSet *iavl.ChangeSet) (bool, error) { + if err := versionDB.FeedChangeSet(version, store, changeSet); err != nil { + return false, err + } + return true, nil + }) + + return err + }); err != nil { + return err + } + } + + return nil + }, + } + + cmd.Flags().String(flagStore, "", "store name, the keys are prefixed with \"s/k:{store}/\"") + return cmd +} diff --git a/versiondb/client/verify.go b/versiondb/client/verify.go new file mode 100644 index 0000000..1c4d969 --- /dev/null +++ b/versiondb/client/verify.go @@ -0,0 +1,315 @@ +package client + +import ( + "bytes" + "context" + "encoding/hex" + "fmt" + "os" + "path/filepath" + "runtime" + "sort" + "sync" + + "github.com/alitto/pond" + "github.com/cosmos/gogoproto/jsonpb" + "github.com/cosmos/iavl" + "github.com/spf13/cobra" + + storetypes "cosmossdk.io/store/types" + capabilitytypes "github.com/cosmos/ibc-go/modules/capability/types" + + "github.com/crypto-org-chain/cronos/memiavl" +) + +func VerifyChangeSetCmd(defaultStores []string) *cobra.Command { + cmd := &cobra.Command{ + Use: "verify changeSetDir", + Short: "Replay the input change set files in order to rebuild iavl tree in memory and output app hash and full json encoded commit info, user can compare the root hash against the block headers", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + concurrency, err := cmd.Flags().GetInt(flagConcurrency) + if err != nil { + return err + } + targetVersion, err := cmd.Flags().GetInt64(flagTargetVersion) + if err != nil { + return err + } + saveSnapshot, err := cmd.Flags().GetString(flagSaveSnapshot) + if err != nil { + return err + } + loadSnapshot, err := cmd.Flags().GetString(flagLoadSnapshot) + if err != nil { + return err + } + check, err := cmd.Flags().GetBool(flagCheck) + if err != nil { + return err + } + save, err := cmd.Flags().GetBool(flagSave) + if err != nil { + return err + } + stores, err := GetStoresOrDefault(cmd, defaultStores) + if err != nil { + return err + } + + if len(saveSnapshot) > 0 { + // detect the write permission early on. + if err := os.MkdirAll(saveSnapshot, os.ModePerm); err != nil { + return err + } + } + + changeSetDir := args[0] + + // create fixed size task pool with big enough buffer. + pool := pond.New(concurrency, 0) + defer pool.StopAndWait() + group, _ := pool.GroupContext(context.Background()) + + var ( + lastestVersion int64 + storeInfosLock sync.Mutex + ) + storeInfos := []storetypes.StoreInfo{ + // https://github.com/cosmos/cosmos-sdk/issues/14916 + {Name: capabilitytypes.MemStoreKey, CommitId: storetypes.CommitID{}}, + } + + mtree := memiavl.NewEmptyMultiTree(0, 0) + if len(loadSnapshot) > 0 { + var err error + mtree, err = memiavl.LoadMultiTree(loadSnapshot, true, 0) + if err != nil { + return err + } + } + + for _, store := range stores { + // https://github.com/golang/go/wiki/CommonMistakes#using-goroutines-on-loop-iterator-variables + store := store + tree := mtree.TreeByName(store) + if tree == nil { + tree = memiavl.New(0) + } + group.Submit(func() error { + storeInfo, err := verifyOneStore(tree, store, changeSetDir, saveSnapshot, targetVersion) + if err != nil { + return err + } + if storeInfo == nil { + // the store don't exist before target version, don't affect the commit info and app hash. + return nil + } + + storeInfosLock.Lock() + defer storeInfosLock.Unlock() + storeInfos = append(storeInfos, *storeInfo) + if storeInfo.CommitId.Version > lastestVersion { + lastestVersion = storeInfo.CommitId.Version + } + return nil + }) + } + if err := group.Wait(); err != nil { + return err + } + + commitInfo := buildCommitInfo(storeInfos, lastestVersion) + + if len(saveSnapshot) > 0 { + // write multitree metadata + metadata := memiavl.MultiTreeMetadata{ + CommitInfo: convertCommitInfo(&commitInfo), + } + bz, err := metadata.Marshal() + if err != nil { + return err + } + if err := memiavl.WriteFileSync(filepath.Join(saveSnapshot, memiavl.MetadataFileName), bz); err != nil { + return err + } + } + + // write out the replay result + var buf bytes.Buffer + buf.WriteString(hex.EncodeToString(commitInfo.Hash())) + buf.WriteString("\n") + marshaler := jsonpb.Marshaler{} + if err := marshaler.Marshal(&buf, &commitInfo); err != nil { + return err + } + + verifiedFileName := filepath.Join(changeSetDir, fmt.Sprintf("verified-%d", commitInfo.Version)) + if check { + // check commitInfo against the one stored in change set + bz, err := os.ReadFile(verifiedFileName) + if err != nil { + return err + } + + if !bytes.Equal(buf.Bytes(), bz) { + return fmt.Errorf("verify result don't match") + } + + fmt.Printf("version %d checked successfully\n", commitInfo.Version) + return nil + } + + if save { + if err := os.WriteFile(verifiedFileName, buf.Bytes(), 0o600); err != nil { + return err + } + fmt.Printf("version %d verify result saved to %s\n", commitInfo.Version, verifiedFileName) + return nil + } + + _, err = os.Stdout.Write(buf.Bytes()) + return err + }, + } + + cmd.Flags().Int64(flagTargetVersion, 0, "specify the target version, otherwise it'll exhaust the plain files") + cmd.Flags().String(flagStores, "", "list of store names, default to the current store list in application") + cmd.Flags().String(flagSaveSnapshot, "", "save the snapshot of the target iavl tree to directory") + cmd.Flags().String(flagLoadSnapshot, "", "load the snapshot before doing verification from directory") + cmd.Flags().Int(flagConcurrency, runtime.NumCPU(), "Number concurrent goroutines to parallelize the work") + cmd.Flags().Bool(flagCheck, false, "Check the replayed hash with the one stored in change set directory") + cmd.Flags().Bool(flagSave, false, "Save the verify result to change set directory, otherwise output to stdout") + + return cmd +} + +// verifyOneStore process a single store, can run in parallel with other stores. +// if the store don't exist before the `targetVersion`, returns nil without error. +func verifyOneStore(tree *memiavl.Tree, store, changeSetDir, saveSnapshot string, targetVersion int64) (*storetypes.StoreInfo, error) { + filesWithVersion, err := scanChangeSetFiles(changeSetDir, store) + if err != nil { + return nil, err + } + + if len(filesWithVersion) == 0 { + return nil, nil + } + // set the initial version for the store + initialVersion := filesWithVersion[0].Version + if targetVersion > 0 && initialVersion > uint64(targetVersion) { + return nil, nil + } + + if err := tree.SetInitialVersion(int64(initialVersion)); err != nil { + return nil, err + } + + for _, file := range filesWithVersion { + if targetVersion > 0 && file.Version > uint64(targetVersion) { + break + } + + err = withChangeSetFile(file.FileName, func(reader Reader) error { + _, err := IterateChangeSets(reader, func(version int64, changeSet *iavl.ChangeSet) (bool, error) { + if version <= tree.Version() { + // skip old change sets + return true, nil + } + + // no need to update hashes for intermediate versions. + tree.ApplyChangeSet(convertChangeSet(changeSet)) + _, v, err := tree.SaveVersion(false) + if err != nil { + return false, err + } + if v != version { + return false, fmt.Errorf("version don't match: %d != %d", v, version) + } + return targetVersion == 0 || v < targetVersion, nil + }) + + return err + }) + if err != nil { + break + } + + if targetVersion > 0 && tree.Version() >= targetVersion { + break + } + } + + if err != nil { + return nil, err + } + + if len(saveSnapshot) > 0 { + snapshotDir := filepath.Join(saveSnapshot, store) + if err := os.MkdirAll(snapshotDir, os.ModePerm); err != nil { + return nil, err + } + if err := tree.WriteSnapshot(snapshotDir); err != nil { + return nil, err + } + } + + return &storetypes.StoreInfo{ + Name: store, + CommitId: lastCommitID(tree), + }, nil +} + +// lastCommitID build `CommitID` from a memiavl tree. +func lastCommitID(tree *memiavl.Tree) storetypes.CommitID { + // copy out the hash in case it's relied on mmap-ed file. + var hash [memiavl.SizeHash]byte + copy(hash[:], tree.RootHash()) + return storetypes.CommitID{ + Version: tree.Version(), + Hash: hash[:], + } +} + +// buildCommitInfo sort the storeInfos by store name, and built `CommitInfo`. +func buildCommitInfo(storeInfos []storetypes.StoreInfo, version int64) storetypes.CommitInfo { + sort.SliceStable(storeInfos, func(i, j int) bool { + return storeInfos[i].Name < storeInfos[j].Name + }) + + return storetypes.CommitInfo{ + Version: storeInfos[0].CommitId.Version, + StoreInfos: storeInfos, + } +} + +func convertCommitInfo(commitInfo *storetypes.CommitInfo) *memiavl.CommitInfo { + storeInfos := make([]memiavl.StoreInfo, len(commitInfo.StoreInfos)) + for i, storeInfo := range commitInfo.StoreInfos { + storeInfos[i] = memiavl.StoreInfo{ + Name: storeInfo.Name, + CommitId: memiavl.CommitID{ + Version: storeInfo.CommitId.Version, + Hash: storeInfo.CommitId.Hash, + }, + } + } + return &memiavl.CommitInfo{ + Version: commitInfo.Version, + StoreInfos: storeInfos, + } +} + +func convertChangeSet(cs *iavl.ChangeSet) memiavl.ChangeSet { + pairs := make([]*memiavl.KVPair, len(cs.Pairs)) + for i, pair := range cs.Pairs { + pairs[i] = &memiavl.KVPair{ + Delete: pair.Delete, + Key: pair.Key, + Value: pair.Value, + } + } + return memiavl.ChangeSet{ + Pairs: pairs, + } +} diff --git a/versiondb/client/wrapreader.go b/versiondb/client/wrapreader.go new file mode 100644 index 0000000..1417231 --- /dev/null +++ b/versiondb/client/wrapreader.go @@ -0,0 +1,46 @@ +package client + +import ( + "errors" + "io" +) + +type wrapReader struct { + reader Reader + closer io.Closer +} + +// WrapReader wraps reader and closer together to create a new io.ReadCloser. +// +// The Read function will simply call the wrapped reader's Read function, +// while the Close function will call the wrapped closer's Close function. +// +// If the wrapped reader is also an io.Closer, +// its Close function will be called in Close as well. +// +// closer can be `nil`, to support stdin. +func WrapReader(reader Reader, closer io.Closer) ReadCloser { + return &wrapReader{ + reader: reader, + closer: closer, + } +} + +func (r *wrapReader) Read(p []byte) (int, error) { + return r.reader.Read(p) +} + +func (r *wrapReader) ReadByte() (byte, error) { + return r.reader.ReadByte() +} + +func (r *wrapReader) Close() error { + var errs []error + if closer, ok := r.reader.(io.Closer); ok { + errs = append(errs, closer.Close()) + } + if r.closer != nil { + errs = append(errs, r.closer.Close()) + } + return errors.Join(errs...) +} diff --git a/versiondb/extsort/delta_encoding.go b/versiondb/extsort/delta_encoding.go new file mode 100644 index 0000000..922e424 --- /dev/null +++ b/versiondb/extsort/delta_encoding.go @@ -0,0 +1,89 @@ +package extsort + +import ( + "encoding/binary" + "io" +) + +// DeltaEncoder applies delta encoding to a sequence of keys +type DeltaEncoder struct { + last []byte +} + +func NewDeltaEncoder() *DeltaEncoder { + return &DeltaEncoder{} +} + +func (de *DeltaEncoder) Write(w io.Writer, key []byte) error { + var sizeBuf [binary.MaxVarintLen64 + binary.MaxVarintLen64]byte + + shared := diffOffset(de.last, key) + nonShared := len(key) - shared + + n1 := binary.PutUvarint(sizeBuf[:], uint64(shared)) + n2 := binary.PutUvarint(sizeBuf[n1:], uint64(nonShared)) + + if _, err := w.Write(sizeBuf[:n1+n2]); err != nil { + return err + } + + if _, err := w.Write(key[shared:]); err != nil { + return err + } + + de.last = key + return nil +} + +// DeltaDecoder decodes delta-encoded keys +type DeltaDecoder struct { + last []byte +} + +func NewDeltaDecoder() *DeltaDecoder { + return &DeltaDecoder{} +} + +func (dd *DeltaDecoder) Read( + reader reader, +) ([]byte, error) { + shared, err := binary.ReadUvarint(reader) + if err != nil { + return nil, err + } + nonShared, err := binary.ReadUvarint(reader) + if err != nil { + return nil, err + } + + item := make([]byte, shared+nonShared) + copy(item[:shared], dd.last[:shared]) + if _, err := io.ReadFull(reader, item[shared:]); err != nil { + return nil, err + } + + dd.last = item + return item, nil +} + +type reader interface { + io.Reader + io.ByteReader +} + +// diffOffset returns the index of first byte that's different in two bytes slice. +func diffOffset(a, b []byte) int { + var off int + var l int + if len(a) < len(b) { + l = len(a) + } else { + l = len(b) + } + for ; off < l; off++ { + if a[off] != b[off] { + break + } + } + return off +} diff --git a/versiondb/extsort/delta_encoding_test.go b/versiondb/extsort/delta_encoding_test.go new file mode 100644 index 0000000..f09da3b --- /dev/null +++ b/versiondb/extsort/delta_encoding_test.go @@ -0,0 +1,51 @@ +package extsort + +import ( + "bytes" + "io" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestDeltaEncoding(t *testing.T) { + testCases := []struct { + name string + keys []string + expSize int + }{ + { + "no share", + []string{"hello", "world"}, + 14, + }, + { + "share", + []string{"hello", "hello2"}, + 10, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + encoder := NewDeltaEncoder() + var buf bytes.Buffer + for _, key := range tc.keys { + require.NoError(t, encoder.Write(&buf, []byte(key))) + } + require.Equal(t, tc.expSize, buf.Len()) + + decoder := NewDeltaDecoder() + var keys []string + for { + key, err := decoder.Read(&buf) + if err == io.EOF { + break + } + require.NoError(t, err) + keys = append(keys, string(key)) + } + require.Equal(t, tc.keys, keys) + }) + } +} diff --git a/versiondb/extsort/merge.go b/versiondb/extsort/merge.go new file mode 100644 index 0000000..f54bf32 --- /dev/null +++ b/versiondb/extsort/merge.go @@ -0,0 +1,110 @@ +package extsort + +import ( + "container/heap" +) + +// MultiWayMerge implements k-way merge using min-heap provided by golang builtin library, +// it implements `heap.Interface` interface. +type MultiWayMerge struct { + entries []*heapEntry + lesserFunc LesserFunc +} + +var _ heap.Interface = (*MultiWayMerge)(nil) + +// NewMultiWayMerge initialize a new `MultiWayMerge` instance. +func NewMultiWayMerge(streams []NextFunc, lesserFunc LesserFunc) (*MultiWayMerge, error) { + var entries []*heapEntry + for _, stream := range streams { + entry, err := newHeapEntry(stream) + if err != nil { + return nil, err + } + if entry != nil { + entries = append(entries, entry) + } + } + + merge := &MultiWayMerge{ + entries: entries, + lesserFunc: lesserFunc, + } + + heap.Init(merge) + return merge, nil +} + +// Len implements `heap.Interface` +func (merge *MultiWayMerge) Len() int { + return len(merge.entries) +} + +// Swap implements `heap.Interface` +func (merge *MultiWayMerge) Swap(i, j int) { + merge.entries[i], merge.entries[j] = merge.entries[j], merge.entries[i] +} + +// Less implements `heap.Interface` +func (merge *MultiWayMerge) Less(i, j int) bool { + return merge.lesserFunc(merge.entries[i].value, merge.entries[j].value) +} + +// Push implements `heap.Interface` +func (merge *MultiWayMerge) Push(x interface{}) { + entry := x.(*heapEntry) + merge.entries = append(merge.entries, entry) +} + +// Pop implements `heap.Interface` +func (merge *MultiWayMerge) Pop() interface{} { + l := merge.Len() + item := merge.entries[l-1] + merge.entries = merge.entries[:l-1] + return item +} + +// Next provides an iterator that yields sorted items +func (merge *MultiWayMerge) Next() ([]byte, error) { + if merge.Len() == 0 { + return nil, nil + } + minEntry := merge.entries[0] + result := minEntry.value + if err := minEntry.update(); err != nil { + return nil, err + } + if minEntry.value == nil { + heap.Remove(merge, 0) + } else { + heap.Fix(merge, 0) + } + return result, nil +} + +// heapEntry is the min-heap entry represents a single sorted chunk. +type heapEntry struct { + stream NextFunc + value []byte +} + +// newHeapEntry initialize a `heapEntry`. +func newHeapEntry(stream NextFunc) (*heapEntry, error) { + value, err := stream() + if err != nil { + return nil, err + } + if value == nil { + return nil, nil + } + return &heapEntry{ + stream: stream, + value: value, + }, nil +} + +// update fetch the next item in the stream +func (entry *heapEntry) update() (err error) { + entry.value, err = entry.stream() + return +} diff --git a/versiondb/extsort/sort.go b/versiondb/extsort/sort.go new file mode 100644 index 0000000..8b223b3 --- /dev/null +++ b/versiondb/extsort/sort.go @@ -0,0 +1,253 @@ +// Package extsort implements external sorting algorithm, it has several differnet design choices compared with alternatives like https://github.com/lanrat/extsort: +// - apply efficient compressions(delta encoding + snappy) to the chunk files to reduce IO cost, +// since the items are sorted, delta encoding should be effective to it, and snappy is pretty efficient. +// - chunks are stored in separate temporary files, so the chunk sorting and saving can run in parallel (eats more ram though). +// - clean interface, user just feed `[]byte` directly, and provides a compare function based on `[]byte`. +package extsort + +import ( + "bufio" + "encoding/binary" + "errors" + "io" + "math" + "os" + "sort" + "strings" + "sync" + + "github.com/golang/snappy" +) + +// Options defines configurable options for `ExtSorter` +type Options struct { + // if apply delta encoding to the items in sorted chunk + DeltaEncoding bool + // if apply snappy compression to the items in sorted chunk + SnappyCompression bool + // Maxiumum uncompressed size of the sorted chunk + MaxChunkSize int64 + // function that compares two items + LesserFunc LesserFunc +} + +// ExtSorter implements external sorting. +// It split the inputs into chunks, sort each chunk separately and save to disk file, +// then provide an iterator of sorted items by doing a k-way merge with all the sorted chunk files. +type ExtSorter struct { + opts Options + + // directory to store temporary chunk files + tmpDir string + + // current chunk + currentChunk [][]byte + currentChunkSize int64 + + // manage the chunk goroutines + chunkWG sync.WaitGroup + lock sync.Mutex + // finished chunk files + chunkFiles []*os.File + // chunking goroutine failure messages + failures []string +} + +// New creates a new `ExtSorter`. +func New(tmpDir string, opts Options) *ExtSorter { + return &ExtSorter{ + tmpDir: tmpDir, + opts: opts, + } +} + +// Spawn spawns a new goroutine to do the external sorting, +// returns two channels for user to interact. +func Spawn(tmpDir string, opts Options, bufferSize int) (chan []byte, chan []byte) { + inputChan := make(chan []byte, bufferSize) + outputChan := make(chan []byte, bufferSize) + + go func() { + defer close(outputChan) + + sorter := New(tmpDir, opts) + defer sorter.Close() + + for bz := range inputChan { + if err := sorter.Feed(bz); err != nil { + panic(err) + } + } + + reader, err := sorter.Finalize() + if err != nil { + panic(err) + } + + for { + item, err := reader.Next() + if err != nil { + panic(err) + } + if item == nil { + break + } + + outputChan <- item + } + }() + + return inputChan, outputChan +} + +// Feed add un-ordered items to the sorter. +func (s *ExtSorter) Feed(item []byte) error { + if len(item) > math.MaxUint32 { + return errors.New("item length overflows uint32") + } + + s.currentChunkSize += int64(len(item)) + 4 + s.currentChunk = append(s.currentChunk, item) + + if s.currentChunkSize >= s.opts.MaxChunkSize { + return s.sortChunkAndRotate() + } + return nil +} + +// sortChunkAndRotate sort the current chunk and save to disk. +func (s *ExtSorter) sortChunkAndRotate() error { + chunkFile, err := os.CreateTemp(s.tmpDir, "sort-chunk-*") + if err != nil { + return err + } + + // rotate chunk + chunk := s.currentChunk + s.currentChunk = nil + s.currentChunkSize = 0 + + s.chunkWG.Add(1) + go func() { + defer s.chunkWG.Done() + if err := s.sortAndSaveChunk(chunk, chunkFile); err != nil { + chunkFile.Close() + s.lock.Lock() + defer s.lock.Unlock() + s.failures = append(s.failures, err.Error()) + return + } + s.lock.Lock() + defer s.lock.Unlock() + s.chunkFiles = append(s.chunkFiles, chunkFile) + }() + return nil +} + +// Finalize wait for all chunking goroutines to finish, and return the merged sorted stream. +func (s *ExtSorter) Finalize() (*MultiWayMerge, error) { + // handle the pending chunk + if s.currentChunkSize > 0 { + if err := s.sortChunkAndRotate(); err != nil { + return nil, err + } + } + + s.chunkWG.Wait() + if len(s.failures) > 0 { + return nil, errors.New(strings.Join(s.failures, "\n")) + } + + streams := make([]NextFunc, len(s.chunkFiles)) + for i, chunkFile := range s.chunkFiles { + if _, err := chunkFile.Seek(0, 0); err != nil { + return nil, err + } + + var reader reader + if s.opts.SnappyCompression { + reader = snappy.NewReader(chunkFile) + } else { + reader = bufio.NewReader(chunkFile) + } + + if s.opts.DeltaEncoding { + decoder := NewDeltaDecoder() + streams[i] = func() ([]byte, error) { + item, err := decoder.Read(reader) + if err == io.EOF { + return nil, nil + } + return item, err + } + } else { + streams[i] = func() ([]byte, error) { + size, err := binary.ReadUvarint(reader) + if err != nil { + if err == io.EOF && size == 0 { + return nil, nil + } + return nil, err + } + item := make([]byte, size) + if _, err := io.ReadFull(reader, item); err != nil { + return nil, err + } + return item, nil + } + } + } + + return NewMultiWayMerge(streams, s.opts.LesserFunc) +} + +// Close closes and remove all the temporary chunk files +func (s *ExtSorter) Close() error { + var errs []error + for _, chunkFile := range s.chunkFiles { + errs = append(errs, chunkFile.Close(), os.Remove(chunkFile.Name())) + } + return errors.Join(errs...) +} + +type bufWriter interface { + io.Writer + Flush() error +} + +// sortAndSaveChunk sort the chunk in memory and save to disk in order, +// it applies delta encoding and snappy compression to the items. +func (s *ExtSorter) sortAndSaveChunk(chunk [][]byte, output *os.File) error { + // sort the chunk and write to file + sort.Slice(chunk, func(i, j int) bool { + return s.opts.LesserFunc(chunk[i], chunk[j]) + }) + + var writer bufWriter + if s.opts.SnappyCompression { + writer = snappy.NewBufferedWriter(output) + } else { + writer = bufio.NewWriter(output) + } + + if s.opts.DeltaEncoding { + encoder := NewDeltaEncoder() + for _, item := range chunk { + if err := encoder.Write(writer, item); err != nil { + return err + } + } + } else { + for _, item := range chunk { + var buf [binary.MaxVarintLen64]byte + n := binary.PutUvarint(buf[:], uint64(len(item))) + if _, err := writer.Write(buf[:n]); err != nil { + return err + } + if _, err := writer.Write(item); err != nil { + return err + } + } + } + return writer.Flush() +} diff --git a/versiondb/extsort/sort_test.go b/versiondb/extsort/sort_test.go new file mode 100644 index 0000000..92e909b --- /dev/null +++ b/versiondb/extsort/sort_test.go @@ -0,0 +1,71 @@ +package extsort + +import ( + "bytes" + "fmt" + "math/rand" + "sort" + "testing" + + "github.com/stretchr/testify/require" +) + +const ( + ItemSize = 20 + ItemTpl = "testkey-%d" +) + +func doTestExtSorter(t *testing.T, chunkSize int64, inputCount int) { + sorter := New("/tmp", Options{ + MaxChunkSize: chunkSize, + LesserFunc: func(a, b []byte) bool { + return bytes.Compare(a, b) == -1 + }, + DeltaEncoding: true, + SnappyCompression: true, + }) + defer func() { + require.NoError(t, sorter.Close()) + }() + + var expItems [][]byte + for i := 0; i < inputCount; i++ { + item := []byte(fmt.Sprintf(ItemTpl, i)) + expItems = append(expItems, item) + } + + sort.Slice(expItems, func(i, j int) bool { + return bytes.Compare(expItems[i], expItems[j]) == -1 + }) + + randItems := make([][]byte, len(expItems)) + copy(randItems, expItems) + g := rand.New(rand.NewSource(0)) + g.Shuffle(len(randItems), func(i, j int) { randItems[i], randItems[j] = randItems[j], randItems[i] }) + + // feed in random order + for _, item := range randItems { + err := sorter.Feed(item) + require.NoError(t, err) + } + reader, err := sorter.Finalize() + require.NoError(t, err) + + var result [][]byte + for { + item, err := reader.Next() + require.NoError(t, err) + if item == nil { + break + } + result = append(result, item) + } + + require.Equal(t, expItems, result) +} + +func TestExtSort(t *testing.T) { + doTestExtSorter(t, ItemSize*100, 100) + doTestExtSorter(t, ItemSize*100, 1550) + doTestExtSorter(t, ItemSize*100, 155000) +} diff --git a/versiondb/extsort/types.go b/versiondb/extsort/types.go new file mode 100644 index 0000000..a8dca07 --- /dev/null +++ b/versiondb/extsort/types.go @@ -0,0 +1,8 @@ +package extsort + +type ( + // LesserFunc compares two items in external sorter + LesserFunc func(a, b []byte) bool + // NextFunc is a stream that yields bytes, it should return `nil` when stream is exhausted + NextFunc func() ([]byte, error) +) diff --git a/versiondb/go.mod b/versiondb/go.mod new file mode 100644 index 0000000..a0f2a0f --- /dev/null +++ b/versiondb/go.mod @@ -0,0 +1,174 @@ +module github.com/crypto-org-chain/cronos/versiondb + +go 1.22 + +toolchain go1.22.0 + +require ( + cosmossdk.io/errors v1.0.1 + cosmossdk.io/log v1.3.1 + cosmossdk.io/store v1.1.0 + github.com/alitto/pond v1.8.3 + github.com/cometbft/cometbft v0.38.11 + github.com/cosmos/cosmos-db v1.0.2 + github.com/cosmos/cosmos-sdk v0.50.4 + github.com/cosmos/gogoproto v1.4.12 + github.com/cosmos/iavl v1.1.2 + github.com/cosmos/ibc-go/modules/capability v1.0.0 + github.com/crypto-org-chain/cronos/memiavl v0.0.3 + github.com/golang/snappy v0.0.4 + github.com/linxGnu/grocksdb v1.9.2 + github.com/spf13/cast v1.6.0 + github.com/spf13/cobra v1.8.0 + github.com/stretchr/testify v1.9.0 +) + +require ( + cosmossdk.io/api v0.7.5 // indirect + cosmossdk.io/collections v0.4.0 // indirect + cosmossdk.io/core v0.11.0 // indirect + cosmossdk.io/depinject v1.0.0-alpha.4 // indirect + cosmossdk.io/math v1.3.0 // indirect + cosmossdk.io/x/tx v0.13.3 // indirect + filippo.io/edwards25519 v1.0.0 // indirect + github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect + github.com/99designs/keyring v1.2.1 // indirect + github.com/DataDog/datadog-go v3.2.0+incompatible // indirect + github.com/DataDog/zstd v1.5.5 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect + github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect + github.com/cenkalti/backoff/v4 v4.1.3 // indirect + github.com/cespare/xxhash v1.1.0 // 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/cometbft/cometbft-db v0.9.1 // indirect + github.com/cosmos/btcutil v1.0.5 // indirect + github.com/cosmos/cosmos-proto v1.0.0-beta.5 // indirect + github.com/cosmos/go-bip39 v1.0.0 // indirect + github.com/cosmos/gogogateway v1.2.0 // indirect + github.com/cosmos/ics23/go v0.10.0 // indirect + github.com/cosmos/ledger-cosmos-go v0.13.3 // indirect + github.com/danieljoos/wincred v1.1.2 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect + github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect + github.com/dgraph-io/badger/v2 v2.2007.4 // indirect + github.com/dgraph-io/ristretto v0.1.1 // indirect + github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/dvsekhvalnov/jose2go v1.6.0 // indirect + github.com/emicklei/dot v1.6.1 // indirect + github.com/fatih/color v1.15.0 // indirect + github.com/felixge/httpsnoop v1.0.2 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/getsentry/sentry-go v0.27.0 // indirect + github.com/go-kit/kit v0.12.0 // indirect + github.com/go-kit/log v0.2.1 // indirect + github.com/go-logfmt/logfmt v0.6.0 // indirect + github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect + github.com/gogo/googleapis v1.4.1 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/glog v1.2.0 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/google/btree v1.1.2 // indirect + github.com/google/go-cmp v0.6.0 // indirect + github.com/google/orderedcode v0.0.1 // indirect + github.com/gorilla/handlers v1.5.1 // indirect + github.com/gorilla/mux v1.8.0 // indirect + github.com/gorilla/websocket v1.5.0 // indirect + github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect + github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect + github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect + github.com/hashicorp/go-hclog v1.5.0 // indirect + github.com/hashicorp/go-immutable-radix v1.3.1 // indirect + github.com/hashicorp/go-metrics v0.5.3 // indirect + github.com/hashicorp/go-plugin v1.5.2 // indirect + github.com/hashicorp/golang-lru v1.0.2 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/hashicorp/yamux v0.1.1 // indirect + github.com/hdevalence/ed25519consensus v0.1.0 // indirect + github.com/huandu/skiplist v1.2.0 // indirect + github.com/iancoleman/strcase v0.3.0 // indirect + github.com/improbable-eng/grpc-web v0.15.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/jmhodges/levigo v1.0.0 // indirect + github.com/klauspost/compress v1.17.7 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/ledgerwatch/erigon-lib v0.0.0-20230210071639-db0e7ed11263 // indirect + github.com/lib/pq v1.10.7 // 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/minio/highwayhash v1.0.2 // indirect + github.com/mitchellh/go-testing-interface v1.14.1 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/mtibben/percent v0.2.1 // indirect + github.com/oasisprotocol/curve25519-voi v0.0.0-20230904125328-1f23a7beb09a // indirect + github.com/oklog/run v1.1.0 // indirect + github.com/pelletier/go-toml/v2 v2.1.0 // indirect + github.com/petermattis/goid v0.0.0-20231207134359-e60b3f734c67 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/prometheus/client_golang v1.19.0 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.52.2 // indirect + github.com/prometheus/procfs v0.13.0 // indirect + github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect + github.com/rs/cors v1.8.3 // indirect + github.com/rs/zerolog v1.32.0 // indirect + github.com/sagikazarmark/locafero v0.4.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/sasha-s/go-deadlock v0.3.1 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.11.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/spf13/viper v1.18.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 + github.com/tidwall/gjson v1.10.2 // indirect + github.com/tidwall/match v1.1.1 // indirect + github.com/tidwall/pretty v1.2.0 // indirect + github.com/tidwall/tinylru v1.1.0 // indirect + github.com/tidwall/wal v1.1.7 // indirect + github.com/zbiljic/go-filelock v0.0.0-20170914061330-1dbf7103ab7d // indirect + github.com/zondax/hid v0.9.2 // indirect + github.com/zondax/ledger-go v0.14.3 // indirect + go.etcd.io/bbolt v1.3.8 // indirect + go.uber.org/multierr v1.10.0 // indirect + golang.org/x/crypto v0.22.0 // indirect + golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0 // indirect + golang.org/x/net v0.24.0 // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/sys v0.19.0 // indirect + golang.org/x/term v0.19.0 // indirect + golang.org/x/text v0.14.0 // indirect + google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda // indirect + google.golang.org/grpc v1.63.2 // indirect + google.golang.org/protobuf v1.33.0 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + gotest.tools/v3 v3.5.1 // indirect + nhooyr.io/websocket v1.8.6 // indirect + pgregory.net/rapid v1.1.0 // indirect + sigs.k8s.io/yaml v1.4.0 // indirect +) + +replace ( + // release/v0.50.x + cosmossdk.io/store => github.com/crypto-org-chain/cosmos-sdk/store v0.0.0-20240722033504-50f1fa0c49d1 + // release/v0.50.x + github.com/cosmos/cosmos-sdk => github.com/crypto-org-chain/cosmos-sdk v0.50.6-0.20240722033504-50f1fa0c49d1 + github.com/crypto-org-chain/cronos/memiavl => ../memiavl +) diff --git a/versiondb/go.sum b/versiondb/go.sum new file mode 100644 index 0000000..9def9d3 --- /dev/null +++ b/versiondb/go.sum @@ -0,0 +1,1039 @@ +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/api v0.7.5 h1:eMPTReoNmGUm8DeiQL9DyM8sYDjEhWzL1+nLbI9DqtQ= +cosmossdk.io/api v0.7.5/go.mod h1:IcxpYS5fMemZGqyYtErK7OqvdM0C8kdW3dq8Q/XIG38= +cosmossdk.io/collections v0.4.0 h1:PFmwj2W8szgpD5nOd8GWH6AbYNi1f2J6akWXJ7P5t9s= +cosmossdk.io/collections v0.4.0/go.mod h1:oa5lUING2dP+gdDquow+QjlF45eL1t4TJDypgGd+tv0= +cosmossdk.io/core v0.11.0 h1:vtIafqUi+1ZNAE/oxLOQQ7Oek2n4S48SWLG8h/+wdbo= +cosmossdk.io/core v0.11.0/go.mod h1:LaTtayWBSoacF5xNzoF8tmLhehqlA9z1SWiPuNC6X1w= +cosmossdk.io/depinject v1.0.0-alpha.4 h1:PLNp8ZYAMPTUKyG9IK2hsbciDWqna2z1Wsl98okJopc= +cosmossdk.io/depinject v1.0.0-alpha.4/go.mod h1:HeDk7IkR5ckZ3lMGs/o91AVUc7E596vMaOmslGFM3yU= +cosmossdk.io/errors v1.0.1 h1:bzu+Kcr0kS/1DuPBtUFdWjzLqyUuCiyHjyJB6srBV/0= +cosmossdk.io/errors v1.0.1/go.mod h1:MeelVSZThMi4bEakzhhhE/CKqVv3nOJDA25bIqRDu/U= +cosmossdk.io/log v1.3.1 h1:UZx8nWIkfbbNEWusZqzAx3ZGvu54TZacWib3EzUYmGI= +cosmossdk.io/log v1.3.1/go.mod h1:2/dIomt8mKdk6vl3OWJcPk2be3pGOS8OQaLUM/3/tCM= +cosmossdk.io/math v1.3.0 h1:RC+jryuKeytIiictDslBP9i1fhkVm6ZDmZEoNP316zE= +cosmossdk.io/math v1.3.0/go.mod h1:vnRTxewy+M7BtXBNFybkuhSH4WfedVAAnERHgVFhp3k= +cosmossdk.io/x/tx v0.13.3 h1:Ha4mNaHmxBc6RMun9aKuqul8yHiL78EKJQ8g23Zf73g= +cosmossdk.io/x/tx v0.13.3/go.mod h1:I8xaHv0rhUdIvIdptKIqzYy27+n2+zBVaxO6fscFhys= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek= +filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= +github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs= +github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4/go.mod h1:hN7oaIRCjzsZ2dE+yG5k+rsdt3qcwykqK6HVGcKwsw4= +github.com/99designs/keyring v1.2.1 h1:tYLp1ULvO7i3fI5vE21ReQuj99QFSs7lGm0xWyJo87o= +github.com/99designs/keyring v1.2.1/go.mod h1:fc+wB5KTk9wQ9sDx0kFXB3A0MaeGHM9AwRStKOQ5vOA= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/DataDog/datadog-go v3.2.0+incompatible h1:qSG2N4FghB1He/r2mFrWKCaL7dXCilEuNEeAn20fdD4= +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/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= +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/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= +github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= +github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= +github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= +github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= +github.com/adlio/schema v1.3.3 h1:oBJn8I02PyTB466pZO1UZEn1TV5XLlifBSyMrmHl/1I= +github.com/adlio/schema v1.3.3/go.mod h1:1EsRssiv9/Ce2CMzq5DoL7RiMshhuigQxrR4DMV9fHg= +github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/alitto/pond v1.8.3 h1:ydIqygCLVPqIX/USe5EaV/aSRXTRXDEI9JwuDdu+/xs= +github.com/alitto/pond v1.8.3/go.mod h1:CmvIIGd5jKLasGI3D87qDkQxjzChdKMmnXMg3fG6M6Q= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= +github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= +github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +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/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 h1:41iFGWnSlI2gVpmOtVTJZNodLdLQLn/KsJqFvXwnd/s= +github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bits-and-blooms/bitset v1.8.0 h1:FD+XqgOZDUxxZ8hzoBFuV9+cGWY9CslN6d5MS5JVb4c= +github.com/bits-and-blooms/bitset v1.8.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= +github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= +github.com/btcsuite/btcd/btcutil v1.1.3 h1:xfbtw8lwpp0G6NwSHb+UE67ryTFHJAiNuipusjXSohQ= +github.com/btcsuite/btcd/btcutil v1.1.3/go.mod h1:UR7dsSJzJUfMmFiiLlIrMq1lS9jh9EdCV7FStZSnpi0= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/bufbuild/protocompile v0.6.0 h1:Uu7WiSQ6Yj9DbkdnOe7U4mNKp58y9WDMKDn28/ZlunY= +github.com/bufbuild/protocompile v0.6.0/go.mod h1:YNP35qEYoYGme7QMtz5SBCoN4kL4g12jTtjuzRNdjpE= +github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= +github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= +github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= +github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.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/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI= +github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk= +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/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/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/apd/v2 v2.0.2 h1:weh8u7Cneje73dDh+2tEVLUvyBc89iwepWCD8b8034E= +github.com/cockroachdb/apd/v2 v2.0.2/go.mod h1:DDxRlzC2lo3/vSlmSoS7JkqbbrARPuFOGr0B9pvN3Gw= +github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +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/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= +github.com/cometbft/cometbft v0.38.11 h1:6bNDUB8/xq4uYonYwIfGc9OqK1ZH4NkdaMmR1LZIJqk= +github.com/cometbft/cometbft v0.38.11/go.mod h1:jHPx9vQpWzPHEAiYI/7EDKaB1NXhK6o3SArrrY8ExKc= +github.com/cometbft/cometbft-db v0.9.1 h1:MIhVX5ja5bXNHF8EYrThkG9F7r9kSfv8BX4LWaxWJ4M= +github.com/cometbft/cometbft-db v0.9.1/go.mod h1:iliyWaoV0mRwBJoizElCwwRA9Tf7jZJOURcRZF9m60U= +github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg= +github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cosmos/btcutil v1.0.5 h1:t+ZFcX77LpKtDBhjucvnOH8C2l2ioGsBNEQ3jef8xFk= +github.com/cosmos/btcutil v1.0.5/go.mod h1:IyB7iuqZMJlthe2tkIFL33xPyzbFYP0XVdS8P5lUPis= +github.com/cosmos/cosmos-db v1.0.2 h1:hwMjozuY1OlJs/uh6vddqnk9j7VamLv+0DBlbEXbAKs= +github.com/cosmos/cosmos-db v1.0.2/go.mod h1:Z8IXcFJ9PqKK6BIsVOB3QXtkKoqUOp1vRvPT39kOXEA= +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/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY= +github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4xuwvCdJw= +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.4.12 h1:vB6Lbe/rtnYGjQuFxkPiPYiCybqFT8QvLipDZP8JpFE= +github.com/cosmos/gogoproto v1.4.12/go.mod h1:LnZob1bXRdUoqMMtwYlcR3wjiElmlC+FkjaZRv1/eLY= +github.com/cosmos/iavl v1.1.2 h1:zL9FK7C4L/P4IF1Dm5fIwz0WXCnn7Bp1M2FxH0ayM7Y= +github.com/cosmos/iavl v1.1.2/go.mod h1:jLeUvm6bGT1YutCaL2fIar/8vGUE8cPZvh/gXEWDaDM= +github.com/cosmos/ibc-go/modules/capability v1.0.0 h1:r/l++byFtn7jHYa09zlAdSeevo8ci1mVZNO9+V0xsLE= +github.com/cosmos/ibc-go/modules/capability v1.0.0/go.mod h1:D81ZxzjZAe0ZO5ambnvn1qedsFQ8lOwtqicG6liLBco= +github.com/cosmos/ics23/go v0.10.0 h1:iXqLLgp2Lp+EdpIuwXTYIQU+AiHj9mOC2X9ab++bZDM= +github.com/cosmos/ics23/go v0.10.0/go.mod h1:ZfJSmng/TBNTBkFemHHHj5YY7VAU/MBU980F4VU1NG0= +github.com/cosmos/ledger-cosmos-go v0.13.3 h1:7ehuBGuyIytsXbd4MP43mLeoN2LTOEnk5nvue4rK+yM= +github.com/cosmos/ledger-cosmos-go v0.13.3/go.mod h1:HENcEP+VtahZFw38HZ3+LS3Iv5XV6svsnkk9vdJtLr8= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/crypto-org-chain/cosmos-sdk v0.50.6-0.20240722033504-50f1fa0c49d1 h1:xahHemSiT79xgh8Ig8zOTeHSLHt9FfPzViK7rATWhUM= +github.com/crypto-org-chain/cosmos-sdk v0.50.6-0.20240722033504-50f1fa0c49d1/go.mod h1:Rb43DdB0i/rKcCN69Tg2X3+zA4WhJ7MC8K3a6Ezh38E= +github.com/crypto-org-chain/cosmos-sdk/store v0.0.0-20240722033504-50f1fa0c49d1 h1:ZlezTiQu9pYpVO+6sB9+W3fvthIpV1GgSI8kPjw+v5s= +github.com/crypto-org-chain/cosmos-sdk/store v0.0.0-20240722033504-50f1fa0c49d1/go.mod h1:gjE3DZe4t/+VeIk6CmrouyqiuDbZ7QOVDDq3nLqBTpg= +github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0= +github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0= +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/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= +github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= +github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f h1:U5y3Y5UE0w7amNe7Z5G/twsBW0KEalRQXZzf8ufSh9I= +github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE= +github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o= +github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk= +github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= +github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= +github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= +github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/dvsekhvalnov/jose2go v1.6.0 h1:Y9gnSnP4qEI0+/uQkHvFXeD2PLPJeXEL+ySMEA2EjTY= +github.com/dvsekhvalnov/jose2go v1.6.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB/mPZadG+mhXU= +github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= +github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/emicklei/dot v1.6.1 h1:ujpDlBkkwgWUY+qPId5IwapRW/xEoligRSYjioR6DFI= +github.com/emicklei/dot v1.6.1/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s= +github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= +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.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= +github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= +github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/felixge/httpsnoop v1.0.2 h1:+nS9g82KMXccJ/wp0zyRW9ZBHFETmMGtkk+2CTTrW4o= +github.com/felixge/httpsnoop v1.0.2/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= +github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= +github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= +github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/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/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= +github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8= +github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= +github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= +github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= +github.com/go-kit/kit v0.12.0 h1:e4o3o3IsBfAKQh5Qbbiqyfu97Ku7jrO/JbohvztANh4= +github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEaizzs= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= +github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= +github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= +github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= +github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= +github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= +github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ= +github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= +github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +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/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0= +github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= +github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8= +github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo= +github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= +github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk= +github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0= +github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= +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.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68= +github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= +github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +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.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +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 v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/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.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/orderedcode v0.0.1 h1:UzfcAexk9Vhv8+9pNOgRu41f16lHq725vPwnSeiG/Us= +github.com/google/orderedcode v0.0.1/go.mod h1:iVyU4/qPKHY5h/wSd6rZZCDcLJNxiWO6dvsYES2Sb20= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= +github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= +github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= +github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= +github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +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/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8ZofjG1Y75iExal34USq5p+wiN1tpie8IrU= +github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NMPJylDgVpX0MLRlPy15sqSwOFv/U1GZ2m21JhFfek0= +github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= +github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= +github.com/hashicorp/go-hclog v1.5.0/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-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-plugin v1.5.2 h1:aWv8eimFqWlsEiMrYZdPYl+FdHaBJSN4AWwGWfT1G2Y= +github.com/hashicorp/go-plugin v1.5.2/go.mod h1:w1sAEES3g3PuV/RzUrgow20W2uErMly84hhD3um1WL4= +github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE= +github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= +github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= +github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= +github.com/hdevalence/ed25519consensus v0.1.0 h1:jtBwzzcHuTmFrQN6xQZn6CQEO/V9f7HsjsjeEZ6auqU= +github.com/hdevalence/ed25519consensus v0.1.0/go.mod h1:w3BHWjwJbFU29IRHL1Iqkw3sus+7FctEyM4RqDxYNzo= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/huandu/go-assert v1.1.5 h1:fjemmA7sSfYHJD7CUqs9qTwwfdNAx7/j2/ZlHXzNB3c= +github.com/huandu/go-assert v1.1.5/go.mod h1:yOLvuqZwmcHIC5rIzrBhT7D3Q9c3GFnd0JrPVhn/06U= +github.com/huandu/skiplist v1.2.0 h1:gox56QD77HzSC0w+Ws3MH3iie755GBJU1OER3h5VsYw= +github.com/huandu/skiplist v1.2.0/go.mod h1:7v3iFjLcSAzO4fN5B8dvebvo/qsfumiLiDXMrPiHF9w= +github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= +github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI= +github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/improbable-eng/grpc-web v0.15.0 h1:BN+7z6uNXZ1tQGcNAuaU1YjsLTApzkjt2tzCixLaUPQ= +github.com/improbable-eng/grpc-web v0.15.0/go.mod h1:1sy9HKV4Jt9aEs9JSnkWlRJPuPtwNr0l57L4f878wP8= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= +github.com/jhump/protoreflect v1.15.3 h1:6SFRuqU45u9hIZPJAoZ8c28T3nK64BNdp9w6jFonzls= +github.com/jhump/protoreflect v1.15.3/go.mod h1:4ORHmSBmlCW8fh3xHmJMGyul1zNqZK4Elxc8qKP+p1k= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U= +github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= +github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg= +github.com/klauspost/compress v1.17.7/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/konsorten/go-windows-terminal-sequences v1.0.3/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/ledgerwatch/erigon-lib v0.0.0-20230210071639-db0e7ed11263 h1:LGEzZvf33Y1NhuP5+jI/ni9l1TFS6oYPDilgy74NusM= +github.com/ledgerwatch/erigon-lib v0.0.0-20230210071639-db0e7ed11263/go.mod h1:OXgMDuUo2lZ3NpH29ZvMYbk+LxFd5ffDl2Z2mGMuY/I= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= +github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= +github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= +github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= +github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= +github.com/linxGnu/grocksdb v1.9.2 h1:O3mzvO0wuzQ9mtlHbDrShixyVjVbmuqTjFrzlf43wZ8= +github.com/linxGnu/grocksdb v1.9.2/go.mod h1:QYiYypR2d4v63Wj1adOOfzglnoII0gLj3PNh4fZkcFA= +github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA= +github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.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-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= +github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= +github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.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 h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mtibben/percent v0.2.1 h1:5gssi8Nqo8QU/r2pynCm+hBQHpkB/uNK7BJCFogWdzs= +github.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ibNBTZrns= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/grpc-proxy v0.0.0-20181017164139-0f1106ef9c76/go.mod h1:x5OoJHDHqxHS801UIuhqGl6QdSAEJvtausosHSdazIo= +github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= +github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= +github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= +github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= +github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/oasisprotocol/curve25519-voi v0.0.0-20230904125328-1f23a7beb09a h1:dlRvE5fWabOchtH7znfiFCcOvmIYgOeAS5ifBXBlh9Q= +github.com/oasisprotocol/curve25519-voi v0.0.0-20230904125328-1f23a7beb09a/go.mod h1:hVoHR2EVESiICEMbg137etN/Lx+lSrHPTD39Z/uE+2s= +github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= +github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +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/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.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.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +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/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034= +github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= +github.com/opencontainers/runc v1.1.3 h1:vIXrkId+0/J2Ymu2m7VjGvbSlAId9XNRPhn2p4b+d8w= +github.com/opencontainers/runc v1.1.3/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg= +github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= +github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= +github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= +github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= +github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/ory/dockertest v3.3.5+incompatible h1:iLLK6SQwIhcbrG783Dghaaa3WPzGc+4Emza6EbVUUGA= +github.com/ory/dockertest v3.3.5+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs= +github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= +github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= +github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= +github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o= +github.com/petermattis/goid v0.0.0-20231207134359-e60b3f734c67 h1:jik8PHtAIsPlCRJjJzl4udgEf7hawInF9texMeO2jrU= +github.com/petermattis/goid v0.0.0-20231207134359-e60b3f734c67/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= +github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/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/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= +github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU= +github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/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.1.0/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.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= +github.com/prometheus/common v0.52.2 h1:LW8Vk7BccEdONfrJBDffQGRtpSzi5CQaRZGtboOO2ck= +github.com/prometheus/common v0.52.2/go.mod h1:lrWtQx+iDfn2mbH5GUzlH9TSHyfZpHkSiG1W7y3sF2Q= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/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.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.3.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.13.0 h1:GqzLlQyfsPbaEHaQkO7tbDlriv/4o5Hudv6OXHGKX7o= +github.com/prometheus/procfs v0.13.0/go.mod h1:cd4PFCR54QLnGKPaKGA6l+cfuNXtht43ZKY6tow0Y1g= +github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= +github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.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/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/rs/cors v1.8.3 h1:O+qNyWn7Z+F9M0ILBHgMVPuB1xTOucVd5gtaYyXBpRo= +github.com/rs/cors v1.8.3/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.32.0 h1:keLypqrlIjaFsbmJOBdB/qvyF8KEtCWHwobLp5l/mQ0= +github.com/rs/zerolog v1.32.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= +github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= +github.com/sasha-s/go-deadlock v0.3.1 h1:sqv7fDNShgjcaxkO0JNcOAlr8B9+cV5Ey/OB71efZx0= +github.com/sasha-s/go-deadlock v0.3.1/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= +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/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +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.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= +github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= +github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= +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.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/subosito/gotenv v1.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/tidwall/gjson v1.10.2 h1:APbLGOM0rrEkd8WBw9C24nllro4ajFuJu0Sc9hRz8Bo= +github.com/tidwall/gjson v1.10.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/tinylru v1.1.0 h1:XY6IUfzVTU9rpwdhKUF6nQdChgCdGjkMfLzbWyiau6I= +github.com/tidwall/tinylru v1.1.0/go.mod h1:3+bX+TJ2baOLMWTnlyNWHh4QMnFyARg2TLTQ6OFbzw8= +github.com/tidwall/wal v1.1.7 h1:emc1TRjIVsdKKSnpwGBAcsAGg0767SvUk8+ygx7Bb+4= +github.com/tidwall/wal v1.1.7/go.mod h1:r6lR1j27W9EPalgHiB7zLJDYu3mzW5BQP5KrzBpYY/E= +github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= +github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/zbiljic/go-filelock v0.0.0-20170914061330-1dbf7103ab7d h1:XQyeLr7N9iY9mi+TGgsBFkj54+j3fdoo8e2u6zrGP5A= +github.com/zbiljic/go-filelock v0.0.0-20170914061330-1dbf7103ab7d/go.mod h1:hoMeDjlNXTNqVwrCk8YDyaBS2g5vFfEX2ezMi4vb6CY= +github.com/zondax/hid v0.9.2 h1:WCJFnEDMiqGF64nlZz28E9qLVZ0KSJ7xpc5DLEyma2U= +github.com/zondax/hid v0.9.2/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= +github.com/zondax/ledger-go v0.14.3 h1:wEpJt2CEcBJ428md/5MgSLsXLBos98sBOyxNmCjfUCw= +github.com/zondax/ledger-go v0.14.3/go.mod h1:IKKaoxupuB43g4NxeQmbLXv7T9AlQyie1UpHb342ycI= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA= +go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= +go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= +go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= +go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/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-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= +golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= +golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0 h1:985EYyeCOxTpcgOTJpflJUwOeEz0CQOdPt73OzpE9F8= +golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/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-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-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-20190813141303-74dc4d7220e7/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-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-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.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/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-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/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-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-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-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210819135213-f52c844e1c1c/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-20211025201205-69cdffdb9359/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.0.0-20221010170243-090e33056c14/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.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= +golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.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.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-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-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-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.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY= +golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.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-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +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-20210126160654-44e461bb6506/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +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-20240227224415-6ceb2ff114de h1:jFNzHPIeuzhdRwVhbZdiym9q0ory/xY3sA+v2wPg8I0= +google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:5iCWqnniDlqZHrd3neWVTOwvh/v6s3232omMecelax8= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda h1:LI5DOvAxUPMv/50agcLLoo+AdWc1irS9Rzz4vPuD1V4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.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.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= +google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.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.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +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-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +nhooyr.io/websocket v1.8.6 h1:s+C3xAMLwGmlI31Nyn/eAehUlZPwfYZu2JXM621Q5/k= +nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= +pgregory.net/rapid v1.1.0 h1:CMa0sjHSru3puNx+J0MIAuiiEV4N0qj8/cMWGBBCsjw= +pgregory.net/rapid v1.1.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= +sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= diff --git a/versiondb/multistore.go b/versiondb/multistore.go new file mode 100644 index 0000000..a88850d --- /dev/null +++ b/versiondb/multistore.go @@ -0,0 +1,159 @@ +package versiondb + +import ( + "fmt" + "io" + "sync" + + "cosmossdk.io/store/cachemulti" + "cosmossdk.io/store/types" +) + +var _ types.MultiStore = (*MultiStore)(nil) + +// MultiStore wraps `VersionStore` to implement `MultiStore` interface. +type MultiStore struct { + versionDB VersionStore + stores map[types.StoreKey]types.KVStore + + // transient/memory/object stores, they are delegated to the parent + delegatedStoreKeys map[types.StoreKey]struct{} + + // proxy the calls for transient or mem stores to the parent + parent types.MultiStore + + traceWriter io.Writer + traceContext types.TraceContext + traceContextMutex sync.Mutex +} + +// NewMultiStore returns a new versiondb `MultiStore`. +func NewMultiStore( + parent types.MultiStore, + versionDB VersionStore, + storeKeys map[string]*types.KVStoreKey, + delegatedStoreKeys map[types.StoreKey]struct{}, +) *MultiStore { + stores := make(map[types.StoreKey]types.KVStore, len(storeKeys)) + for _, k := range storeKeys { + stores[k] = NewKVStore(versionDB, k, nil) + } + return &MultiStore{ + versionDB: versionDB, + stores: stores, + parent: parent, + delegatedStoreKeys: delegatedStoreKeys, + } +} + +// GetStoreType implements `MultiStore` interface. +func (s *MultiStore) GetStoreType() types.StoreType { + return types.StoreTypeMulti +} + +// cacheMultiStore branch out the multistore. +func (s *MultiStore) cacheMultiStore(version *int64) types.CacheMultiStore { + stores := make(map[types.StoreKey]types.CacheWrapper, len(s.delegatedStoreKeys)+len(s.stores)) + for k := range s.delegatedStoreKeys { + stores[k] = types.CacheWrapper(s.parent.GetStore(k)) + } + for k := range s.stores { + if version == nil { + stores[k] = s.stores[k] + } else { + stores[k] = NewKVStore(s.versionDB, k, version) + } + } + return cachemulti.NewStore(nil, stores, nil, s.traceWriter, s.getTracingContext()) +} + +// CacheMultiStore implements `MultiStore` interface +func (s *MultiStore) CacheMultiStore() types.CacheMultiStore { + return s.cacheMultiStore(nil) +} + +// CacheMultiStoreWithVersion implements `MultiStore` interface +func (s *MultiStore) CacheMultiStoreWithVersion(version int64) (types.CacheMultiStore, error) { + return s.cacheMultiStore(&version), nil +} + +// CacheWrap implements CacheWrapper/MultiStore/CommitStore. +func (s *MultiStore) CacheWrap() types.CacheWrap { + return s.CacheMultiStore().(types.CacheWrap) +} + +// GetStore implements `MultiStore` interface +func (s *MultiStore) GetStore(storeKey types.StoreKey) types.Store { + if store, ok := s.stores[storeKey]; ok { + return store + } + if _, ok := s.delegatedStoreKeys[storeKey]; ok { + // delegate the transient/memory/object stores to real cms + return s.parent.GetStore(storeKey) + } + panic(fmt.Errorf("store key %s is not registered", storeKey.Name())) +} + +// GetKVStore implements `MultiStore` interface +func (s *MultiStore) GetKVStore(storeKey types.StoreKey) types.KVStore { + return s.GetStore(storeKey).(types.KVStore) +} + +// SetTracer sets the tracer for the MultiStore that the underlying +// stores will utilize to trace operations. A MultiStore is returned. +func (s *MultiStore) SetTracer(w io.Writer) types.MultiStore { + s.traceWriter = w + return s +} + +// SetTracingContext updates the tracing context for the MultiStore by merging +// the given context with the existing context by key. Any existing keys will +// be overwritten. It is implied that the caller should update the context when +// necessary between tracing operations. It returns a modified MultiStore. +func (s *MultiStore) SetTracingContext(tc types.TraceContext) types.MultiStore { + s.traceContextMutex.Lock() + defer s.traceContextMutex.Unlock() + s.traceContext = s.traceContext.Merge(tc) + + return s +} + +func (s *MultiStore) getTracingContext() types.TraceContext { + s.traceContextMutex.Lock() + defer s.traceContextMutex.Unlock() + + if s.traceContext == nil { + return nil + } + + ctx := types.TraceContext{} + for k, v := range s.traceContext { + ctx[k] = v + } + + return ctx +} + +// TracingEnabled returns if tracing is enabled for the MultiStore. +func (s *MultiStore) TracingEnabled() bool { + return s.traceWriter != nil +} + +// LatestVersion returns the latest version saved in versiondb +func (s *MultiStore) LatestVersion() int64 { + version, err := s.versionDB.GetLatestVersion() + if err != nil { + panic(err) + } + return version +} + +// Close will flush the versiondb +func (s *MultiStore) Close() error { + return s.versionDB.Flush() +} + +// CacheWrapWithTrace is kept to build with upstream sdk. +func (s *MultiStore) CacheWrapWithTrace(w io.Writer, tc types.TraceContext) types.CacheWrap { + panic("not implemented") +} diff --git a/versiondb/objstore.go b/versiondb/objstore.go new file mode 100644 index 0000000..0961a8f --- /dev/null +++ b/versiondb/objstore.go @@ -0,0 +1,11 @@ +//go:build objstore +// +build objstore + +package versiondb + +import "cosmossdk.io/store/types" + +// GetObjKVStore implements `MultiStore` interface +func (s *MultiStore) GetObjKVStore(storeKey types.StoreKey) types.ObjKVStore { + return s.GetStore(storeKey).(types.ObjKVStore) +} diff --git a/versiondb/store.go b/versiondb/store.go new file mode 100644 index 0000000..866b027 --- /dev/null +++ b/versiondb/store.go @@ -0,0 +1,88 @@ +package versiondb + +import ( + "io" + "time" + + "cosmossdk.io/store/cachekv" + "cosmossdk.io/store/types" + "github.com/cosmos/cosmos-sdk/telemetry" +) + +const StoreTypeVersionDB = 100 + +var _ types.KVStore = (*Store)(nil) + +// Store Implements types.KVStore +type Store struct { + store VersionStore + storeKey types.StoreKey + version *int64 +} + +func NewKVStore(store VersionStore, storeKey types.StoreKey, version *int64) *Store { + return &Store{store, storeKey, version} +} + +// Implements Store. +func (st *Store) GetStoreType() types.StoreType { + // should have effect, just define an unique indentifier, don't be conflicts with cosmos-sdk's builtin ones. + return StoreTypeVersionDB +} + +// Implements Store. +func (st *Store) CacheWrap() types.CacheWrap { + return cachekv.NewStore(st) +} + +// Implements types.KVStore. +func (st *Store) Get(key []byte) []byte { + defer telemetry.MeasureSince(time.Now(), "store", "versiondb", "get") + value, err := st.store.GetAtVersion(st.storeKey.Name(), key, st.version) + if err != nil { + panic(err) + } + return value +} + +// Implements types.KVStore. +func (st *Store) Has(key []byte) (exists bool) { + defer telemetry.MeasureSince(time.Now(), "store", "versiondb", "has") + has, err := st.store.HasAtVersion(st.storeKey.Name(), key, st.version) + if err != nil { + panic(err) + } + return has +} + +// Implements types.KVStore. +func (st *Store) Iterator(start, end []byte) types.Iterator { + itr, err := st.store.IteratorAtVersion(st.storeKey.Name(), start, end, st.version) + if err != nil { + panic(err) + } + return itr +} + +// Implements types.KVStore. +func (st *Store) ReverseIterator(start, end []byte) types.Iterator { + itr, err := st.store.ReverseIteratorAtVersion(st.storeKey.Name(), start, end, st.version) + if err != nil { + panic(err) + } + return itr +} + +// Implements types.KVStore. +func (st *Store) Set(key, value []byte) { + panic("write operation is not supported") +} + +// Implements types.KVStore. +func (st *Store) Delete(key []byte) { + panic("write operation is not supported") +} + +func (st *Store) CacheWrapWithTrace(w io.Writer, tc types.TraceContext) types.CacheWrap { + panic("not implemented") +} diff --git a/versiondb/streaming_service.go b/versiondb/streaming_service.go new file mode 100644 index 0000000..e1a8f4e --- /dev/null +++ b/versiondb/streaming_service.go @@ -0,0 +1,33 @@ +package versiondb + +import ( + "context" + + abci "github.com/cometbft/cometbft/abci/types" + + "cosmossdk.io/store/types" +) + +var _ types.ABCIListener = &StreamingService{} + +// StreamingService is a concrete implementation of StreamingService that accumulate the state changes in current block, +// writes the ordered changeset out to version storage. +type StreamingService struct { + versionStore VersionStore + currentBlockNumber int64 // the current block number +} + +// NewStreamingService creates a new StreamingService for the provided writeDir, (optional) filePrefix, and storeKeys +func NewStreamingService(versionStore VersionStore) *StreamingService { + return &StreamingService{versionStore: versionStore} +} + +// ListenFinalizeBlock satisfies the types.ABCIListener interface +func (fss *StreamingService) ListenFinalizeBlock(ctx context.Context, req abci.RequestFinalizeBlock, res abci.ResponseFinalizeBlock) error { + fss.currentBlockNumber = req.Height + return nil +} + +func (fss *StreamingService) ListenCommit(ctx context.Context, res abci.ResponseCommit, changeSet []*types.StoreKVPair) error { + return fss.versionStore.PutAtVersion(fss.currentBlockNumber, changeSet) +} diff --git a/versiondb/tsrocksdb/comparator.go b/versiondb/tsrocksdb/comparator.go new file mode 100644 index 0000000..815c73f --- /dev/null +++ b/versiondb/tsrocksdb/comparator.go @@ -0,0 +1,59 @@ +package tsrocksdb + +import ( + "bytes" + "encoding/binary" + + "github.com/linxGnu/grocksdb" +) + +// CreateTSComparator should behavior identical with rocksdb builtin timestamp comparator. +// we also use the same builtin comparator name so the builtin tools `ldb`/`sst_dump` can work with the database. +func CreateTSComparator() *grocksdb.Comparator { + return grocksdb.NewComparatorWithTimestamp( + "leveldb.BytewiseComparator.u64ts", TimestampSize, compare, compareTS, compareWithoutTS, + ) +} + +// compareTS compares timestamp as little endian encoded integers. +// +// NOTICE: the behavior must be identical to rocksdb builtin comparator "leveldb.BytewiseComparator.u64ts". +func compareTS(bz1 []byte, bz2 []byte) int { + ts1 := binary.LittleEndian.Uint64(bz1) + ts2 := binary.LittleEndian.Uint64(bz2) + switch { + case ts1 < ts2: + return -1 + case ts1 > ts2: + return 1 + default: + return 0 + } +} + +// compare compares two internal keys with timestamp surfix, larger timestamp comes first. +// +// NOTICE: the behavior must be identical to rocksdb builtin comparator "leveldb.BytewiseComparator.u64ts". +func compare(a []byte, b []byte) int { + ret := compareWithoutTS(a, true, b, true) + if ret != 0 { + return ret + } + // Compare timestamp. + // For the same user key with different timestamps, larger (newer) timestamp + // comes first, which means seek operation will try to find a version less than or equal to the target version. + return -compareTS(a[len(a)-TimestampSize:], b[len(b)-TimestampSize:]) +} + +// compareWithoutTS compares two internal keys without the timestamp part +// +// NOTICE: the behavior must be identical to rocksdb builtin comparator "leveldb.BytewiseComparator.u64ts". +func compareWithoutTS(a []byte, aHasTS bool, b []byte, bHasTS bool) int { + if aHasTS { + a = a[:len(a)-TimestampSize] + } + if bHasTS { + b = b[:len(b)-TimestampSize] + } + return bytes.Compare(a, b) +} diff --git a/versiondb/tsrocksdb/iterator.go b/versiondb/tsrocksdb/iterator.go new file mode 100644 index 0000000..02065a9 --- /dev/null +++ b/versiondb/tsrocksdb/iterator.go @@ -0,0 +1,147 @@ +package tsrocksdb + +import ( + "bytes" + + "cosmossdk.io/store/types" + "github.com/linxGnu/grocksdb" +) + +type rocksDBIterator struct { + source *grocksdb.Iterator + prefix, start, end []byte + isReverse bool + isInvalid bool +} + +var _ types.Iterator = (*rocksDBIterator)(nil) + +func newRocksDBIterator(source *grocksdb.Iterator, prefix, start, end []byte, isReverse bool) *rocksDBIterator { + if isReverse { + if end == nil { + source.SeekToLast() + } else { + source.Seek(end) + if source.Valid() { + eoakey := source.Key() // end or after key + defer eoakey.Free() + if bytes.Compare(end, eoakey.Data()) <= 0 { + source.Prev() + } + } else { + source.SeekToLast() + } + } + } else { + if start == nil { + source.SeekToFirst() + } else { + source.Seek(start) + } + } + return &rocksDBIterator{ + source: source, + prefix: prefix, + start: start, + end: end, + isReverse: isReverse, + isInvalid: false, + } +} + +// Domain implements Iterator. +func (itr *rocksDBIterator) Domain() ([]byte, []byte) { + return itr.start, itr.end +} + +// Valid implements Iterator. +func (itr *rocksDBIterator) Valid() bool { + // Once invalid, forever invalid. + if itr.isInvalid { + return false + } + + // If source has error, invalid. + if err := itr.source.Err(); err != nil { + itr.isInvalid = true + return false + } + + // If source is invalid, invalid. + if !itr.source.Valid() { + itr.isInvalid = true + return false + } + + // If key is end or past it, invalid. + start := itr.start + end := itr.end + key := itr.source.Key() + defer key.Free() + if itr.isReverse { + if start != nil && bytes.Compare(key.Data(), start) < 0 { + itr.isInvalid = true + return false + } + } else { + if end != nil && bytes.Compare(end, key.Data()) <= 0 { + itr.isInvalid = true + return false + } + } + + // It's valid. + return true +} + +// Key implements Iterator. +func (itr *rocksDBIterator) Key() []byte { + itr.assertIsValid() + return moveSliceToBytes(itr.source.Key())[len(itr.prefix):] +} + +// Value implements Iterator. +func (itr *rocksDBIterator) Value() []byte { + itr.assertIsValid() + return moveSliceToBytes(itr.source.Value()) +} + +// Next implements Iterator. +func (itr rocksDBIterator) Next() { + itr.assertIsValid() + if itr.isReverse { + itr.source.Prev() + } else { + itr.source.Next() + } +} + +// Error implements Iterator. +func (itr *rocksDBIterator) Error() error { + return itr.source.Err() +} + +// Close implements Iterator. +func (itr *rocksDBIterator) Close() error { + itr.source.Close() + return nil +} + +func (itr *rocksDBIterator) assertIsValid() { + if !itr.Valid() { + panic("iterator is invalid") + } +} + +// moveSliceToBytes will free the slice and copy out a go []byte +// This function can be applied on *Slice returned from Key() and Value() +// of an Iterator, because they are marked as freed. +func moveSliceToBytes(s *grocksdb.Slice) []byte { + defer s.Free() + if !s.Exists() { + return nil + } + v := make([]byte, len(s.Data())) + copy(v, s.Data()) + return v +} diff --git a/versiondb/tsrocksdb/opts.go b/versiondb/tsrocksdb/opts.go new file mode 100644 index 0000000..604c203 --- /dev/null +++ b/versiondb/tsrocksdb/opts.go @@ -0,0 +1,85 @@ +package tsrocksdb + +import ( + "encoding/binary" + "runtime" + + "github.com/linxGnu/grocksdb" +) + +const VersionDBCFName = "versiondb" + +// NewVersionDBOpts returns the options used for the versiondb column family. +// FIXME: we don't enable dict compression for SSTFileWriter, because otherwise the file writer won't report correct file size. +// https://github.com/facebook/rocksdb/issues/11146 +func NewVersionDBOpts(sstFileWriter bool) *grocksdb.Options { + opts := grocksdb.NewDefaultOptions() + opts.SetCreateIfMissing(true) + opts.SetComparator(CreateTSComparator()) + opts.IncreaseParallelism(runtime.NumCPU()) + opts.OptimizeLevelStyleCompaction(512 * 1024 * 1024) + opts.SetTargetFileSizeMultiplier(2) + opts.SetLevelCompactionDynamicLevelBytes(true) + + // block based table options + bbto := grocksdb.NewDefaultBlockBasedTableOptions() + + // 1G block cache + bbto.SetBlockSize(32 * 1024) + bbto.SetBlockCache(grocksdb.NewLRUCache(1 << 30)) + + bbto.SetFilterPolicy(grocksdb.NewRibbonHybridFilterPolicy(9.9, 1)) + bbto.SetIndexType(grocksdb.KBinarySearchWithFirstKey) + bbto.SetOptimizeFiltersForMemory(true) + opts.SetBlockBasedTableFactory(bbto) + // improve sst file creation speed: compaction or sst file writer. + opts.SetCompressionOptionsParallelThreads(4) + + if !sstFileWriter { + // compression options at bottommost level + opts.SetBottommostCompression(grocksdb.ZSTDCompression) + compressOpts := grocksdb.NewDefaultCompressionOptions() + compressOpts.MaxDictBytes = 112640 // 110k + compressOpts.Level = 12 + opts.SetBottommostCompressionOptions(compressOpts, true) + opts.SetBottommostCompressionOptionsZstdMaxTrainBytes(compressOpts.MaxDictBytes*100, true) + } + return opts +} + +// OpenVersionDB opens versiondb, the default column family is used for metadata, +// actually key-value pairs are stored on another column family named with "versiondb", +// which has user-defined timestamp enabled. +func OpenVersionDB(dir string) (*grocksdb.DB, *grocksdb.ColumnFamilyHandle, error) { + opts := grocksdb.NewDefaultOptions() + opts.SetCreateIfMissing(true) + opts.SetCreateIfMissingColumnFamilies(true) + db, cfHandles, err := grocksdb.OpenDbColumnFamilies( + opts, dir, []string{"default", VersionDBCFName}, + []*grocksdb.Options{opts, NewVersionDBOpts(false)}, + ) + if err != nil { + return nil, nil, err + } + return db, cfHandles[1], nil +} + +// OpenVersionDBAndTrimHistory opens versiondb similar to `OpenVersionDB`, +// but it also trim the versions newer than target one, can be used for rollback. +func OpenVersionDBAndTrimHistory(dir string, version int64) (*grocksdb.DB, *grocksdb.ColumnFamilyHandle, error) { + var ts [TimestampSize]byte + binary.LittleEndian.PutUint64(ts[:], uint64(version)) + + opts := grocksdb.NewDefaultOptions() + opts.SetCreateIfMissing(true) + opts.SetCreateIfMissingColumnFamilies(true) + db, cfHandles, err := grocksdb.OpenDbAndTrimHistory( + opts, dir, []string{"default", VersionDBCFName}, + []*grocksdb.Options{opts, NewVersionDBOpts(false)}, + ts[:], + ) + if err != nil { + return nil, nil, err + } + return db, cfHandles[1], nil +} diff --git a/versiondb/tsrocksdb/store.go b/versiondb/tsrocksdb/store.go new file mode 100644 index 0000000..44f82af --- /dev/null +++ b/versiondb/tsrocksdb/store.go @@ -0,0 +1,290 @@ +package tsrocksdb + +import ( + "encoding/binary" + "errors" + "fmt" + "math" + + "cosmossdk.io/store/types" + "github.com/cosmos/iavl" + "github.com/crypto-org-chain/cronos/versiondb" + "github.com/linxGnu/grocksdb" +) + +const ( + TimestampSize = 8 + + StorePrefixTpl = "s/k:%s/" + latestVersionKey = "s/latest" + + ImportCommitBatchSize = 10000 +) + +var ( + errKeyEmpty = errors.New("key cannot be empty") + + _ versiondb.VersionStore = Store{} + + defaultWriteOpts = grocksdb.NewDefaultWriteOptions() + defaultSyncWriteOpts = grocksdb.NewDefaultWriteOptions() + defaultReadOpts = grocksdb.NewDefaultReadOptions() +) + +func init() { + defaultSyncWriteOpts.SetSync(true) +} + +type Store struct { + db *grocksdb.DB + cfHandle *grocksdb.ColumnFamilyHandle +} + +func NewStore(dir string) (Store, error) { + db, cfHandle, err := OpenVersionDB(dir) + if err != nil { + return Store{}, err + } + return Store{ + db: db, + cfHandle: cfHandle, + }, nil +} + +func NewStoreWithDB(db *grocksdb.DB, cfHandle *grocksdb.ColumnFamilyHandle) Store { + return Store{ + db: db, + cfHandle: cfHandle, + } +} + +func (s Store) SetLatestVersion(version int64) error { + var ts [TimestampSize]byte + binary.LittleEndian.PutUint64(ts[:], uint64(version)) + return s.db.Put(defaultWriteOpts, []byte(latestVersionKey), ts[:]) +} + +// PutAtVersion implements VersionStore interface +func (s Store) PutAtVersion(version int64, changeSet []*types.StoreKVPair) error { + var ts [TimestampSize]byte + binary.LittleEndian.PutUint64(ts[:], uint64(version)) + + batch := grocksdb.NewWriteBatch() + defer batch.Destroy() + batch.Put([]byte(latestVersionKey), ts[:]) + + for _, pair := range changeSet { + key := prependStoreKey(pair.StoreKey, pair.Key) + if pair.Delete { + batch.DeleteCFWithTS(s.cfHandle, key, ts[:]) + } else { + batch.PutCFWithTS(s.cfHandle, key, ts[:], pair.Value) + } + } + + return s.db.Write(defaultSyncWriteOpts, batch) +} + +func (s Store) GetAtVersionSlice(storeKey string, key []byte, version *int64) (*grocksdb.Slice, error) { + return s.db.GetCF( + newTSReadOptions(version), + s.cfHandle, + prependStoreKey(storeKey, key), + ) +} + +// GetAtVersion implements VersionStore interface +func (s Store) GetAtVersion(storeKey string, key []byte, version *int64) ([]byte, error) { + slice, err := s.GetAtVersionSlice(storeKey, key, version) + if err != nil { + return nil, err + } + return moveSliceToBytes(slice), nil +} + +// HasAtVersion implements VersionStore interface +func (s Store) HasAtVersion(storeKey string, key []byte, version *int64) (bool, error) { + slice, err := s.GetAtVersionSlice(storeKey, key, version) + if err != nil { + return false, err + } + defer slice.Free() + return slice.Exists(), nil +} + +// GetLatestVersion returns the latest version stored in plain state, +// it's committed after the changesets, so the data for this version is guaranteed to be persisted. +// returns -1 if the key don't exists. +func (s Store) GetLatestVersion() (int64, error) { + bz, err := s.db.GetBytes(defaultReadOpts, []byte(latestVersionKey)) + if err != nil { + return 0, err + } + if len(bz) == 0 { + return 0, nil + } + return int64(binary.LittleEndian.Uint64(bz)), nil +} + +// IteratorAtVersion implements VersionStore interface +func (s Store) IteratorAtVersion(storeKey string, start, end []byte, version *int64) (types.Iterator, error) { + if (start != nil && len(start) == 0) || (end != nil && len(end) == 0) { + return nil, errKeyEmpty + } + + prefix := storePrefix(storeKey) + start, end = iterateWithPrefix(prefix, start, end) + + itr := s.db.NewIteratorCF(newTSReadOptions(version), s.cfHandle) + return newRocksDBIterator(itr, prefix, start, end, false), nil +} + +// ReverseIteratorAtVersion implements VersionStore interface +func (s Store) ReverseIteratorAtVersion(storeKey string, start, end []byte, version *int64) (types.Iterator, error) { + if (start != nil && len(start) == 0) || (end != nil && len(end) == 0) { + return nil, errKeyEmpty + } + + prefix := storePrefix(storeKey) + start, end = iterateWithPrefix(storePrefix(storeKey), start, end) + + itr := s.db.NewIteratorCF(newTSReadOptions(version), s.cfHandle) + return newRocksDBIterator(itr, prefix, start, end, true), nil +} + +// FeedChangeSet is used to migrate legacy change sets into versiondb +func (s Store) FeedChangeSet(version int64, store string, changeSet *iavl.ChangeSet) error { + var ts [TimestampSize]byte + binary.LittleEndian.PutUint64(ts[:], uint64(version)) + + prefix := storePrefix(store) + + batch := grocksdb.NewWriteBatch() + defer batch.Destroy() + + for _, pair := range changeSet.Pairs { + key := cloneAppend(prefix, pair.Key) + + if pair.Delete { + batch.DeleteCFWithTS(s.cfHandle, key, ts[:]) + } else { + batch.PutCFWithTS(s.cfHandle, key, ts[:], pair.Value) + } + } + + return s.db.Write(defaultWriteOpts, batch) +} + +// Import loads the initial version of the state +func (s Store) Import(version int64, ch <-chan versiondb.ImportEntry) error { + batch := grocksdb.NewWriteBatch() + defer batch.Destroy() + + var ts [TimestampSize]byte + binary.LittleEndian.PutUint64(ts[:], uint64(version)) + + var counter int + for entry := range ch { + key := cloneAppend(storePrefix(entry.StoreKey), entry.Key) + batch.PutCFWithTS(s.cfHandle, key, ts[:], entry.Value) + + counter++ + if counter%ImportCommitBatchSize == 0 { + if err := s.db.Write(defaultWriteOpts, batch); err != nil { + return err + } + batch.Clear() + } + } + + if batch.Count() > 0 { + if err := s.db.Write(defaultWriteOpts, batch); err != nil { + return err + } + } + + return s.SetLatestVersion(version) +} + +func (s Store) Flush() error { + opts := grocksdb.NewDefaultFlushOptions() + defer opts.Destroy() + + return errors.Join( + s.db.Flush(opts), + s.db.FlushCF(s.cfHandle, opts), + ) +} + +func newTSReadOptions(version *int64) *grocksdb.ReadOptions { + var ver uint64 + if version == nil { + ver = math.MaxUint64 + } else { + ver = uint64(*version) + } + + var ts [TimestampSize]byte + binary.LittleEndian.PutUint64(ts[:], ver) + + readOpts := grocksdb.NewDefaultReadOptions() + readOpts.SetTimestamp(ts[:]) + return readOpts +} + +func storePrefix(storeKey string) []byte { + return []byte(fmt.Sprintf(StorePrefixTpl, storeKey)) +} + +// prependStoreKey prepends storeKey to the key +func prependStoreKey(storeKey string, key []byte) []byte { + return append(storePrefix(storeKey), key...) +} + +func cloneAppend(bz []byte, tail []byte) (res []byte) { + res = make([]byte, len(bz)+len(tail)) + copy(res, bz) + copy(res[len(bz):], tail) + return +} + +// Returns a slice of the same length (big endian) +// except incremented by one. +// Returns nil on overflow (e.g. if bz bytes are all 0xFF) +// CONTRACT: len(bz) > 0 +func cpIncr(bz []byte) (ret []byte) { + if len(bz) == 0 { + panic("cpIncr expects non-zero bz length") + } + ret = make([]byte, len(bz)) + copy(ret, bz) + for i := len(bz) - 1; i >= 0; i-- { + if ret[i] < byte(0xFF) { + ret[i]++ + return + } + ret[i] = byte(0x00) + if i == 0 { + // Overflow + return nil + } + } + return nil +} + +// iterateWithPrefix calculate the acual iterate range +func iterateWithPrefix(prefix, begin, end []byte) ([]byte, []byte) { + if len(prefix) == 0 { + return begin, end + } + + begin = cloneAppend(prefix, begin) + + if end == nil { + end = cpIncr(prefix) + } else { + end = cloneAppend(prefix, end) + } + + return begin, end +} diff --git a/versiondb/tsrocksdb/store_test.go b/versiondb/tsrocksdb/store_test.go new file mode 100644 index 0000000..a532897 --- /dev/null +++ b/versiondb/tsrocksdb/store_test.go @@ -0,0 +1,155 @@ +package tsrocksdb + +import ( + "encoding/binary" + "testing" + + "github.com/crypto-org-chain/cronos/versiondb" + "github.com/linxGnu/grocksdb" + "github.com/stretchr/testify/require" +) + +func TestTSVersionDB(t *testing.T) { + versiondb.Run(t, func() versiondb.VersionStore { + store, err := NewStore(t.TempDir()) + require.NoError(t, err) + return store + }) +} + +// TestUserTimestamp tests the behaviors of user-defined timestamp feature of rocksdb +func TestUserTimestampBasic(t *testing.T) { + key := []byte("hello") + writeOpts := grocksdb.NewDefaultWriteOptions() + + db, cfHandle, err := OpenVersionDB(t.TempDir()) + require.NoError(t, err) + + var ts [8]byte + binary.LittleEndian.PutUint64(ts[:], 1000) + + err = db.PutCFWithTS(writeOpts, cfHandle, key, ts[:], []byte{1}) + require.NoError(t, err) + err = db.PutCFWithTS(writeOpts, cfHandle, []byte("zempty"), ts[:], []byte{}) + require.NoError(t, err) + + // key don't exists in older version + v := int64(999) + bz, err := db.GetCF(newTSReadOptions(&v), cfHandle, key) + require.NoError(t, err) + require.False(t, bz.Exists()) + bz.Free() + + // key exists in latest version + bz, err = db.GetCF(newTSReadOptions(nil), cfHandle, key) + require.NoError(t, err) + require.Equal(t, []byte{1}, bz.Data()) + bz.Free() + + // iterator can find the key in right version + v = int64(1000) + it := db.NewIteratorCF(newTSReadOptions(&v), cfHandle) + it.SeekToFirst() + require.True(t, it.Valid()) + bz = it.Key() + require.Equal(t, key, bz.Data()) + bz.Free() + + // key exists in right version, and empty value is supported + bz, err = db.GetCF(newTSReadOptions(&v), cfHandle, []byte("zempty")) + require.NoError(t, err) + require.Equal(t, []byte{}, bz.Data()) + bz.Free() + + binary.LittleEndian.PutUint64(ts[:], 1002) + err = db.PutCFWithTS(writeOpts, cfHandle, []byte("hella"), ts[:], []byte{2}) + require.NoError(t, err) + + // iterator can find keys from both versions + v = int64(1002) + it = db.NewIteratorCF(newTSReadOptions(&v), cfHandle) + it.SeekToFirst() + require.True(t, it.Valid()) + bz = it.Key() + require.Equal(t, []byte("hella"), bz.Data()) + bz.Free() + + it.Next() + require.True(t, it.Valid()) + bz = it.Key() + require.Equal(t, key, bz.Data()) + bz.Free() + + for i := 1; i < 100; i++ { + binary.LittleEndian.PutUint64(ts[:], uint64(i)) + err := db.PutCFWithTS(writeOpts, cfHandle, key, ts[:], []byte{byte(i)}) + require.NoError(t, err) + } + + for i := int64(1); i < 100; i++ { + binary.LittleEndian.PutUint64(ts[:], uint64(i)) + bz, err := db.GetCF(newTSReadOptions(&i), cfHandle, key) + require.NoError(t, err) + require.Equal(t, []byte{byte(i)}, bz.Data()) + bz.Free() + } +} + +func TestUserTimestampPruning(t *testing.T) { + key := []byte("hello") + writeOpts := grocksdb.NewDefaultWriteOptions() + + dir := t.TempDir() + db, cfHandle, err := OpenVersionDB(dir) + require.NoError(t, err) + + var ts [TimestampSize]byte + for _, i := range []uint64{1, 100, 200} { + binary.LittleEndian.PutUint64(ts[:], i) + err := db.PutCFWithTS(writeOpts, cfHandle, key, ts[:], []byte{byte(i)}) + require.NoError(t, err) + } + + i := int64(49) + + bz, err := db.GetCF(newTSReadOptions(&i), cfHandle, key) + require.NoError(t, err) + require.True(t, bz.Exists()) + bz.Free() + + // prune old versions + binary.LittleEndian.PutUint64(ts[:], 50) + compactOpts := grocksdb.NewCompactRangeOptions() + compactOpts.SetFullHistoryTsLow(ts[:]) + db.CompactRangeCFOpt(cfHandle, grocksdb.Range{}, compactOpts) + + // queries for versions older than 50 are not allowed + _, err = db.GetCF(newTSReadOptions(&i), cfHandle, key) + require.Error(t, err) + + // the value previously at version 1 is still there + i = 50 + bz, err = db.GetCF(newTSReadOptions(&i), cfHandle, key) + require.NoError(t, err) + require.True(t, bz.Exists()) + require.Equal(t, []byte{1}, bz.Data()) + bz.Free() + + i = 200 + bz, err = db.GetCF(newTSReadOptions(&i), cfHandle, key) + require.NoError(t, err) + require.Equal(t, []byte{200}, bz.Data()) + bz.Free() + + // reopen db and trim version 200 + cfHandle.Destroy() + db.Close() + db, cfHandle, err = OpenVersionDBAndTrimHistory(dir, 199) + require.NoError(t, err) + + // the version 200 is gone, 100 is the latest value + bz, err = db.GetCF(newTSReadOptions(&i), cfHandle, key) + require.NoError(t, err) + require.Equal(t, []byte{100}, bz.Data()) + bz.Free() +} diff --git a/versiondb/types.go b/versiondb/types.go new file mode 100644 index 0000000..b3940bd --- /dev/null +++ b/versiondb/types.go @@ -0,0 +1,34 @@ +package versiondb + +import ( + "cosmossdk.io/store/types" +) + +// VersionStore is a versioned storage of a flat key-value pairs. +// it don't need to support merkle proof, so could be implemented in a much more efficient way. +// `nil` version means the latest version. +type VersionStore interface { + GetAtVersion(storeKey string, key []byte, version *int64) ([]byte, error) + HasAtVersion(storeKey string, key []byte, version *int64) (bool, error) + IteratorAtVersion(storeKey string, start, end []byte, version *int64) (types.Iterator, error) + ReverseIteratorAtVersion(storeKey string, start, end []byte, version *int64) (types.Iterator, error) + GetLatestVersion() (int64, error) + + // Persist the change set of a block, + // the `changeSet` should be ordered by (storeKey, key), + // the version should be latest version plus one. + PutAtVersion(version int64, changeSet []*types.StoreKVPair) error + + // Import the initial state of the store + Import(version int64, ch <-chan ImportEntry) error + + // Flush wal logs, and make the changes persistent, + // mainly for rocksdb version upgrade, sometimes the wal format is not compatible. + Flush() error +} + +type ImportEntry struct { + StoreKey string + Key []byte + Value []byte +} From e8a6250b8584176ddb25667f009a7072d88e5671 Mon Sep 17 00:00:00 2001 From: valli0x Date: Wed, 23 Oct 2024 00:13:25 +0300 Subject: [PATCH 04/20] added pending tx func for websocket work --- app/app.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/app/app.go b/app/app.go index 8f9bd40..d123fdd 100644 --- a/app/app.go +++ b/app/app.go @@ -43,6 +43,7 @@ import ( upgradekeeper "cosmossdk.io/x/upgrade/keeper" abci "github.com/cometbft/cometbft/abci/types" "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/ethereum/go-ethereum/common" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" @@ -138,6 +139,7 @@ import ( icatypes "github.com/cosmos/ibc-go/v8/modules/apps/27-interchain-accounts/types" memiavlstore "github.com/crypto-org-chain/cronos/store" "github.com/cosmos/cosmos-sdk/client/flags" + evmante "github.com/evmos/ethermint/app/ante" ) const ( @@ -236,6 +238,8 @@ type App struct { txConfig client.TxConfig interfaceRegistry codectypes.InterfaceRegistry + pendingTxListeners []evmante.PendingTxListener + // non depinject support modules store keys keys map[string]*storetypes.KVStoreKey okeys map[string]*storetypes.ObjectStoreKey @@ -708,6 +712,7 @@ func (app *App) setAnteHandler(txConfig client.TxConfig, maxGasWanted uint64) { sdk.MsgTypeURL(&evmtypes.MsgEthereumTx{}), sdk.MsgTypeURL(&vestingtypes.MsgCreateVestingAccount{}), }, + PendingTxListener: app.onPendingTx, }) if err != nil { panic(err) @@ -752,3 +757,14 @@ func (app *App) AutoCliOpts() autocli.AppOptions { ConsensusAddressCodec: authcodec.NewBech32Codec(sdk.GetConfig().GetBech32ConsensusAddrPrefix()), } } + +// RegisterPendingTxListener is used by json-rpc server to listen to pending transactions callback. +func (app *App) RegisterPendingTxListener(listener evmante.PendingTxListener) { + app.pendingTxListeners = append(app.pendingTxListeners, listener) +} + +func (app *App) onPendingTx(hash common.Hash) { + for _, listener := range app.pendingTxListeners { + listener(hash) + } +} \ No newline at end of file From 6cb8f1ff033e0df11f5def6827a62abfa4369892 Mon Sep 17 00:00:00 2001 From: valli0x Date: Fri, 25 Oct 2024 12:06:56 +0300 Subject: [PATCH 05/20] andromeda compatibility --- app/app.go | 3 ++- app/upgrade_v0_2_4.go | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/app/app.go b/app/app.go index d123fdd..f6dc91c 100644 --- a/app/app.go +++ b/app/app.go @@ -734,8 +734,9 @@ func initParamsKeeper( func (app *App) applyUpgrades() { app.applyUpgrade_v0_1_2() + app.applyUpgrade_v0_2_2() app.applyUpgrade_v0_2_4() - // app.applyUpgrade_v0_2_3() + app.applyUpgrade_v0_2_7() } // AutoCliOpts returns the autocli options for the app. diff --git a/app/upgrade_v0_2_4.go b/app/upgrade_v0_2_4.go index dc60433..4f48caa 100644 --- a/app/upgrade_v0_2_4.go +++ b/app/upgrade_v0_2_4.go @@ -33,3 +33,29 @@ func (app *App) upgradeHandler_v0_2_4() func(ctx context.Context, _ upgradetypes return vm, err } } + +// for andromeda +const ( + planName_0_2_2 = "0.2.2" +) + +func (app *App) applyUpgrade_v0_2_2() { + app.UpgradeKeeper.SetUpgradeHandler(planName_0_2_2, app.upgradeHandler_v0_2_2()) +} + +func (app *App) upgradeHandler_v0_2_2() func(ctx context.Context, _ upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) { + return func(ctx context.Context, plan upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) { + logger := sdk.UnwrapSDKContext(ctx).Logger() + + logger.Info("Starting module migrations...") + + vm, err := app.ModuleManager.RunMigrations(ctx, app.Configurator(), fromVM) + if err != nil { + return vm, err + } + + logger.Info("Upgrade " + plan.Name + " complete") + + return vm, err + } +} From 402ff3e8ed3b8ff46c9739e19e019db065110459 Mon Sep 17 00:00:00 2001 From: valli0x Date: Fri, 25 Oct 2024 15:07:53 +0300 Subject: [PATCH 06/20] test commit --- app/upgrade_v0_2_4.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/app/upgrade_v0_2_4.go b/app/upgrade_v0_2_4.go index 4f48caa..37d847b 100644 --- a/app/upgrade_v0_2_4.go +++ b/app/upgrade_v0_2_4.go @@ -59,3 +59,20 @@ func (app *App) upgradeHandler_v0_2_2() func(ctx context.Context, _ upgradetypes return vm, err } } + + +// solve 0.1.2 update problem on andromeda +const ( + planName_0_2_7 = "0.2.7" +) + +func (app *App) applyUpgrade_v0_2_7() { + app.UpgradeKeeper.SetUpgradeHandler(planName_0_2_7, app.upgradeHandler_v0_2_7()) +} + +func (app *App) upgradeHandler_v0_2_7() func(ctx context.Context, _ upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) { + return func(ctx context.Context, plan upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) { + sdk.UnwrapSDKContext(ctx).Logger().Info("Upgrade " + plan.Name + " complete") + return fromVM, nil + } +} \ No newline at end of file From a9897a96e3632541f335107369dc18dc16aad374 Mon Sep 17 00:00:00 2001 From: Ilya Bygrimov Date: Wed, 30 Oct 2024 18:26:10 +0100 Subject: [PATCH 07/20] Async start --- localnet/init-configs-localnet.sh | 80 ++--- tests/galacli/.gitignore | 4 + tests/galacli/.python-version | 1 + tests/galacli/README.md | 0 tests/galacli/__init__.py | 0 tests/galacli/galacli.py | 511 ++++++++++++++++++++++++++++++ tests/galacli/pyproject.toml | 14 + tests/galacli/uv.lock | 196 ++++++++++++ 8 files changed, 767 insertions(+), 39 deletions(-) create mode 100644 tests/galacli/.gitignore create mode 100644 tests/galacli/.python-version create mode 100644 tests/galacli/README.md create mode 100644 tests/galacli/__init__.py create mode 100644 tests/galacli/galacli.py create mode 100644 tests/galacli/pyproject.toml create mode 100644 tests/galacli/uv.lock diff --git a/localnet/init-configs-localnet.sh b/localnet/init-configs-localnet.sh index 802541c..d8e3242 100755 --- a/localnet/init-configs-localnet.sh +++ b/localnet/init-configs-localnet.sh @@ -17,7 +17,7 @@ MAIN_PATH_HOME=${1:-"./.galactica"} MAIN_PATH_CONFIG=$MAIN_PATH_HOME/config # {identifier}_{EIP155}-{version} -CHAIN_ID=${2:-"galactica_9000-1"} +CHAIN_ID=${2:-"galactica_9302-1"} KEYRING_BACKEND=${3:-"test"} BASE_DENOM=${4:-"agnet"} DISPLAY_DENOM=${5:-"gnet"} @@ -64,34 +64,34 @@ function init_localtestnet() { function configure_app() { # Configure app settings - sed -i '/\[api\]/,+3 s/enable = false/enable = true/' $MAIN_PATH_CONFIG/app.toml - sed -i 's/address = "tcp:\/\/localhost:1317"/address = "tcp:\/\/0.0.0.0:1317"/' $MAIN_PATH_CONFIG/app.toml - # sed -i 's/enabled-unsafe-cors = false/enabled-unsafe-cors = true/' $MAIN_PATH_CONFIG/app.toml - sed -i 's/address = "localhost:9090"/address = "0.0.0.0:9090"/' $MAIN_PATH_CONFIG/app.toml - sed -i '/\[grpc-web\]/,+7 s/address = "localhost:9091"/address = "0.0.0.0:9091"/' $MAIN_PATH_CONFIG/app.toml - sed -i 's/pruning = "default"/pruning = "nothing"/g' $MAIN_PATH_CONFIG/app.toml - sed -i 's/minimum-gas-prices = "0stake"/minimum-gas-prices = "10'$BASE_DENOM'"/g' $MAIN_PATH_CONFIG/app.toml - sed -i '/\[telemetry\]/,+8 s/enabled = false/enabled = true/' $MAIN_PATH_CONFIG/app.toml - sed -i '/\[telemetry\]/,+20 s/prometheus-retention-time = 0/prometheus-retention-time = 60/' $MAIN_PATH_CONFIG/app.toml - sed -i '/global-labels = \[/a\ + sed -i.backup '/\[api\]/,+3 s/enable = false/enable = true/' $MAIN_PATH_CONFIG/app.toml + sed -i.backup 's/address = "tcp:\/\/localhost:1317"/address = "tcp:\/\/0.0.0.0:1317"/' $MAIN_PATH_CONFIG/app.toml + # sed -i.backup 's/enabled-unsafe-cors = false/enabled-unsafe-cors = true/' $MAIN_PATH_CONFIG/app.toml + sed -i.backup 's/address = "localhost:9090"/address = "0.0.0.0:9090"/' $MAIN_PATH_CONFIG/app.toml + sed -i.backup '/\[grpc-web\]/,+7 s/address = "localhost:9091"/address = "0.0.0.0:9091"/' $MAIN_PATH_CONFIG/app.toml + sed -i.backup 's/pruning = "default"/pruning = "nothing"/g' $MAIN_PATH_CONFIG/app.toml + sed -i.backup 's/minimum-gas-prices = "0stake"/minimum-gas-prices = "10'$BASE_DENOM'"/g' $MAIN_PATH_CONFIG/app.toml + sed -i.backup '/\[telemetry\]/,+8 s/enabled = false/enabled = true/' $MAIN_PATH_CONFIG/app.toml + sed -i.backup '/\[telemetry\]/,+20 s/prometheus-retention-time = 0/prometheus-retention-time = 60/' $MAIN_PATH_CONFIG/app.toml + sed -i.backup '/global-labels = \[/a\ \["chain_id", "'$CHAIN_ID'"\], ' $MAIN_PATH_CONFIG/app.toml - sed -i 's/timeout_propose = ".*"/timeout_propose = "3s"/g' $MAIN_PATH_CONFIG/app.toml - sed -i 's/timeout_propose_delta = ".*"/timeout_propose_delta = "500ms"/g' $MAIN_PATH_CONFIG/app.toml - sed -i 's/timeout_prevote = ".*"/timeout_prevote = "1s"/g' $MAIN_PATH_CONFIG/app.toml - sed -i 's/timeout_prevote_delta = ".*"/timeout_prevote_delta = "500ms"/g' $MAIN_PATH_CONFIG/app.toml - sed -i 's/timeout_precommit = ".*"/timeout_precommit = "1s"/g' $MAIN_PATH_CONFIG/app.toml - sed -i 's/timeout_precommit_delta = ".*"/timeout_precommit_delta = "500ms"/g' $MAIN_PATH_CONFIG/app.toml - sed -i 's/timeout_commit = ".*"/timeout_commit = "5s"/g' $MAIN_PATH_CONFIG/app.toml + sed -i.backup 's/timeout_propose = ".*"/timeout_propose = "3s"/g' $MAIN_PATH_CONFIG/app.toml + sed -i.backup 's/timeout_propose_delta = ".*"/timeout_propose_delta = "500ms"/g' $MAIN_PATH_CONFIG/app.toml + sed -i.backup 's/timeout_prevote = ".*"/timeout_prevote = "1s"/g' $MAIN_PATH_CONFIG/app.toml + sed -i.backup 's/timeout_prevote_delta = ".*"/timeout_prevote_delta = "500ms"/g' $MAIN_PATH_CONFIG/app.toml + sed -i.backup 's/timeout_precommit = ".*"/timeout_precommit = "1s"/g' $MAIN_PATH_CONFIG/app.toml + sed -i.backup 's/timeout_precommit_delta = ".*"/timeout_precommit_delta = "500ms"/g' $MAIN_PATH_CONFIG/app.toml + sed -i.backup 's/timeout_commit = ".*"/timeout_commit = "5s"/g' $MAIN_PATH_CONFIG/app.toml # configure config settings - sed -i 's/laddr = "tcp:\/\/127.0.0.1:26657"/laddr = "tcp:\/\/0.0.0.0:26657"/g' $MAIN_PATH_CONFIG/config.toml - sed -i 's/proxy_app = "tcp:\/\/127.0.0.1:26658"/proxy_app = "tcp:\/\/127.0.0.1:26658"/g' $MAIN_PATH_CONFIG/config.toml - sed -i 's/cors_allowed_origins = \[\]/cors_allowed_origins = \["*"\]/g' $MAIN_PATH_CONFIG/config.toml - sed -i 's/max_num_inbound_peers = 40/max_num_inbound_peers = 120/g' $MAIN_PATH_CONFIG/config.toml - sed -i 's/max_num_outbound_peers = 10/max_num_outbound_peers = 60/g' $MAIN_PATH_CONFIG/config.toml + sed -i.backup 's/laddr = "tcp:\/\/127.0.0.1:26657"/laddr = "tcp:\/\/0.0.0.0:26657"/g' $MAIN_PATH_CONFIG/config.toml + sed -i.backup 's/proxy_app = "tcp:\/\/127.0.0.1:26658"/proxy_app = "tcp:\/\/127.0.0.1:26658"/g' $MAIN_PATH_CONFIG/config.toml + sed -i.backup 's/cors_allowed_origins = \[\]/cors_allowed_origins = \["*"\]/g' $MAIN_PATH_CONFIG/config.toml + sed -i.backup 's/max_num_inbound_peers = 40/max_num_inbound_peers = 120/g' $MAIN_PATH_CONFIG/config.toml + sed -i.backup 's/max_num_outbound_peers = 10/max_num_outbound_peers = 60/g' $MAIN_PATH_CONFIG/config.toml } function update_genesis_json() { @@ -144,7 +144,7 @@ function configure_genesis() { "uri_hash": "" }' update_genesis_json '.app_state.bank.send_enabled[0] = {"denom": "'$BASE_DENOM'", "enabled": true}' - update_genesis_json '.app_state.bank.supply[0] = {"denom": "'$BASE_DENOM'", "amount": "'$total_supply'"}' +# update_genesis_json '.app_state.bank.supply[0] = {"denom": "'$BASE_DENOM'", "amount": "'$total_supply'"}' # EVM params update_genesis_json '.app_state.evm.params.evm_denom = "'$BASE_DENOM'"' @@ -235,8 +235,8 @@ function configure_validator() { local new_persistent_peers=$(IFS=','; echo "${filtered_parts[*]}") echo "Validator $moniker persistent_peers: $new_persistent_peers" - sed -i "s/\(persistent_peers *= *\"\).*\(\" *\)/\1$new_persistent_peers\2/" $validator_home/config/config.toml - sed -i 's/moniker = "localtestnet"/moniker = "'$moniker'"/g' $validator_home/config/config.toml + sed -i.backup "s/\(persistent_peers *= *\"\).*\(\" *\)/\1$new_persistent_peers\2/" $validator_home/config/config.toml + sed -i.backup 's/moniker = "localtestnet"/moniker = "'$moniker'"/g' $validator_home/config/config.toml local key=$(gala keys unsafe-export-eth-key --keyring-backend test --keyring-dir ./$MAIN_PATH_HOME $moniker) yes '00000000' | gala keys unsafe-import-eth-key --keyring-backend test --keyring-dir ./$MAIN_PATH_HOME/validators/$moniker $moniker $key --chain-id $CHAIN_ID @@ -259,9 +259,10 @@ function main() { configure_gala # Add keys - add_key "validator01" - add_key "validator02" - add_key "validator03" + add_key "reticulum01" + add_key "reticulum02" + add_key "reticulum03" + add_key "vlval" add_key "treasury" add_key "faucet" add_key_predefined @@ -274,27 +275,28 @@ function main() { # $1 = staking min deposit # $2 = total supply # $3 = faucet eth address - configure_genesis "5000000000" "1000000000000000000000000" $faucet_address_bech32 + configure_genesis "5000000000000000000000" "1000000000000000000000000" $faucet_address_bech32 # Add genesis accounts - add_genesis_account "validator01" "10000000000000000000000$BASE_DENOM" - add_genesis_account "validator02" "10000000000000000000000$BASE_DENOM" - add_genesis_account "validator03" "10000000000000000000000$BASE_DENOM" + add_genesis_account "reticulum01" "10000000000000000000000$BASE_DENOM" + add_genesis_account "reticulum02" "10000000000000000000000$BASE_DENOM" add_genesis_account "faucet" "10000000000000000000000$BASE_DENOM" add_genesis_account "localkey" "10000000000000000000000$BASE_DENOM" add_genesis_account "treasury" "950000000000000000000000$BASE_DENOM" + add_genesis_account "vlval" "10000000000000000000000$BASE_DENOM" + # Initialize validators - initialize_validator "validator01" "192.168.20.2" 26656 "9000000000000000000000$BASE_DENOM" - initialize_validator "validator02" "192.168.20.3" 26656 "9000000000000000000000$BASE_DENOM" - initialize_validator "validator03" "192.168.20.4" 26656 "9000000000000000000000$BASE_DENOM" + initialize_validator "reticulum01" "127.0.1.1" 26656 "9000000000000000000000$BASE_DENOM" + initialize_validator "reticulum02" "127.0.1.2" 26656 "9000000000000000000000$BASE_DENOM" + initialize_validator "vlval" "95.154.64.137" 26656 "9000000000000000000000$BASE_DENOM" collect_gentxs validate_genesis - configure_validator "validator01" "192.168.20.2" - configure_validator "validator02" "192.168.20.3" - configure_validator "validator03" "192.168.20.4" + configure_validator "reticulum01" "127.0.1.1" + configure_validator "reticulum02" "127.0.1.2" + configure_validator "vlval" "95.154.64.137" configure_faucet } diff --git a/tests/galacli/.gitignore b/tests/galacli/.gitignore new file mode 100644 index 0000000..c8dc11c --- /dev/null +++ b/tests/galacli/.gitignore @@ -0,0 +1,4 @@ +.venv +data +config +node0* diff --git a/tests/galacli/.python-version b/tests/galacli/.python-version new file mode 100644 index 0000000..e4fba21 --- /dev/null +++ b/tests/galacli/.python-version @@ -0,0 +1 @@ +3.12 diff --git a/tests/galacli/README.md b/tests/galacli/README.md new file mode 100644 index 0000000..e69de29 diff --git a/tests/galacli/__init__.py b/tests/galacli/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/galacli/galacli.py b/tests/galacli/galacli.py new file mode 100644 index 0000000..5a5e3df --- /dev/null +++ b/tests/galacli/galacli.py @@ -0,0 +1,511 @@ +import subprocess +from dateutil.parser import isoparse +import json +import time +import socket +import os +import sys +from pathlib import Path +import tempfile +import requests +import toml +from pprint import pprint +import urllib.parse +import asyncio + + +DEBUG = True +DEFAULT_DENOM = "agnet" +DEFAULT_CHAIN_BINARY = "../../build/galacticad" + +DEFAULT_TEST_MONIKER = "test-node01" +DEFAULT_TEST_CHAINID = "test_41239-41239" + +PREDEFINED_KEY_MNEMONIC = "gesture inject test cycle original hollow east ridge hen combine junk child bacon zero hope comfort vacuum milk pitch cage oppose unhappy lunar seat" + + +def wait_for_port(port, host="127.0.0.1", timeout=40.0): + start_time = time.perf_counter() + while True: + try: + with socket.create_connection((host, port), timeout=timeout): + break + except OSError as ex: + time.sleep(0.1) + if time.perf_counter() - start_time >= timeout: + raise TimeoutError( + "Waited too long for the port {} on host {} to start accepting " + "connections.".format(port, host) + ) from ex + + +def get_current_height(cli): + try: + status = cli.status() + except AssertionError as e: + print(f"get sync status failed: {e}", file=sys.stderr) + else: + current_height = int(status["sync_info"]["latest_block_height"]) + return current_height + + +def wait_for_block(cli, height, timeout=240): + for _ in range(timeout * 2): + current_height = get_current_height(cli) + if current_height >= height: + break + print("current block height", current_height) + time.sleep(0.5) + else: + raise TimeoutError(f"wait for block {height} timeout") + + +def interact(cmd, ignore_error=False, input=None, **kwargs): + if DEBUG: + print("\033[94m" + cmd + "\033[0m") + kwargs.setdefault("stderr", subprocess.STDOUT) + proc = subprocess.Popen( + cmd, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + shell=True, + **kwargs, + ) + # begin = time.perf_counter() + (stdout, _) = proc.communicate(input=input) + # print('[%.02f] %s' % (time.perf_counter() - begin, cmd)) + if not ignore_error: + assert proc.returncode == 0, f'{stdout.decode("utf-8")} ({cmd})' + return stdout + + +def safe_cli_string(s): + 'wrap string in "", used for cli argument when contains spaces' + if len(f"{s}".split()) > 1: + return f"'{s}'" + return f"{s}" + + +def build_cli_args_safe(*args, **kwargs): + args = [safe_cli_string(arg) for arg in args if arg] + for k, v in kwargs.items(): + if v is None: + continue + args.append("--" + k.strip("_").replace("_", "-")) + args.append(safe_cli_string(v)) + return list(map(str, args)) + + +def build_cli_args(*args, **kwargs): + args = [arg for arg in args if arg is not None] + for k, v in kwargs.items(): + if v is None: + continue + args.append("--" + k.strip("_").replace("_", "-")) + args.append(v) + return list(map(str, args)) + + +def format_doc_string(**kwargs): + def decorator(target): + target.__doc__ = target.__doc__.format(**kwargs) + return target + + return decorator + + +class GalaToml: + def __init__(self, path): + self.path = path + self.load() + + def load(self): + "load config from toml file from self.path" + try: + with open(self.path, "r") as file: + self.config = toml.load(file) + except FileNotFoundError: + print(f"Config file {self.path} not found.") + + def save(self): + "save config to self.path file in toml format" + with open(self.path, "w") as file: + toml.dump(self.config, file) + + def edit(self, new_config): + "edit config with new_config" + for key, value in new_config.items(): + if isinstance(value, dict): + self.config[key].update(value) + else: + self.config[key] = value + self.save() + + def diff(self, other): + "return a dictionary containing the differences between self and other" + diff_config = {} + for key, value in self.config.items(): + if key not in other.config: + diff_config[key] = value + elif isinstance(value, dict): + sub_diff = self._diff_dict(value, other.config[key]) + if sub_diff: + diff_config[key] = sub_diff + elif value != other.config[key]: + diff_config[key] = value + for key, value in other.config.items(): + if key not in self.config: + diff_config[key] = value + return diff_config + + def _diff_dict(self, dict1, dict2): + "helper method to compare two dictionaries" + diff_dict = {} + for key, value in dict1.items(): + if key not in dict2: + diff_dict[key] = value + elif isinstance(value, dict): + sub_diff = self._diff_dict(value, dict2[key]) + if sub_diff: + diff_dict[key] = sub_diff + elif value != dict2[key]: + diff_dict[key] = value + return diff_dict + + def apply_addr(self, new_addr): + "replace the host in all address values in config with the host from new_addr" + new_host = urllib.parse.urlparse(new_addr).hostname or new_addr + self._apply_addr_to_dict(self.config, new_host) + self.save() + + def _apply_addr_to_dict(self, inner_dict, new_host): + "helper method to apply new_host to nested dictionaries recursively" + address_suffixes = ["address", "addr", "proxy_app"] + + for key, value in inner_dict.items(): + if isinstance(value, dict): + self._apply_addr_to_dict(value, new_host) + elif any( + key.endswith(suffix) for suffix in address_suffixes + ) and isinstance(value, str): + # TODO: make sure that addresses with '' are not in conflict + if value == "": + continue + parsed_url = urllib.parse.urlparse(value) + scheme = parsed_url.scheme + invalid_scheme = not scheme or scheme == "localhost" + if invalid_scheme: + try: + hostname, port = value.split(":") + except ValueError as e: + print(e, value) + else: + port = parsed_url.port + netloc = new_host + ":" + str(port) + if invalid_scheme: + new_endpoint = netloc + else: + new_endpoint = urllib.parse.urlunparse( + (scheme, netloc, *parsed_url[2:]) + ) + inner_dict[key] = new_endpoint + + +class GalaClientConfig(GalaToml): + def __init__(self, path): + super().__init__(path) + + def to_dict(self): + return self.config + + +class BinaryCommand: + def __init__(self, cmd): + self.cmd = cmd + + def __call__(self, cmd, *args, stdin=None, **kwargs): + "execute cmd with binary chaind" + args = " ".join(build_cli_args_safe(cmd, *args, **kwargs)) + return interact(f"{self.cmd} {args}", input=stdin) + + def __str__(self, cmd, *args, **kwargs): + args = " ".join(build_cli_args_safe(cmd, *args, **kwargs)) + return f"{self.cmd} {args}" + + +class GalaCLI: + "the apis to interact with wallet and blockchain" + + def __init__( + self, + cmd=DEFAULT_CHAIN_BINARY, + data_dir=None, + node_rpc=None, + chain_id=DEFAULT_TEST_CHAINID, + keyring_backend="test", + broadcast_mode="sync", + output_format="json", # --output="json" + ): + if data_dir: + self.data_dir = Path(data_dir) + else: + temp = tempfile.TemporaryDirectory(delete=True) + self.data_dir = Path(temp.name) + self.chain_id = chain_id + self.keyring_backend = keyring_backend + self.node_rpc = node_rpc + self.cmd = cmd + self.raw = BinaryCommand(cmd) + self.output = None + self.output_format = output_format + self.broadcast_mode = broadcast_mode + self.error = None + # self.raw("config", home=self.data_dir, chain_id=self.chain_id) + # self.config = GalaToml(self.data_dir / "config/config.toml") + # self.app_config = GalaToml(self.data_dir / "config/app.toml") + # self.client_config = GalaClientConfig(self.data_dir / "config/client.toml") + + @property + def node_rpc_http(self): + return "http" + self.node_rpc.removeprefix("tcp") + + def status(self): + return json.loads(self.raw("status", node=self.node_rpc)) + + def block_height(self): + return int(self.status()["sync_info"]["latest_block_height"]) + + def block_time(self): + return isoparse(self.status()["sync_info"]["latest_block_time"]) + + def rollback(self, hard=False): + return self.raw("rollback", "--hard" if hard else None, home=self.data_dir) + + def version(self): + return self.raw("version", home=self.data_dir) + + # ========================== + # GENESIS cmds + # ========================== + + def validate_genesis(self): + return self.raw("validate-genesis", home=self.data_dir) + + def add_genesis_account(self, addr, coins, **kwargs): + return self.raw( + "add-genesis-account", + addr, + coins, + home=self.data_dir, + output="json", + **kwargs, + ) + + def gentx(self, name, coins, min_self_delegation=1, pubkey=None): + return self.raw( + "gentx", + name, + coins, + min_self_delegation=str(min_self_delegation), + home=self.data_dir, + chain_id=self.chain_id, + keyring_backend=self.keyring_backend, + pubkey=pubkey, + ) + + def collect_gentxs(self, gentx_dir): + return self.raw("collect-gentxs", gentx_dir, home=self.data_dir) + + # ========================== + # ACCOUNT KEYS utils + # ========================== + + def migrate_keystore(self): + return self.raw("keys", "migrate", home=self.data_dir) + + def address(self, name, bech="acc"): + output = self.raw( + "keys", + "show", + name, + "-a", + home=self.data_dir, + keyring_backend=self.keyring_backend, + bech=bech, + ) + return output.strip().decode() + + def create_account(self, name, mnemonic=None): + "create new keypair in node's keyring" + if mnemonic is None: + output = self.raw( + "keys", + "add", + name, + home=self.data_dir, + output="json", + keyring_backend=self.keyring_backend, + ) + else: + output = self.raw( + "keys", + "add", + name, + "--recover", + home=self.data_dir, + algo="eth_secp256k1", + output="json", + keyring_backend=self.keyring_backend, + stdin=mnemonic.encode() + b"\n", + ) + return json.loads(output) + + def delete_account(self, name): + "delete wallet account in node's keyring" + return self.raw( + "keys", + "delete", + name, + "-y", + "--force", + home=self.data_dir, + output="json", + keyring_backend=self.keyring_backend, + ) + + +class GalaNodeCLI(GalaCLI): + def __init__( + self, + cmd=DEFAULT_CHAIN_BINARY, + data_dir=None, + node_rpc=None, + # node_api=None, + chain_id=None, + # node_id=None, + moniker=DEFAULT_TEST_MONIKER, + keyring_backend="test", + ): + super().__init__( + cmd=cmd, + data_dir=data_dir, + node_rpc=node_rpc, + chain_id=chain_id, + keyring_backend=keyring_backend, + ) + # self.node_id = node_id + self.moniker = moniker + self.process = None + + def start(self): ... + + def node_info(self): + return requests.get( + f"{self.node_rpc_http}/cosmos/staking/v1beta1/validators/{self.node_id}" + ).json() + + def init(self, moniker=None): + "generate initial config with genesis.json" + moniker = moniker or self.moniker or DEFAULT_TEST_MONIKER + return self.raw( + "init", + moniker, + chain_id=self.chain_id, + home=self.data_dir, + ) + + async def run(self, cmd, *args, **kwargs): + cmd_args = build_cli_args_safe(cmd, *args, **kwargs) + with open(self.data_dir / "stdout.log", "a") as out, open( + self.data_dir / "stderr.log", "a" + ) as err: + self.process = await asyncio.create_subprocess_exec( + self.cmd, + *cmd_args, + stdout=out, + stderr=err, + ) + return self + + async def get_output(self): + stdout, stderr = await self.process.communicate() + return stdout.decode(), stderr.decode(), self.process.returncode + + def is_running(self): + return self.process and self.process.returncode is None + + async def terminate(self): + if self.is_running(): + self.process.terminate() + await self.process.wait() + + +async def main(): + chain_id = DEFAULT_TEST_CHAINID + moniker = "test-node01" + gn1 = GalaNodeCLI( + data_dir="node01", + chain_id=chain_id, + moniker=moniker, + node_rpc="tcp://127.0.0.2:26657", + ) + gn1.init() + + gn1.client_config = GalaClientConfig(gn1.data_dir / "./config/client.toml") + gn1.client_config.edit( + { + "chain-id": chain_id, + "keyring-backend": "test", + "output": "json", + } + ) + + gn1.app_config = GalaToml(gn1.data_dir / "./config/app.toml") + gn1.app_config.apply_addr("127.0.0.2") + gn1.app_config.edit({"api": {"enable": True}}) + gn1.app_config.edit({"pruning": "nothing"}) + gn1.app_config.edit({"minimum-gas-prices": f"10{DEFAULT_DENOM}"}) + gn1.app_config.edit( + { + "telemetry": { + "service-name": "galacticad", + "enabled": True, + "prometheus-retention-time": "60", + "global-labels": [["chain-id", DEFAULT_TEST_CHAINID]], + } + } + ) + + gn1.config = GalaToml(gn1.data_dir / "./config/config.toml") + gn1.config.edit({"moniker": "test-node01", "log_format": "json"}) + gn1.config.apply_addr("127.0.0.2") + gn1.config.edit( + { + "rpc": { + "cors_allowed_origins": [ + "*", + ] + } + } + ) + ## just check diff between andromeda + # andr_app_config = GalaToml( + # "/Users/booger/occamfi/galactica/ansible-node/gala-andr-root/galaandr_41238-41238/config/app.toml" + # ) + # d = andr_app_config.diff(app_config) + print("config created") + account = gn1.create_account("local-test", PREDEFINED_KEY_MNEMONIC) + pprint(account) + pprint(gn1.address("local-test")) + + print("configure genesis") + await asyncio.sleep(1) + await gn1.run("start", home=gn1.data_dir, chain_id=gn1.chain_id) + await asyncio.sleep(1) + print(gn1.is_running()) + + await gn1.terminate() + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/tests/galacli/pyproject.toml b/tests/galacli/pyproject.toml new file mode 100644 index 0000000..f8bf814 --- /dev/null +++ b/tests/galacli/pyproject.toml @@ -0,0 +1,14 @@ +[project] +name = "galacli" +version = "0.1.0" +description = "Library for testing galacticad binary" +readme = "README.md" +requires-python = ">=3.12" +dependencies = [ + "python-dateutil>=2.9.0.post0", + "pyyaml>=6.0.2", + "requests>=2.32.3", + "ruff>=0.7.1", + "toml>=0.10.2", + "tomlkit>=0.13.2", +] diff --git a/tests/galacli/uv.lock b/tests/galacli/uv.lock new file mode 100644 index 0000000..daabf9b --- /dev/null +++ b/tests/galacli/uv.lock @@ -0,0 +1,196 @@ +version = 1 +requires-python = ">=3.12" + +[[package]] +name = "certifi" +version = "2024.8.30" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/ee/9b19140fe824b367c04c5e1b369942dd754c4c5462d5674002f75c4dedc1/certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9", size = 168507 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/90/3c9ff0512038035f59d279fddeb79f5f1eccd8859f06d6163c58798b9487/certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8", size = 167321 }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f2/4f/e1808dc01273379acc506d18f1504eb2d299bd4131743b9fc54d7be4df1e/charset_normalizer-3.4.0.tar.gz", hash = "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e", size = 106620 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d3/0b/4b7a70987abf9b8196845806198975b6aab4ce016632f817ad758a5aa056/charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0713f3adb9d03d49d365b70b84775d0a0d18e4ab08d12bc46baa6132ba78aaf6", size = 194445 }, + { url = "https://files.pythonhosted.org/packages/50/89/354cc56cf4dd2449715bc9a0f54f3aef3dc700d2d62d1fa5bbea53b13426/charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:de7376c29d95d6719048c194a9cf1a1b0393fbe8488a22008610b0361d834ecf", size = 125275 }, + { url = "https://files.pythonhosted.org/packages/fa/44/b730e2a2580110ced837ac083d8ad222343c96bb6b66e9e4e706e4d0b6df/charset_normalizer-3.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4a51b48f42d9358460b78725283f04bddaf44a9358197b889657deba38f329db", size = 119020 }, + { url = "https://files.pythonhosted.org/packages/9d/e4/9263b8240ed9472a2ae7ddc3e516e71ef46617fe40eaa51221ccd4ad9a27/charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b295729485b06c1a0683af02a9e42d2caa9db04a373dc38a6a58cdd1e8abddf1", size = 139128 }, + { url = "https://files.pythonhosted.org/packages/6b/e3/9f73e779315a54334240353eaea75854a9a690f3f580e4bd85d977cb2204/charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ee803480535c44e7f5ad00788526da7d85525cfefaf8acf8ab9a310000be4b03", size = 149277 }, + { url = "https://files.pythonhosted.org/packages/1a/cf/f1f50c2f295312edb8a548d3fa56a5c923b146cd3f24114d5adb7e7be558/charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d59d125ffbd6d552765510e3f31ed75ebac2c7470c7274195b9161a32350284", size = 142174 }, + { url = "https://files.pythonhosted.org/packages/16/92/92a76dc2ff3a12e69ba94e7e05168d37d0345fa08c87e1fe24d0c2a42223/charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cda06946eac330cbe6598f77bb54e690b4ca93f593dee1568ad22b04f347c15", size = 143838 }, + { url = "https://files.pythonhosted.org/packages/a4/01/2117ff2b1dfc61695daf2babe4a874bca328489afa85952440b59819e9d7/charset_normalizer-3.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07afec21bbbbf8a5cc3651aa96b980afe2526e7f048fdfb7f1014d84acc8b6d8", size = 146149 }, + { url = "https://files.pythonhosted.org/packages/f6/9b/93a332b8d25b347f6839ca0a61b7f0287b0930216994e8bf67a75d050255/charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6b40e8d38afe634559e398cc32b1472f376a4099c75fe6299ae607e404c033b2", size = 140043 }, + { url = "https://files.pythonhosted.org/packages/ab/f6/7ac4a01adcdecbc7a7587767c776d53d369b8b971382b91211489535acf0/charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b8dcd239c743aa2f9c22ce674a145e0a25cb1566c495928440a181ca1ccf6719", size = 148229 }, + { url = "https://files.pythonhosted.org/packages/9d/be/5708ad18161dee7dc6a0f7e6cf3a88ea6279c3e8484844c0590e50e803ef/charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:84450ba661fb96e9fd67629b93d2941c871ca86fc38d835d19d4225ff946a631", size = 151556 }, + { url = "https://files.pythonhosted.org/packages/5a/bb/3d8bc22bacb9eb89785e83e6723f9888265f3a0de3b9ce724d66bd49884e/charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:44aeb140295a2f0659e113b31cfe92c9061622cadbc9e2a2f7b8ef6b1e29ef4b", size = 149772 }, + { url = "https://files.pythonhosted.org/packages/f7/fa/d3fc622de05a86f30beea5fc4e9ac46aead4731e73fd9055496732bcc0a4/charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1db4e7fefefd0f548d73e2e2e041f9df5c59e178b4c72fbac4cc6f535cfb1565", size = 144800 }, + { url = "https://files.pythonhosted.org/packages/9a/65/bdb9bc496d7d190d725e96816e20e2ae3a6fa42a5cac99c3c3d6ff884118/charset_normalizer-3.4.0-cp312-cp312-win32.whl", hash = "sha256:5726cf76c982532c1863fb64d8c6dd0e4c90b6ece9feb06c9f202417a31f7dd7", size = 94836 }, + { url = "https://files.pythonhosted.org/packages/3e/67/7b72b69d25b89c0b3cea583ee372c43aa24df15f0e0f8d3982c57804984b/charset_normalizer-3.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:b197e7094f232959f8f20541ead1d9862ac5ebea1d58e9849c1bf979255dfac9", size = 102187 }, + { url = "https://files.pythonhosted.org/packages/f3/89/68a4c86f1a0002810a27f12e9a7b22feb198c59b2f05231349fbce5c06f4/charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114", size = 194617 }, + { url = "https://files.pythonhosted.org/packages/4f/cd/8947fe425e2ab0aa57aceb7807af13a0e4162cd21eee42ef5b053447edf5/charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed", size = 125310 }, + { url = "https://files.pythonhosted.org/packages/5b/f0/b5263e8668a4ee9becc2b451ed909e9c27058337fda5b8c49588183c267a/charset_normalizer-3.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250", size = 119126 }, + { url = "https://files.pythonhosted.org/packages/ff/6e/e445afe4f7fda27a533f3234b627b3e515a1b9429bc981c9a5e2aa5d97b6/charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920", size = 139342 }, + { url = "https://files.pythonhosted.org/packages/a1/b2/4af9993b532d93270538ad4926c8e37dc29f2111c36f9c629840c57cd9b3/charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64", size = 149383 }, + { url = "https://files.pythonhosted.org/packages/fb/6f/4e78c3b97686b871db9be6f31d64e9264e889f8c9d7ab33c771f847f79b7/charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23", size = 142214 }, + { url = "https://files.pythonhosted.org/packages/2b/c9/1c8fe3ce05d30c87eff498592c89015b19fade13df42850aafae09e94f35/charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc", size = 144104 }, + { url = "https://files.pythonhosted.org/packages/ee/68/efad5dcb306bf37db7db338338e7bb8ebd8cf38ee5bbd5ceaaaa46f257e6/charset_normalizer-3.4.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d", size = 146255 }, + { url = "https://files.pythonhosted.org/packages/0c/75/1ed813c3ffd200b1f3e71121c95da3f79e6d2a96120163443b3ad1057505/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88", size = 140251 }, + { url = "https://files.pythonhosted.org/packages/7d/0d/6f32255c1979653b448d3c709583557a4d24ff97ac4f3a5be156b2e6a210/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90", size = 148474 }, + { url = "https://files.pythonhosted.org/packages/ac/a0/c1b5298de4670d997101fef95b97ac440e8c8d8b4efa5a4d1ef44af82f0d/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b", size = 151849 }, + { url = "https://files.pythonhosted.org/packages/04/4f/b3961ba0c664989ba63e30595a3ed0875d6790ff26671e2aae2fdc28a399/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d", size = 149781 }, + { url = "https://files.pythonhosted.org/packages/d8/90/6af4cd042066a4adad58ae25648a12c09c879efa4849c705719ba1b23d8c/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482", size = 144970 }, + { url = "https://files.pythonhosted.org/packages/cc/67/e5e7e0cbfefc4ca79025238b43cdf8a2037854195b37d6417f3d0895c4c2/charset_normalizer-3.4.0-cp313-cp313-win32.whl", hash = "sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67", size = 94973 }, + { url = "https://files.pythonhosted.org/packages/65/97/fc9bbc54ee13d33dc54a7fcf17b26368b18505500fc01e228c27b5222d80/charset_normalizer-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b", size = 102308 }, + { url = "https://files.pythonhosted.org/packages/bf/9b/08c0432272d77b04803958a4598a51e2a4b51c06640af8b8f0f908c18bf2/charset_normalizer-3.4.0-py3-none-any.whl", hash = "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079", size = 49446 }, +] + +[[package]] +name = "galacli" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "python-dateutil" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "ruff" }, + { name = "toml" }, + { name = "tomlkit" }, +] + +[package.metadata] +requires-dist = [ + { name = "python-dateutil", specifier = ">=2.9.0.post0" }, + { name = "pyyaml", specifier = ">=6.0.2" }, + { name = "requests", specifier = ">=2.32.3" }, + { name = "ruff", specifier = ">=0.7.1" }, + { name = "toml", specifier = ">=0.10.2" }, + { name = "tomlkit", specifier = ">=0.13.2" }, +] + +[[package]] +name = "idna" +version = "3.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 }, +] + +[[package]] +name = "pyyaml" +version = "6.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873 }, + { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302 }, + { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154 }, + { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223 }, + { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542 }, + { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164 }, + { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611 }, + { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591 }, + { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338 }, + { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309 }, + { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679 }, + { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428 }, + { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361 }, + { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523 }, + { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660 }, + { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597 }, + { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527 }, + { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 }, +] + +[[package]] +name = "requests" +version = "2.32.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 }, +] + +[[package]] +name = "ruff" +version = "0.7.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a6/21/5c6e05e0fd3fbb41be4fb92edbc9a04de70baf60adb61435ce0c6b8c3d55/ruff-0.7.1.tar.gz", hash = "sha256:9d8a41d4aa2dad1575adb98a82870cf5db5f76b2938cf2206c22c940034a36f4", size = 3181670 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/65/45/8a20a9920175c9c4892b2420f80ff3cf14949cf3067118e212f9acd9c908/ruff-0.7.1-py3-none-linux_armv6l.whl", hash = "sha256:cb1bc5ed9403daa7da05475d615739cc0212e861b7306f314379d958592aaa89", size = 10389268 }, + { url = "https://files.pythonhosted.org/packages/1b/d3/2f8382db2cf4f9488e938602e33e36287f9d26cb283aa31f11c31297ce79/ruff-0.7.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:27c1c52a8d199a257ff1e5582d078eab7145129aa02721815ca8fa4f9612dc35", size = 10188348 }, + { url = "https://files.pythonhosted.org/packages/a2/31/7d14e2a88da351200f844b7be889a0845d9e797162cf76b136d21b832a23/ruff-0.7.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:588a34e1ef2ea55b4ddfec26bbe76bc866e92523d8c6cdec5e8aceefeff02d99", size = 9841448 }, + { url = "https://files.pythonhosted.org/packages/db/99/738cafdc768eceeca0bd26c6f03e213aa91203d2278e1d95b1c31c4ece41/ruff-0.7.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94fc32f9cdf72dc75c451e5f072758b118ab8100727168a3df58502b43a599ca", size = 10674864 }, + { url = "https://files.pythonhosted.org/packages/fe/12/bcf2836b50eab53c65008383e7d55201e490d75167c474f14a16e1af47d2/ruff-0.7.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:985818742b833bffa543a84d1cc11b5e6871de1b4e0ac3060a59a2bae3969250", size = 10192105 }, + { url = "https://files.pythonhosted.org/packages/2b/71/261d5d668bf98b6c44e89bfb5dfa4cb8cb6c8b490a201a3d8030e136ea4f/ruff-0.7.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32f1e8a192e261366c702c5fb2ece9f68d26625f198a25c408861c16dc2dea9c", size = 11194144 }, + { url = "https://files.pythonhosted.org/packages/90/1f/0926d18a3b566fa6e7b3b36093088e4ffef6b6ba4ea85a462d9a93f7e35c/ruff-0.7.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:699085bf05819588551b11751eff33e9ca58b1b86a6843e1b082a7de40da1565", size = 11917066 }, + { url = "https://files.pythonhosted.org/packages/cd/a8/9fac41f128b6a44ab4409c1493430b4ee4b11521e8aeeca19bfe1ce851f9/ruff-0.7.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:344cc2b0814047dc8c3a8ff2cd1f3d808bb23c6658db830d25147339d9bf9ea7", size = 11458821 }, + { url = "https://files.pythonhosted.org/packages/25/cd/59644168f086ab13fe4e02943b9489a0aa710171f66b178e179df5383554/ruff-0.7.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4316bbf69d5a859cc937890c7ac7a6551252b6a01b1d2c97e8fc96e45a7c8b4a", size = 12700379 }, + { url = "https://files.pythonhosted.org/packages/fb/30/3bac63619eb97174661829c07fc46b2055a053dee72da29d7c304c1cd2c0/ruff-0.7.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:79d3af9dca4c56043e738a4d6dd1e9444b6d6c10598ac52d146e331eb155a8ad", size = 11019813 }, + { url = "https://files.pythonhosted.org/packages/4b/af/f567b885b5cb3bcdbcca3458ebf210cc8c9c7a9f61c332d3c2a050c3b21e/ruff-0.7.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c5c121b46abde94a505175524e51891f829414e093cd8326d6e741ecfc0a9112", size = 10662146 }, + { url = "https://files.pythonhosted.org/packages/bc/ad/eb930d3ad117a9f2f7261969c21559ebd82bb13b6e8001c7caed0d44be5f/ruff-0.7.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8422104078324ea250886954e48f1373a8fe7de59283d747c3a7eca050b4e378", size = 10256911 }, + { url = "https://files.pythonhosted.org/packages/20/d5/af292ce70a016fcec792105ca67f768b403dd480a11888bc1f418fed0dd5/ruff-0.7.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:56aad830af8a9db644e80098fe4984a948e2b6fc2e73891538f43bbe478461b8", size = 10767488 }, + { url = "https://files.pythonhosted.org/packages/24/85/cc04a3bd027f433bebd2a097e63b3167653c079f7f13d8f9a1178e693412/ruff-0.7.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:658304f02f68d3a83c998ad8bf91f9b4f53e93e5412b8f2388359d55869727fd", size = 11093368 }, + { url = "https://files.pythonhosted.org/packages/0b/fb/c39cbf32d1f3e318674b8622f989417231794926b573f76dd4d0ca49f0f1/ruff-0.7.1-py3-none-win32.whl", hash = "sha256:b517a2011333eb7ce2d402652ecaa0ac1a30c114fbbd55c6b8ee466a7f600ee9", size = 8594180 }, + { url = "https://files.pythonhosted.org/packages/5a/71/ec8cdea34ecb90c830ca60d54ac7b509a7b5eab50fae27e001d4470fe813/ruff-0.7.1-py3-none-win_amd64.whl", hash = "sha256:f38c41fcde1728736b4eb2b18850f6d1e3eedd9678c914dede554a70d5241307", size = 9419751 }, + { url = "https://files.pythonhosted.org/packages/79/7b/884553415e9f0a9bf358ed52fb68b934e67ef6c5a62397ace924a1afdf9a/ruff-0.7.1-py3-none-win_arm64.whl", hash = "sha256:19aa200ec824c0f36d0c9114c8ec0087082021732979a359d6f3c390a6ff2a37", size = 8717402 }, +] + +[[package]] +name = "six" +version = "1.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/71/39/171f1c67cd00715f190ba0b100d606d440a28c93c7714febeca8b79af85e/six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", size = 34041 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d9/5a/e7c31adbe875f2abbb91bd84cf2dc52d792b5a01506781dbcf25c91daf11/six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254", size = 11053 }, +] + +[[package]] +name = "toml" +version = "0.10.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f", size = 22253 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588 }, +] + +[[package]] +name = "tomlkit" +version = "0.13.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b1/09/a439bec5888f00a54b8b9f05fa94d7f901d6735ef4e55dcec9bc37b5d8fa/tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79", size = 192885 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/b6/a447b5e4ec71e13871be01ba81f5dfc9d0af7e473da256ff46bc0e24026f/tomlkit-0.13.2-py3-none-any.whl", hash = "sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde", size = 37955 }, +] + +[[package]] +name = "urllib3" +version = "2.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ed/63/22ba4ebfe7430b76388e7cd448d5478814d3032121827c12a2cc287e2260/urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9", size = 300677 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/d9/5f4c13cecde62396b0d3fe530a50ccea91e7dfc1ccf0e09c228841bb5ba8/urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac", size = 126338 }, +] From 439e4ac551e84c6ac319bb0929d317dbff0772a8 Mon Sep 17 00:00:00 2001 From: valli0x Date: Thu, 31 Oct 2024 14:45:00 +0300 Subject: [PATCH 08/20] added gracefull shutdown into ethermint --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 8baa31d..c9124f2 100644 --- a/go.mod +++ b/go.mod @@ -307,7 +307,7 @@ replace ( github.com/dgrijalva/jwt-go => github.com/golang-jwt/jwt/v4 v4.4.2 github.com/ethereum/go-ethereum => github.com/crypto-org-chain/go-ethereum v1.10.20-0.20240425065928-ebb09502e7a7 // develop - github.com/evmos/ethermint => github.com/crypto-org-chain/ethermint v0.6.1-0.20240913100216-dbc7eb41488c + github.com/evmos/ethermint => github.com/valli0x/ethermint v0.1.1-gala // Fix upstream GHSA-h395-qcrw-5vmq and GHSA-3vp4-m3rf-835h vulnerabilities. // TODO Remove it: https://github.com/cosmos/cosmos-sdk/issues/10409 diff --git a/go.sum b/go.sum index db03c64..4de1d0c 100644 --- a/go.sum +++ b/go.sum @@ -440,8 +440,6 @@ github.com/crypto-org-chain/cosmos-sdk/store v0.0.0-20240911084450-6870ba130be2 github.com/crypto-org-chain/cosmos-sdk/store v0.0.0-20240911084450-6870ba130be2/go.mod h1:gjE3DZe4t/+VeIk6CmrouyqiuDbZ7QOVDDq3nLqBTpg= github.com/crypto-org-chain/cosmos-sdk/x/tx v0.0.0-20240911084450-6870ba130be2 h1:mxlOSCru7YgmX055rrlkCSUu0D8lAqJ8Dnhp0yXCBuM= github.com/crypto-org-chain/cosmos-sdk/x/tx v0.0.0-20240911084450-6870ba130be2/go.mod h1:RTiTs4hkXG6IvYGknvB8p79YgjYJdcbzLUOGJChsPnY= -github.com/crypto-org-chain/ethermint v0.6.1-0.20240913100216-dbc7eb41488c h1:pJJNL+ZganmfcxzEijVNqwNDhzXsTyMk/Of1/lUvxlM= -github.com/crypto-org-chain/ethermint v0.6.1-0.20240913100216-dbc7eb41488c/go.mod h1:D2lnc8ARuVmgc2/2IWla2Ky1o8/pjmyrnIt+d46Clco= github.com/crypto-org-chain/go-block-stm v0.0.0-20240912024944-1cd89976aa5e h1:FFpE6+Y4o5GxkeGwUcETM6amgohh7msWvWf1MDqueVc= github.com/crypto-org-chain/go-block-stm v0.0.0-20240912024944-1cd89976aa5e/go.mod h1:iwQTX9xMX8NV9k3o2BiWXA0SswpsZrDk5q3gA7nWYiE= github.com/crypto-org-chain/go-ethereum v1.10.20-0.20240425065928-ebb09502e7a7 h1:V43F3JFcqG4MUThf9W/DytnPblpR6CcaLBw2Wx6zTgE= @@ -1205,6 +1203,8 @@ github.com/urfave/cli v1.22.12 h1:igJgVw1JdKH+trcLWLeLwZjU9fEfPesQ+9/e4MQ44S8= github.com/urfave/cli v1.22.12/go.mod h1:sSBEIC79qR6OvcmsD4U3KABeOTxDqQtdDnaFuUN30b8= github.com/urfave/cli/v2 v2.17.2-0.20221006022127-8f469abc00aa h1:5SqCsI/2Qya2bCzK15ozrqo2sZxkh0FHynJZOTVoV6Q= github.com/urfave/cli/v2 v2.17.2-0.20221006022127-8f469abc00aa/go.mod h1:1CNUng3PtjQMtRzJO4FMXBQvkGtuYRxxiR9xMa7jMwI= +github.com/valli0x/ethermint v0.1.1-gala h1:N4c7lAhw7N0LXlLoxE7l39eJLoWfhrNEJWlywfuOWo8= +github.com/valli0x/ethermint v0.1.1-gala/go.mod h1:D2lnc8ARuVmgc2/2IWla2Ky1o8/pjmyrnIt+d46Clco= github.com/vbatts/tar-split v0.11.3 h1:hLFqsOLQ1SsppQNTMpkpPXClLDfC2A3Zgy9OUU+RVck= github.com/vbatts/tar-split v0.11.3/go.mod h1:9QlHN18E+fEH7RdG+QAJJcuya3rqT7eXSTY7wGrAokY= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= From e8a9c77f213249565aedd962f066c8929adc4f46 Mon Sep 17 00:00:00 2001 From: valli0x Date: Tue, 5 Nov 2024 13:12:22 +0300 Subject: [PATCH 09/20] pruning comamnd --- cmd/galacticad/cmd/root.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmd/galacticad/cmd/root.go b/cmd/galacticad/cmd/root.go index 496d484..314c905 100644 --- a/cmd/galacticad/cmd/root.go +++ b/cmd/galacticad/cmd/root.go @@ -72,6 +72,7 @@ import ( appparams "github.com/Galactica-corp/galactica/app/params" "github.com/Galactica-corp/galactica/cmd/galacticad/cmd/ethkeys" dbm "github.com/cosmos/cosmos-db" + "github.com/cosmos/cosmos-sdk/client/pruning" "github.com/cosmos/cosmos-sdk/crypto/keyring" simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" txsign "github.com/cosmos/cosmos-sdk/types/tx/signing" @@ -231,6 +232,7 @@ func initRootCmd( rootCmd.AddCommand( snapshot.Cmd(a.newApp), + pruning.Cmd(a.newApp, app.DefaultNodeHome), ) rootCmd, err := srvflags.AddGlobalFlags(rootCmd) From 8d02fd79aa5f3759536d78ee14f12ee82ca9d204 Mon Sep 17 00:00:00 2001 From: Ilya Bygrimov Date: Wed, 6 Nov 2024 16:01:34 +0100 Subject: [PATCH 10/20] Working on genesis.json --- tests/galacli/galacli.py | 320 ++++++++++++++++++++++++++++++++++----- 1 file changed, 286 insertions(+), 34 deletions(-) diff --git a/tests/galacli/galacli.py b/tests/galacli/galacli.py index 5a5e3df..287c2bd 100644 --- a/tests/galacli/galacli.py +++ b/tests/galacli/galacli.py @@ -1,9 +1,11 @@ +from collections import defaultdict import subprocess from dateutil.parser import isoparse import json import time import socket -import os + +# import os import sys from pathlib import Path import tempfile @@ -13,15 +15,17 @@ import urllib.parse import asyncio - DEBUG = True DEFAULT_DENOM = "agnet" +BASE_DENOM = DEFAULT_DENOM +DISPLAY_DENOM = "gnet" DEFAULT_CHAIN_BINARY = "../../build/galacticad" DEFAULT_TEST_MONIKER = "test-node01" DEFAULT_TEST_CHAINID = "test_41239-41239" PREDEFINED_KEY_MNEMONIC = "gesture inject test cycle original hollow east ridge hen combine junk child bacon zero hope comfort vacuum milk pitch cage oppose unhappy lunar seat" +PREDEFINED_KEY_MNEMONIC_FAUCET = "heart grape ignore face equip monkey keep armor tumble donkey final horror harsh way retire this enforce pave there unfair scrap shine physical since" def wait_for_port(port, host="127.0.0.1", timeout=40.0): @@ -120,7 +124,7 @@ def __init__(self, path): self.load() def load(self): - "load config from toml file from self.path" + "Load config from toml file at self.path" try: with open(self.path, "r") as file: self.config = toml.load(file) @@ -128,21 +132,25 @@ def load(self): print(f"Config file {self.path} not found.") def save(self): - "save config to self.path file in toml format" + "Save config to self.path in toml format" with open(self.path, "w") as file: toml.dump(self.config, file) - def edit(self, new_config): - "edit config with new_config" - for key, value in new_config.items(): - if isinstance(value, dict): - self.config[key].update(value) + def deep_update(self, original, new): + "Recursive update of nested dictionaries" + for key, value in new.items(): + if isinstance(value, dict) and isinstance(original.get(key), dict): + self.deep_update(original[key], value) else: - self.config[key] = value + original[key] = value + + def edit(self, new_config): + "Edit config with new_config, preserving unchanged keys" + self.deep_update(self.config, new_config) self.save() def diff(self, other): - "return a dictionary containing the differences between self and other" + "Return a dictionary containing the differences between self and other" diff_config = {} for key, value in self.config.items(): if key not in other.config: @@ -159,7 +167,7 @@ def diff(self, other): return diff_config def _diff_dict(self, dict1, dict2): - "helper method to compare two dictionaries" + "Helper method to compare two dictionaries" diff_dict = {} for key, value in dict1.items(): if key not in dict2: @@ -173,13 +181,13 @@ def _diff_dict(self, dict1, dict2): return diff_dict def apply_addr(self, new_addr): - "replace the host in all address values in config with the host from new_addr" + "Replace the host in all address values in config with the host from new_addr" new_host = urllib.parse.urlparse(new_addr).hostname or new_addr self._apply_addr_to_dict(self.config, new_host) self.save() def _apply_addr_to_dict(self, inner_dict, new_host): - "helper method to apply new_host to nested dictionaries recursively" + "Helper method to apply new_host to nested dictionaries recursively" address_suffixes = ["address", "addr", "proxy_app"] for key, value in inner_dict.items(): @@ -233,6 +241,39 @@ def __str__(self, cmd, *args, **kwargs): return f"{self.cmd} {args}" +class Genesis: + def __init__(self, path: Path): + self.path = path + self.config = defaultdict(lambda: defaultdict(dict)) + self.data = self.load() + + def load(self): + "load config from toml file from self.path" + try: + with open(self.path, "r") as file: + self.config.update(json.load(file)) + except FileNotFoundError: + print(f"Config file {self.path} not found.") + + def save(self): + "save config to self.path file in toml format" + with open(self.path, "w") as file: + json.dump(self.config, file) + + def deep_update(self, original, new): + "Recursive update of nested dictionaries" + for key, value in new.items(): + if isinstance(value, dict) and isinstance(original.get(key), dict): + self.deep_update(original[key], value) + else: + original[key] = value + + def edit(self, new_config): + "edit config with new_config, keeping unchanged keys" + self.deep_update(self.config, new_config) + self.save() + + class GalaCLI: "the apis to interact with wallet and blockchain" @@ -260,10 +301,15 @@ def __init__( self.output_format = output_format self.broadcast_mode = broadcast_mode self.error = None - # self.raw("config", home=self.data_dir, chain_id=self.chain_id) - # self.config = GalaToml(self.data_dir / "config/config.toml") - # self.app_config = GalaToml(self.data_dir / "config/app.toml") - # self.client_config = GalaClientConfig(self.data_dir / "config/client.toml") + if Path(self.data_dir / "config/config.toml").exists(): + self.config = GalaToml(self.data_dir / "config/config.toml") + if Path(self.data_dir / "config/app.toml").exists(): + self.app_config = GalaToml(self.data_dir / "config/app.toml") + if Path(self.data_dir / "config/client.toml").exists(): + self.client_config = GalaClientConfig(self.data_dir / "config/client.toml") + + def version(self): + return self.raw("version", home=self.data_dir) @property def node_rpc_http(self): @@ -278,15 +324,22 @@ def block_height(self): def block_time(self): return isoparse(self.status()["sync_info"]["latest_block_time"]) + def wait_for_block(self, height, timeout=240): + for _ in range(timeout * 2): + current_height = self.block_height() + if current_height >= height: + return True + print("current block height", current_height) + time.sleep(0.5) + else: + raise TimeoutError(f"wait for block {height} timeout") + def rollback(self, hard=False): return self.raw("rollback", "--hard" if hard else None, home=self.data_dir) - def version(self): - return self.raw("version", home=self.data_dir) - - # ========================== + ############################## # GENESIS cmds - # ========================== + ############################## def validate_genesis(self): return self.raw("validate-genesis", home=self.data_dir) @@ -301,7 +354,7 @@ def add_genesis_account(self, addr, coins, **kwargs): **kwargs, ) - def gentx(self, name, coins, min_self_delegation=1, pubkey=None): + def gentx(self, name, coins, min_self_delegation=1, pubkey=None, **kwargs): return self.raw( "gentx", name, @@ -311,14 +364,15 @@ def gentx(self, name, coins, min_self_delegation=1, pubkey=None): chain_id=self.chain_id, keyring_backend=self.keyring_backend, pubkey=pubkey, + **kwargs, ) def collect_gentxs(self, gentx_dir): return self.raw("collect-gentxs", gentx_dir, home=self.data_dir) - # ========================== + ############################## # ACCOUNT KEYS utils - # ========================== + ############################## def migrate_keystore(self): return self.raw("keys", "migrate", home=self.data_dir) @@ -373,8 +427,71 @@ def delete_account(self, name): keyring_backend=self.keyring_backend, ) + ############################## + # Tendermint + ############################## + + def consensus_address(self): + "get tendermint consensus address" + output = self.raw("tendermint", "show-address", home=self.data_dir) + return output.decode().strip() + + def node_id(self): + "get tendermint node id" + output = self.raw("tendermint", "show-node-id", home=self.data_dir) + return output.decode().strip() + + def export(self): + return self.raw("export", home=self.data_dir) + + def unsaferesetall(self): + return self.raw("unsafe-reset-all") + + ############################## + # FEEMARKET Module + ############################## + + def query_base_fee(self, **kwargs): + default_kwargs = {"home": self.data_dir} + + # TODO: is this assumption correct? Having the base fee turned off has caused some test failures + # because it was returning `null` and not an `int(...)` -> we'll return 0 here. + params = json.loads( + self.raw( + "q", + "feemarket", + "params", + output="json", + node=self.node_rpc, + **(default_kwargs | kwargs), + ) + ) + no_base_fee = params["params"]["no_base_fee"] + if no_base_fee: + return 0 + + base_fee_out = self.raw( + "q", + "feemarket", + "base-fee", + output="json", + node=self.node_rpc, + **(default_kwargs | kwargs), + ) + + out_dict = json.loads(base_fee_out) + if not out_dict: + raise ValueError(f"failed to return base fee: {out_dict}") + + base_fee = out_dict["base_fee"] + if not base_fee: + raise ValueError(f"failed to return base fee: {out_dict}") + + return float(base_fee) + class GalaNodeCLI(GalaCLI): + "Class to control started node of galacticad" def __init__( self, cmd=DEFAULT_CHAIN_BINARY, @@ -434,15 +551,35 @@ async def get_output(self): def is_running(self): return self.process and self.process.returncode is None - async def terminate(self): + async def terminate(self, timeout=30): if self.is_running(): self.process.terminate() - await self.process.wait() + try: + await asyncio.wait_for(self.process.wait(), timeout=timeout) + exit_code = self.process.returncode + print(f"Instance {self.moniker} exited with code {exit_code}") + return exit_code + except asyncio.TimeoutError: + print( + f"Process exceeded timeout of {timeout} seconds. Killing the process." + ) + self.process.kill() + await self.process.wait() + return self.process.returncode async def main(): chain_id = DEFAULT_TEST_CHAINID moniker = "test-node01" + g_client = GalaClientConfig("/dev/null") + + g_client.config.update(dict( + chain_id = "test_41239-41239", + keyring_backend = "test", + output = "json", + node = "tcp://127.0.0.2:26657", + broadcast_mode = "sync" + )) gn1 = GalaNodeCLI( data_dir="node01", chain_id=chain_id, @@ -479,6 +616,7 @@ async def main(): gn1.config = GalaToml(gn1.data_dir / "./config/config.toml") gn1.config.edit({"moniker": "test-node01", "log_format": "json"}) gn1.config.apply_addr("127.0.0.2") + gn1.config.edit({"consensus": {"timeout_commit": "1s"}}) gn1.config.edit( { "rpc": { @@ -494,17 +632,131 @@ async def main(): # ) # d = andr_app_config.diff(app_config) print("config created") - account = gn1.create_account("local-test", PREDEFINED_KEY_MNEMONIC) - pprint(account) - pprint(gn1.address("local-test")) - print("configure genesis") + + total_supply = str(int(200e18)) + staking_min_deposit = str(int(100e18)) + max_deposit_period = "600s" + unbonding_time = "30s" + + faucet = gn1.create_account("faucet", PREDEFINED_KEY_MNEMONIC_FAUCET) + faucet_address = faucet["address"] + inflation_validators_share = "0.99933" + inflation_faucet_share = "0.00067" + + block_max_gas = "40000000" + block_max_bytes = "22020096" + time_iota_ms = "1000" + voting_period = "60s" + expedited_voting_period = "30s" + genesis = Genesis(path=gn1.data_dir / "config/genesis.json") + genesis.edit({"consensus": {"params": {"block": {"max_bytes": block_max_bytes}}}}) + genesis.edit({"consensus": {"params": {"block": {"max_gas": block_max_gas}}}}) + genesis.edit({"consensus": {"params": {"block": {"time_iota_ms": time_iota_ms}}}}) + genesis.edit( + {"app_state": {"gov": {"voting_params": {"voting_period": voting_period}}}} + ) + + update_genesis = { + "app_state": { + "gov": { + "deposit_params": { + "min_deposit": [ + {"denom": BASE_DENOM, "amount": staking_min_deposit} + ] + }, + "params": { + "min_deposit": [ + {"denom": BASE_DENOM, "amount": staking_min_deposit} + ], + "max_deposit_period": max_deposit_period, + "voting_period": voting_period, + "expedited_voting_period": expedited_voting_period, + }, + }, + "staking": { + "params": {"bond_denom": BASE_DENOM, "unbonding_time": unbonding_time} + }, + "crisis": {"constant_fee": {"denom": BASE_DENOM}}, + "mint": {"params": {"mint_denom": BASE_DENOM}}, + "bank": { + "denom_metadata": [ + { + "description": "The native staking token of the Galactica Network.", + "denom_units": [ + { + "denom": BASE_DENOM, + "exponent": 0, + "aliases": ["attognet"], + }, + {"denom": "ugnet", "exponent": 6, "aliases": ["micrognet"]}, + {"denom": DISPLAY_DENOM, "exponent": 18}, + ], + "base": BASE_DENOM, + "display": DISPLAY_DENOM, + "name": "Galactica Network", + "symbol": DISPLAY_DENOM.upper(), + "uri": "", + "uri_hash": "", + } + ], + "send_enabled": [{"denom": BASE_DENOM, "enabled": True}], + "supply": [{"denom": BASE_DENOM, "amount": total_supply}], + }, + "evm": {"params": {"evm_denom": BASE_DENOM}}, + "inflation": { + "params": { + "enable_inflation": True, + "mint_denom": BASE_DENOM, + "inflation_distribution": { + "validators_share": inflation_validators_share, + "other_shares": [ + { + "address": faucet_address, + "name": "faucet", + "share": inflation_faucet_share, + } + ], + }, + }, + "inflation_distribution": { + "validators_share": inflation_validators_share, + "other_shares": [ + { + "address": faucet_address, + "name": "faucet", + "share": inflation_faucet_share, + } + ], + }, + }, + }, + } + genesis.edit(update_genesis) + print("add some genesis accounts") + test_node01_acc = gn1.create_account("test-node01") + # total_supply = 910e18 + gn1.add_genesis_account(test_node01_acc["address"], str(int(200e18)) + BASE_DENOM) + gn1.gentx( + "test-node01", + str(int(150e18)) + BASE_DENOM, + ip="127.0.0.2", + commission_rate="0.02", + details="test.node01.details", + ) + gn1.collect_gentxs(gn1.data_dir / "config/gentx") + gn1.validate_genesis() + print("start node...") + await asyncio.sleep(1) await gn1.run("start", home=gn1.data_dir, chain_id=gn1.chain_id) await asyncio.sleep(1) print(gn1.is_running()) - - await gn1.terminate() + gn1.wait_for_block(5) + # await gn1.terminate() + # await asyncio.sleep(10) + if await gn1.terminate() == 0 : + print("Success") if __name__ == "__main__": From e33b776e903fa83d636ac14ff72d81bfbf0b8307 Mon Sep 17 00:00:00 2001 From: Ilya Bygrimov Date: Wed, 6 Nov 2024 16:05:58 +0100 Subject: [PATCH 11/20] fmt --- tests/galacli/galacli.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/tests/galacli/galacli.py b/tests/galacli/galacli.py index 287c2bd..387e49e 100644 --- a/tests/galacli/galacli.py +++ b/tests/galacli/galacli.py @@ -492,6 +492,7 @@ def query_base_fee(self, **kwargs): class GalaNodeCLI(GalaCLI): "Class to control started node of galacticad" + def __init__( self, cmd=DEFAULT_CHAIN_BINARY, @@ -573,13 +574,15 @@ async def main(): moniker = "test-node01" g_client = GalaClientConfig("/dev/null") - g_client.config.update(dict( - chain_id = "test_41239-41239", - keyring_backend = "test", - output = "json", - node = "tcp://127.0.0.2:26657", - broadcast_mode = "sync" - )) + g_client.config.update( + dict( + chain_id="test_41239-41239", + keyring_backend="test", + output="json", + node="tcp://127.0.0.2:26657", + broadcast_mode="sync", + ) + ) gn1 = GalaNodeCLI( data_dir="node01", chain_id=chain_id, @@ -755,7 +758,7 @@ async def main(): gn1.wait_for_block(5) # await gn1.terminate() # await asyncio.sleep(10) - if await gn1.terminate() == 0 : + if await gn1.terminate() == 0: print("Success") From 242866cf619a3e559c9bc50079a1e65fdeac2acc Mon Sep 17 00:00:00 2001 From: Ilya Bygrimov Date: Wed, 6 Nov 2024 16:10:35 +0100 Subject: [PATCH 12/20] rm comment --- tests/galacli/galacli.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/galacli/galacli.py b/tests/galacli/galacli.py index 387e49e..95829a1 100644 --- a/tests/galacli/galacli.py +++ b/tests/galacli/galacli.py @@ -629,10 +629,6 @@ async def main(): } } ) - ## just check diff between andromeda - # andr_app_config = GalaToml( - # "/Users/booger/occamfi/galactica/ansible-node/gala-andr-root/galaandr_41238-41238/config/app.toml" - # ) # d = andr_app_config.diff(app_config) print("config created") print("configure genesis") From 1079312c4f5543b374c3045b5a17e04848bb0ca2 Mon Sep 17 00:00:00 2001 From: Ilya Bygrimov Date: Wed, 6 Nov 2024 16:17:04 +0100 Subject: [PATCH 13/20] Edit Makefile --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 373ba8c..f661120 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,8 @@ #!/usr/bin/make -f -VERSION ?= $(shell echo $(shell git describe --tags `git rev-list --tags="v*" --max-count=1`) | sed 's/^v//') +VERSION ?= $(shell echo $(shell git describe --tags `git rev-list --tags="v*" --max-count=1 2>/dev/null`) 2>/dev/null | sed 's/^v//') TMVERSION := $(shell go list -m github.com/cometbft/cometbft | sed 's:.* ::') -COMMIT := $(shell git log -1 --format='%H') +COMMIT ?= $(shell git log -1 --format='%H' 2>/dev/null ) LEDGER_ENABLED ?= true GALACTICA_BINARY = galacticad BUILDDIR ?= $(CURDIR)/build From c2ff3b32d1344666402b52916f55d25ceb1a20c9 Mon Sep 17 00:00:00 2001 From: Ilya Bygrimov Date: Wed, 13 Nov 2024 23:53:43 +0100 Subject: [PATCH 14/20] Launch some test with three node in localnet --- .github/workflows/ci.yaml | 74 ++++ Makefile | 8 + localnet/init-configs-localnet.sh | 130 +++--- tests/Makefile | 2 + tests/galacli/.gitignore | 1 + tests/galacli/Makefile | 20 + tests/galacli/galacli.py | 631 +++++++++++++++++++++--------- tests/galacli/pyproject.toml | 1 + tests/galacli/uv.lock | 11 + 9 files changed, 630 insertions(+), 248 deletions(-) create mode 100644 .github/workflows/ci.yaml create mode 100644 tests/Makefile create mode 100644 tests/galacli/Makefile diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..7d42caa --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,74 @@ +name: CI/CD Pipeline + +on: + push: + branches: + - "*" + pull_request: + types: [opened, synchronize] + workflow_dispatch: + inputs: + branch: + description: "Branch to run tests on" + required: true + default: "main" + release: + types: [created] + +jobs: + build: + runs-on: self-hosted + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: "1.22" + + - name: Build binary + run: make build + + - name: Upload binary artifact + if: github.event_name == 'release' + uses: actions/upload-artifact@v2 + with: + name: galacticad + path: build/galacticad + + test: + runs-on: self-hosted + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - uses: astral-sh/setup-uv@v3 + with: + version: "latest" + + - name: Run tests + run: make test + + - name: Test status check + if: failure() + run: exit 1 + + permissions: + pull-requests: write + + manual-tests: + runs-on: self-hosted + needs: test + if: github.event_name == 'workflow_dispatch' + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Set up Go + uses: actions/setup-go@v3 + with: + go-version: "1.17" + + - name: Run manual tests + run: go test ./... diff --git a/Makefile b/Makefile index f661120..4b86891 100644 --- a/Makefile +++ b/Makefile @@ -144,3 +144,11 @@ install: build mkdir -p $(BINDIR) cp -f $(BUILDDIR)/$(GALACTICA_BINARY) $(BINDIR)/$(GALACTICA_BINARY) @echo "Galactica has been installed to $(BINDIR)" + +help: ## Show this help + @printf "\033[33m%s:\033[0m\n" 'Available commands' + @awk 'BEGIN {FS = ":.*?## "} /^[[:alpha:][:punct:]]+:.*?## / {printf " \033[32m%-18s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST) + +ifeq ($(CI),true) +include tests/Makefile +endif diff --git a/localnet/init-configs-localnet.sh b/localnet/init-configs-localnet.sh index d8e3242..d6b17ef 100755 --- a/localnet/init-configs-localnet.sh +++ b/localnet/init-configs-localnet.sh @@ -21,13 +21,12 @@ CHAIN_ID=${2:-"galactica_9302-1"} KEYRING_BACKEND=${3:-"test"} BASE_DENOM=${4:-"agnet"} DISPLAY_DENOM=${5:-"gnet"} -SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) +SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) # localKey address 0x7cb61d4117ae31a12e393a1cfa3bac666481d02e PREDEFINED_KEY_NAME="localkey" PREDEFINED_KEY_MNEMONIC="gesture inject test cycle original hollow east ridge hen combine junk child bacon zero hope comfort vacuum milk pitch cage oppose unhappy lunar seat" - function gala() { ./build/galacticad --home "$MAIN_PATH_HOME" "$@" } @@ -50,16 +49,12 @@ function add_key() { } function add_key_predefined() { - echo $PREDEFINED_KEY_MNEMONIC | gala keys add \ - $PREDEFINED_KEY_NAME \ - --recover \ - --keyring-backend $KEYRING_BACKEND \ - --algo "eth_secp256k1" \ - --keyring-dir $MAIN_PATH_HOME/ -} - -function init_localtestnet() { - gala init localtestnet --chain-id $CHAIN_ID --default-denom $BASE_DENOM + echo $PREDEFINED_KEY_MNEMONIC | gala keys add \ + $PREDEFINED_KEY_NAME \ + --recover \ + --keyring-backend $KEYRING_BACKEND \ + --algo "eth_secp256k1" \ + --keyring-dir $MAIN_PATH_HOME/ } function configure_app() { @@ -69,41 +64,40 @@ function configure_app() { # sed -i.backup 's/enabled-unsafe-cors = false/enabled-unsafe-cors = true/' $MAIN_PATH_CONFIG/app.toml sed -i.backup 's/address = "localhost:9090"/address = "0.0.0.0:9090"/' $MAIN_PATH_CONFIG/app.toml sed -i.backup '/\[grpc-web\]/,+7 s/address = "localhost:9091"/address = "0.0.0.0:9091"/' $MAIN_PATH_CONFIG/app.toml - sed -i.backup 's/pruning = "default"/pruning = "nothing"/g' $MAIN_PATH_CONFIG/app.toml - sed -i.backup 's/minimum-gas-prices = "0stake"/minimum-gas-prices = "10'$BASE_DENOM'"/g' $MAIN_PATH_CONFIG/app.toml + sed -i.backup 's/pruning = "default"/pruning = "nothing"/g' $MAIN_PATH_CONFIG/app.toml + sed -i.backup 's/minimum-gas-prices = "0stake"/minimum-gas-prices = "10'$BASE_DENOM'"/g' $MAIN_PATH_CONFIG/app.toml sed -i.backup '/\[telemetry\]/,+8 s/enabled = false/enabled = true/' $MAIN_PATH_CONFIG/app.toml sed -i.backup '/\[telemetry\]/,+20 s/prometheus-retention-time = 0/prometheus-retention-time = 60/' $MAIN_PATH_CONFIG/app.toml sed -i.backup '/global-labels = \[/a\ \["chain_id", "'$CHAIN_ID'"\], ' $MAIN_PATH_CONFIG/app.toml - sed -i.backup 's/timeout_propose = ".*"/timeout_propose = "3s"/g' $MAIN_PATH_CONFIG/app.toml - sed -i.backup 's/timeout_propose_delta = ".*"/timeout_propose_delta = "500ms"/g' $MAIN_PATH_CONFIG/app.toml - sed -i.backup 's/timeout_prevote = ".*"/timeout_prevote = "1s"/g' $MAIN_PATH_CONFIG/app.toml - sed -i.backup 's/timeout_prevote_delta = ".*"/timeout_prevote_delta = "500ms"/g' $MAIN_PATH_CONFIG/app.toml - sed -i.backup 's/timeout_precommit = ".*"/timeout_precommit = "1s"/g' $MAIN_PATH_CONFIG/app.toml - sed -i.backup 's/timeout_precommit_delta = ".*"/timeout_precommit_delta = "500ms"/g' $MAIN_PATH_CONFIG/app.toml - sed -i.backup 's/timeout_commit = ".*"/timeout_commit = "5s"/g' $MAIN_PATH_CONFIG/app.toml - + sed -i.backup 's/timeout_propose = ".*"/timeout_propose = "3s"/g' $MAIN_PATH_CONFIG/app.toml + sed -i.backup 's/timeout_propose_delta = ".*"/timeout_propose_delta = "500ms"/g' $MAIN_PATH_CONFIG/app.toml + sed -i.backup 's/timeout_prevote = ".*"/timeout_prevote = "1s"/g' $MAIN_PATH_CONFIG/app.toml + sed -i.backup 's/timeout_prevote_delta = ".*"/timeout_prevote_delta = "500ms"/g' $MAIN_PATH_CONFIG/app.toml + sed -i.backup 's/timeout_precommit = ".*"/timeout_precommit = "1s"/g' $MAIN_PATH_CONFIG/app.toml + sed -i.backup 's/timeout_precommit_delta = ".*"/timeout_precommit_delta = "500ms"/g' $MAIN_PATH_CONFIG/app.toml + sed -i.backup 's/timeout_commit = ".*"/timeout_commit = "5s"/g' $MAIN_PATH_CONFIG/app.toml # configure config settings - sed -i.backup 's/laddr = "tcp:\/\/127.0.0.1:26657"/laddr = "tcp:\/\/0.0.0.0:26657"/g' $MAIN_PATH_CONFIG/config.toml - sed -i.backup 's/proxy_app = "tcp:\/\/127.0.0.1:26658"/proxy_app = "tcp:\/\/127.0.0.1:26658"/g' $MAIN_PATH_CONFIG/config.toml - sed -i.backup 's/cors_allowed_origins = \[\]/cors_allowed_origins = \["*"\]/g' $MAIN_PATH_CONFIG/config.toml - sed -i.backup 's/max_num_inbound_peers = 40/max_num_inbound_peers = 120/g' $MAIN_PATH_CONFIG/config.toml - sed -i.backup 's/max_num_outbound_peers = 10/max_num_outbound_peers = 60/g' $MAIN_PATH_CONFIG/config.toml + sed -i.backup 's/laddr = "tcp:\/\/127.0.0.1:26657"/laddr = "tcp:\/\/0.0.0.0:26657"/g' $MAIN_PATH_CONFIG/config.toml + sed -i.backup 's/proxy_app = "tcp:\/\/127.0.0.1:26658"/proxy_app = "tcp:\/\/127.0.0.1:26658"/g' $MAIN_PATH_CONFIG/config.toml + sed -i.backup 's/cors_allowed_origins = \[\]/cors_allowed_origins = \["*"\]/g' $MAIN_PATH_CONFIG/config.toml + sed -i.backup 's/max_num_inbound_peers = 40/max_num_inbound_peers = 120/g' $MAIN_PATH_CONFIG/config.toml + sed -i.backup 's/max_num_outbound_peers = 10/max_num_outbound_peers = 60/g' $MAIN_PATH_CONFIG/config.toml } function update_genesis_json() { local jq_command=$1 local file_path=${2:-"$MAIN_PATH_CONFIG/genesis.json"} - jq "$jq_command" "$file_path" > "${file_path}.tmp" && mv "${file_path}.tmp" "$file_path" + jq "$jq_command" "$file_path" >"${file_path}.tmp" && mv "${file_path}.tmp" "$file_path" } function configure_genesis() { - local staking_min_deposit="$1" - local total_supply="$2" + local staking_min_deposit="$1" # 5000000000000000000000 + local total_supply="$2" # 1000000000000000000000000 local faucet_address="$3" local voting_period="60s" local unbonding_time="30s" @@ -144,7 +138,7 @@ function configure_genesis() { "uri_hash": "" }' update_genesis_json '.app_state.bank.send_enabled[0] = {"denom": "'$BASE_DENOM'", "enabled": true}' -# update_genesis_json '.app_state.bank.supply[0] = {"denom": "'$BASE_DENOM'", "amount": "'$total_supply'"}' + # update_genesis_json '.app_state.bank.supply[0] = {"denom": "'$BASE_DENOM'", "amount": "'$total_supply'"}' # EVM params update_genesis_json '.app_state.evm.params.evm_denom = "'$BASE_DENOM'"' @@ -169,8 +163,8 @@ function add_genesis_account() { local amount="$2" gala add-genesis-account \ - "$(gala keys show $account_name -a --keyring-dir $MAIN_PATH_HOME)" \ - $amount + "$(gala keys show $account_name -a --keyring-dir $MAIN_PATH_HOME)" \ + $amount } function initialize_validator() { @@ -181,36 +175,26 @@ function initialize_validator() { local validator_home=$MAIN_PATH_HOME/validators/$moniker gala init \ - $moniker \ - --chain-id $CHAIN_ID \ - --default-denom $BASE_DENOM \ - --home $validator_home + $moniker \ + --chain-id $CHAIN_ID \ + --default-denom $BASE_DENOM \ + --home $validator_home cp $MAIN_PATH_CONFIG/genesis.json $validator_home/config/genesis.json gala gentx \ - $moniker \ - $staking_amount \ - --ip $ip \ - --p2p-port $p2p_port \ - --home $validator_home \ - --keyring-dir $MAIN_PATH_HOME \ - --keyring-backend $KEYRING_BACKEND + $moniker \ + $staking_amount \ + --ip $ip \ + --p2p-port $p2p_port \ + --home $validator_home \ + --keyring-dir $MAIN_PATH_HOME \ + --keyring-backend $KEYRING_BACKEND mkdir -p $MAIN_PATH_CONFIG/gentx/ cp $validator_home/config/gentx/* $MAIN_PATH_CONFIG/gentx/ } -function collect_gentxs() { - gala collect-gentxs - rm $MAIN_PATH_CONFIG/node_key.json - rm $MAIN_PATH_CONFIG/priv_validator_key.json -} - -function validate_genesis() { - gala validate-genesis -} - function configure_validator() { local moniker=$1 local ip_address=$2 @@ -225,18 +209,21 @@ function configure_validator() { # Filter out the IP address of the current validator from persistent_peers local persistent_peers=$(cat $validator_home/config/config.toml | grep persistent_peers | cut -d '=' -f 2 | tr -d '"') - IFS=',' read -ra parts <<< "$persistent_peers" + IFS=',' read -ra parts <<<"$persistent_peers" filtered_parts=() for part in "${parts[@]}"; do if [[ ! "$part" =~ $ip_address ]]; then filtered_parts+=("$part") fi done - local new_persistent_peers=$(IFS=','; echo "${filtered_parts[*]}") + local new_persistent_peers=$( + IFS=',' + echo "${filtered_parts[*]}" + ) echo "Validator $moniker persistent_peers: $new_persistent_peers" sed -i.backup "s/\(persistent_peers *= *\"\).*\(\" *\)/\1$new_persistent_peers\2/" $validator_home/config/config.toml - sed -i.backup 's/moniker = "localtestnet"/moniker = "'$moniker'"/g' $validator_home/config/config.toml + sed -i.backup 's/moniker = "localtestnet"/moniker = "'$moniker'"/g' $validator_home/config/config.toml local key=$(gala keys unsafe-export-eth-key --keyring-backend test --keyring-dir ./$MAIN_PATH_HOME $moniker) yes '00000000' | gala keys unsafe-import-eth-key --keyring-backend test --keyring-dir ./$MAIN_PATH_HOME/validators/$moniker $moniker $key --chain-id $CHAIN_ID @@ -246,7 +233,7 @@ function configure_faucet() { local key=$(gala keys unsafe-export-eth-key --keyring-backend test --keyring-dir ./$MAIN_PATH_HOME faucet) yes '00000000' | gala keys unsafe-import-eth-key --keyring-backend test --keyring-dir ./$MAIN_PATH_HOME/faucet faucet $key --chain-id $CHAIN_ID - echo $key > ./$MAIN_PATH_HOME/faucet/PRIVATE_KEY + echo $key >./$MAIN_PATH_HOME/faucet/PRIVATE_KEY } function main() { @@ -262,12 +249,11 @@ function main() { add_key "reticulum01" add_key "reticulum02" add_key "reticulum03" - add_key "vlval" add_key "treasury" add_key "faucet" add_key_predefined - init_localtestnet + gala init localtestnet --chain-id $CHAIN_ID --default-denom $BASE_DENOM configure_app local faucet_address_bech32=$(gala keys show faucet -a --keyring-dir $MAIN_PATH_HOME) @@ -280,23 +266,27 @@ function main() { # Add genesis accounts add_genesis_account "reticulum01" "10000000000000000000000$BASE_DENOM" add_genesis_account "reticulum02" "10000000000000000000000$BASE_DENOM" + add_genesis_account "reticulum03" "10000000000000000000000$BASE_DENOM" + add_genesis_account "faucet" "10000000000000000000000$BASE_DENOM" add_genesis_account "localkey" "10000000000000000000000$BASE_DENOM" add_genesis_account "treasury" "950000000000000000000000$BASE_DENOM" - add_genesis_account "vlval" "10000000000000000000000$BASE_DENOM" - # Initialize validators - initialize_validator "reticulum01" "127.0.1.1" 26656 "9000000000000000000000$BASE_DENOM" - initialize_validator "reticulum02" "127.0.1.2" 26656 "9000000000000000000000$BASE_DENOM" - initialize_validator "vlval" "95.154.64.137" 26656 "9000000000000000000000$BASE_DENOM" + initialize_validator "reticulum01" "127.0.0.2" 26656 "9000000000000000000000$BASE_DENOM" + initialize_validator "reticulum02" "127.0.0.3" 26656 "9000000000000000000000$BASE_DENOM" + initialize_validator "reticulum03" "127.0.0.4" 26656 "9000000000000000000000$BASE_DENOM" - collect_gentxs - validate_genesis + gala collect-gentxs + + rm $MAIN_PATH_CONFIG/node_key.json + rm $MAIN_PATH_CONFIG/priv_validator_key.json + + gala validate-genesis - configure_validator "reticulum01" "127.0.1.1" - configure_validator "reticulum02" "127.0.1.2" - configure_validator "vlval" "95.154.64.137" + configure_validator "reticulum01" "127.0.0.2" + configure_validator "reticulum02" "127.0.0.3" + configure_validator "reticulum03" "127.0.0.4" configure_faucet } diff --git a/tests/Makefile b/tests/Makefile new file mode 100644 index 0000000..3f6dd9c --- /dev/null +++ b/tests/Makefile @@ -0,0 +1,2 @@ +test: ## some tests + cd tests/galacli && $(MAKE) diff --git a/tests/galacli/.gitignore b/tests/galacli/.gitignore index c8dc11c..c4f4f28 100644 --- a/tests/galacli/.gitignore +++ b/tests/galacli/.gitignore @@ -2,3 +2,4 @@ data config node0* +__pycache__ diff --git a/tests/galacli/Makefile b/tests/galacli/Makefile new file mode 100644 index 0000000..c52ed11 --- /dev/null +++ b/tests/galacli/Makefile @@ -0,0 +1,20 @@ +uname=$(shell uname -s) +is_darwin :=$(filter Darwin,$(uname)) + +all: $(if $(is_darwin),network,) clean start + +clean: + rm -rf node0* + +.venv/bin/python: + uv venv + uv sync + +start: .venv/bin/python + .venv/bin/python galacli.py + +ifeq ($(uname),Darwin) +network: /tmp/127.0.0.2 /tmp/127.0.0.3 /tmp/127.0.0.4 +/tmp/127.0.0.%: + sudo ifconfig lo0 alias $(shell basename $@) up && touch $@ +endif diff --git a/tests/galacli/galacli.py b/tests/galacli/galacli.py index 95829a1..8f21921 100644 --- a/tests/galacli/galacli.py +++ b/tests/galacli/galacli.py @@ -1,9 +1,14 @@ from collections import defaultdict +import os +import shutil import subprocess +from typing import Dict, List from dateutil.parser import isoparse import json import time import socket +import re + # import os import sys @@ -24,8 +29,96 @@ DEFAULT_TEST_MONIKER = "test-node01" DEFAULT_TEST_CHAINID = "test_41239-41239" -PREDEFINED_KEY_MNEMONIC = "gesture inject test cycle original hollow east ridge hen combine junk child bacon zero hope comfort vacuum milk pitch cage oppose unhappy lunar seat" +PREDEFINED_KEY_MNEMONIC_TREASURY = "gesture inject test cycle original hollow east ridge hen combine junk child bacon zero hope comfort vacuum milk pitch cage oppose unhappy lunar seat" PREDEFINED_KEY_MNEMONIC_FAUCET = "heart grape ignore face equip monkey keep armor tumble donkey final horror harsh way retire this enforce pave there unfair scrap shine physical since" +PREDEFINED_KEY_MNEMONIC_NODE_KEYS = [ + "kick treat protect present permit business own nuclear ranch ancient around deposit dignity cabin kiwi parade sister market must crime tag update yellow theory", + "minimum sing arrow way comfort obvious purse piece reward simple fitness fence october dutch genius spike sunset empower limit dog dutch kid online file", + "uniform spread february wife quality device mix fish rapid win improve van eagle target icon home charge birth reward slogan season robust thunder over", +] + + +class GnetAmount: + DENOMINATIONS = {"agnet": 1, "ugnet": 1e12, "mgnet": 1e15, "gnet": 1e18} + + def __init__(self, amount): + if isinstance(amount, float): + self.amount = amount + else: + match = re.match(r"([\d\.]+)(agnet|ugnet|mgnet|gnet)", amount) + if match: + self.amount = float(match.group(1)) * self.DENOMINATIONS[match.group(2)] + else: + raise ValueError(f"Invalid amount format: {amount}") + + def __repr__(self): + for denomination, value in sorted( + self.DENOMINATIONS.items(), key=lambda item: -item[1] + ): + if self.amount % value == 0: + return f"{self.__class__.__name__}({int(self.amount // value)}{denomination})" + + def __str__(self): + for denomination, value in sorted( + self.DENOMINATIONS.items(), key=lambda item: item[1] + ): + if self.amount % value == 0: + return f"{str(int(self.amount // value))}{denomination}" + + def __format__(self, format_spec): + return str(self) + + def __add__(self, other): + if not isinstance(other, GnetAmount): + other = GnetAmount(other) + return GnetAmount(self.amount + other.amount) + # else: + # raise TypeError( + # f"unsupported operand type(s) for +: '{self.__class__.__name__}' and '{type(other).__name__}'" + # ) + + def __sub__(self, other): + if isinstance(other, GnetAmount): + return GnetAmount(self.amount - other.amount) + else: + raise TypeError( + f"unsupported operand type(s) for -: '{self.__class__.__name__}' and '{type(other).__name__}'" + ) + + def __eq__(self, other): + if isinstance(other, GnetAmount): + return self.amount == other.amount + else: + return False + + def __lt__(self, other): + if isinstance(other, GnetAmount): + return self.amount < other.amount + else: + raise TypeError( + f"unsupported operand type(s) for <: '{self.__class__.__name__}' and '{type(other).__name__}'" + ) + + def __mul__(self, other): + if isinstance(other, (int, float)): + return GnetAmount(self.amount * other) + else: + raise TypeError( + f"unsupported operand type(s) for *: '{self.__class__.__name__}' and '{type(other).__name__}'" + ) + + def __truediv__(self, other): + if isinstance(other, (int, float)): + return GnetAmount(self.amount / other) + elif isinstance(other, (GnetAmount)): + return float(self.amount / other.amount) + else: + raise TypeError( + f"unsupported operand type(s) for /: '{self.__class__.__name__}' and '{type(other).__name__}'" + ) + + def min_denom_amount_str(self) -> str: + return str(int(self.amount)) def wait_for_port(port, host="127.0.0.1", timeout=40.0): @@ -76,7 +169,7 @@ def interact(cmd, ignore_error=False, input=None, **kwargs): **kwargs, ) # begin = time.perf_counter() - (stdout, _) = proc.communicate(input=input) + stdout, _ = proc.communicate(input=input) # print('[%.02f] %s' % (time.perf_counter() - begin, cmd)) if not ignore_error: assert proc.returncode == 0, f'{stdout.decode("utf-8")} ({cmd})' @@ -260,6 +353,11 @@ def save(self): with open(self.path, "w") as file: json.dump(self.config, file) + def save_to(self, path): + "save config to self.path file in toml format" + with open(path, "w") as file: + json.dump(self.config, file) + def deep_update(self, original, new): "Recursive update of nested dictionaries" for key, value in new.items(): @@ -301,6 +399,12 @@ def __init__( self.output_format = output_format self.broadcast_mode = broadcast_mode self.error = None + self.config = None + self.app_config = None + self.client_config = None + self.load_config() + + def load_config(self): if Path(self.data_dir / "config/config.toml").exists(): self.config = GalaToml(self.data_dir / "config/config.toml") if Path(self.data_dir / "config/app.toml").exists(): @@ -397,6 +501,7 @@ def create_account(self, name, mnemonic=None): "add", name, home=self.data_dir, + algo="eth_secp256k1", output="json", keyring_backend=self.keyring_backend, ) @@ -428,17 +533,17 @@ def delete_account(self, name): ) ############################## - # Tendermint + # Tendermint => Cometbft ############################## - def consensus_address(self): - "get tendermint consensus address" - output = self.raw("tendermint", "show-address", home=self.data_dir) + def consensus_address(self) -> str: + "get comet consensus address" + output = self.raw("comet", "show-address", home=self.data_dir) return output.decode().strip() - def node_id(self): - "get tendermint node id" - output = self.raw("tendermint", "show-node-id", home=self.data_dir) + def node_id(self) -> str: + "get comet node id" + output = self.raw("comet", "show-node-id", home=self.data_dir) return output.decode().strip() def export(self): @@ -503,6 +608,7 @@ def __init__( # node_id=None, moniker=DEFAULT_TEST_MONIKER, keyring_backend="test", + node_addr="127.0.0.1", ): super().__init__( cmd=cmd, @@ -513,17 +619,58 @@ def __init__( ) # self.node_id = node_id self.moniker = moniker + self.node_addr = node_addr + self.account = None + self.node_rpc = f"tcp://{self.node_addr}:26657" self.process = None - def start(self): ... + def initial_configure_node(self): + self.raw("config", "set", "client", "keyring-backend", "test") + self.load_config() + self.client_config.edit({"output": "json", "chain-id": self.chain_id}) + self.config.edit({"moniker": self.moniker}) + ## set self network addr + self.config.apply_addr(self.node_addr) + self.client_config.apply_addr(self.node_addr) + self.app_config.apply_addr(self.node_addr) + ## other initial config + self.app_config.edit({"api": {"enable": True}}) + self.app_config.edit({"pruning": "nothing"}) + self.app_config.edit({"minimum-gas-prices": f"10{DEFAULT_DENOM}"}) + self.app_config.edit( + { + "telemetry": { + "service-name": "galacticad", + "enabled": True, + "prometheus-retention-time": "60", + "global-labels": [["chain-id", self.chain_id]], + } + } + ) + self.config.edit( + {"moniker": self.moniker, "log_format": "json", "log_level": "debug"} + ) + self.config.edit({"consensus": {"timeout_commit": "1s"}}) + self.config.edit( + { + "rpc": { + "cors_allowed_origins": [ + "*", + ] + } + } + ) + + async def start(self): + await self.run("start", home=self.data_dir, chain_id=self.chain_id) def node_info(self): return requests.get( f"{self.node_rpc_http}/cosmos/staking/v1beta1/validators/{self.node_id}" ).json() - def init(self, moniker=None): - "generate initial config with genesis.json" + def init_node(self, moniker=None): + "### Generate initial config with genesis.json" moniker = moniker or self.moniker or DEFAULT_TEST_MONIKER return self.raw( "init", @@ -568,145 +715,157 @@ async def terminate(self, timeout=30): await self.process.wait() return self.process.returncode + def set_address_in_configs(self, addr: str): + for c in (self.client_config, self.app_config, self.config): + if c: + c.apply_addr(addr) -async def main(): - chain_id = DEFAULT_TEST_CHAINID - moniker = "test-node01" - g_client = GalaClientConfig("/dev/null") - g_client.config.update( - dict( - chain_id="test_41239-41239", - keyring_backend="test", - output="json", - node="tcp://127.0.0.2:26657", - broadcast_mode="sync", - ) - ) - gn1 = GalaNodeCLI( - data_dir="node01", - chain_id=chain_id, - moniker=moniker, - node_rpc="tcp://127.0.0.2:26657", - ) - gn1.init() - - gn1.client_config = GalaClientConfig(gn1.data_dir / "./config/client.toml") - gn1.client_config.edit( - { - "chain-id": chain_id, - "keyring-backend": "test", - "output": "json", - } - ) +class GalaNetwork: + "### Bunch of GalaNodes with some similar parameters" - gn1.app_config = GalaToml(gn1.data_dir / "./config/app.toml") - gn1.app_config.apply_addr("127.0.0.2") - gn1.app_config.edit({"api": {"enable": True}}) - gn1.app_config.edit({"pruning": "nothing"}) - gn1.app_config.edit({"minimum-gas-prices": f"10{DEFAULT_DENOM}"}) - gn1.app_config.edit( - { - "telemetry": { - "service-name": "galacticad", - "enabled": True, - "prometheus-retention-time": "60", - "global-labels": [["chain-id", DEFAULT_TEST_CHAINID]], - } - } - ) + def __init__(self, n_nodes=3, chain_id=DEFAULT_TEST_CHAINID, *args, **kwargs): + self.chain_id = chain_id + self.nodes: List[GalaNodeCLI] = [] + self.command_node = GalaNodeCLI( + data_dir="node00", moniker="node00", chain_id=chain_id + ) + for n in range(n_nodes): + name = f"node0{n + 1}" + self.nodes.append( + GalaNodeCLI( + moniker=name, + data_dir=name, + node_addr=f"127.0.0.{ 2 + n }", ## 127.0.0.2 127.0.0.3 ... + chain_id=self.chain_id, + *args, + **kwargs, + ) + ) - gn1.config = GalaToml(gn1.data_dir / "./config/config.toml") - gn1.config.edit({"moniker": "test-node01", "log_format": "json"}) - gn1.config.apply_addr("127.0.0.2") - gn1.config.edit({"consensus": {"timeout_commit": "1s"}}) - gn1.config.edit( - { - "rpc": { - "cors_allowed_origins": [ - "*", - ] + async def initial_configure_network(self): + for n in self.nodes: + await n.initial_configure_node() + + async def start(self): + "### Start every node in network" + for node in self.nodes: + await node.start() + + async def stop(self): + "### Stop every node in network" + for node in self.nodes: + await node.terminate() + + async def check_live(self): + "### Check that node is running" + ... + + def configure_genesis(self): + # total_supply = str(GnetAmount("200gnet")) ## will be calculated later + staking_min_deposit = GnetAmount("100gnet").min_denom_amount_str() + max_deposit_period = "600s" + unbonding_time = "30s" + ## faucet initialzed here because its address neede in genesis minting config + self.faucet = self.command_node.create_account( + "faucet", PREDEFINED_KEY_MNEMONIC_FAUCET + ) + faucet_address = self.faucet["address"] + inflation_validators_share = "0.99933" + inflation_faucet_share = "0.00067" + + block_max_gas = "40000000" + block_max_bytes = "22020096" + time_iota_ms = "1000" + voting_period = "60s" + expedited_voting_period = "30s" + genesis = Genesis(path=self.command_node.data_dir / "config/genesis.json") + genesis.edit( + { + "consensus": { + "params": { + "block": { + "max_bytes": block_max_bytes, + "max_gas": block_max_gas, + "time_iota_ms": time_iota_ms, + } + } + } } - } - ) - # d = andr_app_config.diff(app_config) - print("config created") - print("configure genesis") - - total_supply = str(int(200e18)) - staking_min_deposit = str(int(100e18)) - max_deposit_period = "600s" - unbonding_time = "30s" - - faucet = gn1.create_account("faucet", PREDEFINED_KEY_MNEMONIC_FAUCET) - faucet_address = faucet["address"] - inflation_validators_share = "0.99933" - inflation_faucet_share = "0.00067" - - block_max_gas = "40000000" - block_max_bytes = "22020096" - time_iota_ms = "1000" - voting_period = "60s" - expedited_voting_period = "30s" - genesis = Genesis(path=gn1.data_dir / "config/genesis.json") - genesis.edit({"consensus": {"params": {"block": {"max_bytes": block_max_bytes}}}}) - genesis.edit({"consensus": {"params": {"block": {"max_gas": block_max_gas}}}}) - genesis.edit({"consensus": {"params": {"block": {"time_iota_ms": time_iota_ms}}}}) - genesis.edit( - {"app_state": {"gov": {"voting_params": {"voting_period": voting_period}}}} - ) + ) + genesis.edit( + {"app_state": {"gov": {"voting_params": {"voting_period": voting_period}}}} + ) - update_genesis = { - "app_state": { - "gov": { - "deposit_params": { - "min_deposit": [ - {"denom": BASE_DENOM, "amount": staking_min_deposit} - ] + update_genesis = { + "app_state": { + "gov": { + "deposit_params": { + "min_deposit": [ + {"denom": BASE_DENOM, "amount": staking_min_deposit} + ] + }, + "params": { + "min_deposit": [ + {"denom": BASE_DENOM, "amount": staking_min_deposit} + ], + "max_deposit_period": max_deposit_period, + "voting_period": voting_period, + "expedited_voting_period": expedited_voting_period, + }, + }, + "staking": { + "params": { + "bond_denom": BASE_DENOM, + "unbonding_time": unbonding_time, + } }, - "params": { - "min_deposit": [ - {"denom": BASE_DENOM, "amount": staking_min_deposit} + "crisis": {"constant_fee": {"denom": BASE_DENOM}}, + "mint": {"params": {"mint_denom": BASE_DENOM}}, + "bank": { + "denom_metadata": [ + { + "description": "The native staking token of the Galactica Network.", + "denom_units": [ + { + "denom": BASE_DENOM, + "exponent": 0, + "aliases": ["attognet"], + }, + { + "denom": "ugnet", + "exponent": 6, + "aliases": ["micrognet"], + }, + {"denom": DISPLAY_DENOM, "exponent": 18}, + ], + "base": BASE_DENOM, + "display": DISPLAY_DENOM, + "name": "Galactica Network", + "symbol": DISPLAY_DENOM.upper(), + "uri": "", + "uri_hash": "", + } ], - "max_deposit_period": max_deposit_period, - "voting_period": voting_period, - "expedited_voting_period": expedited_voting_period, + "send_enabled": [{"denom": BASE_DENOM, "enabled": True}], + # "supply": [{"denom": BASE_DENOM, "amount": total_supply}], }, - }, - "staking": { - "params": {"bond_denom": BASE_DENOM, "unbonding_time": unbonding_time} - }, - "crisis": {"constant_fee": {"denom": BASE_DENOM}}, - "mint": {"params": {"mint_denom": BASE_DENOM}}, - "bank": { - "denom_metadata": [ - { - "description": "The native staking token of the Galactica Network.", - "denom_units": [ - { - "denom": BASE_DENOM, - "exponent": 0, - "aliases": ["attognet"], - }, - {"denom": "ugnet", "exponent": 6, "aliases": ["micrognet"]}, - {"denom": DISPLAY_DENOM, "exponent": 18}, - ], - "base": BASE_DENOM, - "display": DISPLAY_DENOM, - "name": "Galactica Network", - "symbol": DISPLAY_DENOM.upper(), - "uri": "", - "uri_hash": "", - } - ], - "send_enabled": [{"denom": BASE_DENOM, "enabled": True}], - "supply": [{"denom": BASE_DENOM, "amount": total_supply}], - }, - "evm": {"params": {"evm_denom": BASE_DENOM}}, - "inflation": { - "params": { - "enable_inflation": True, - "mint_denom": BASE_DENOM, + "evm": {"params": {"evm_denom": BASE_DENOM}}, + "inflation": { + "params": { + "enable_inflation": True, + "mint_denom": BASE_DENOM, + "inflation_distribution": { + "validators_share": inflation_validators_share, + "other_shares": [ + { + "address": faucet_address, + "name": "faucet", + "share": inflation_faucet_share, + } + ], + }, + }, "inflation_distribution": { "validators_share": inflation_validators_share, "other_shares": [ @@ -718,44 +877,160 @@ async def main(): ], }, }, - "inflation_distribution": { - "validators_share": inflation_validators_share, - "other_shares": [ - { - "address": faucet_address, - "name": "faucet", - "share": inflation_faucet_share, - } - ], - }, }, - }, - } - genesis.edit(update_genesis) - print("add some genesis accounts") - test_node01_acc = gn1.create_account("test-node01") - # total_supply = 910e18 - gn1.add_genesis_account(test_node01_acc["address"], str(int(200e18)) + BASE_DENOM) - gn1.gentx( - "test-node01", - str(int(150e18)) + BASE_DENOM, - ip="127.0.0.2", - commission_rate="0.02", - details="test.node01.details", - ) - gn1.collect_gentxs(gn1.data_dir / "config/gentx") - gn1.validate_genesis() - print("start node...") + } + genesis.edit(update_genesis) + + def combine_seeds(self) -> Dict: + """ + ### Получение строк которые лягут в seeds или в persistent_peers + """ + not_combined = { + node.moniker: node.node_id() + "@" + node.node_addr + ":26656" + for node in self.nodes + } + combined = { + node.moniker: ",".join( + [not_combined[n] for n in not_combined if n != node.moniker] + ) + for node in self.nodes + } + return combined + + def configure_network(self): + """ + ### func for configuring gala network + + - [X] Init first node to get blank genesis.json + - [X] Edit config files via first node to set common config + - [X] Configure genesis to needed state + - [X] edit genesis.json + - [X] add some gentx + - [X] collect gentx + - [X] validate genesis + - [ ] Get tendermint node-id of each node + - [X] Put node folder + - [X] configure node + - [X] put key into node + - [X] put genesis.json to node config + - [ ] get tendermint node-id + - [ ] Edit individual configs to set some parameters throgh network + - [ ] Persistent peers + """ + + self.command_node.init_node( + moniker=self.command_node.moniker, + ) + self.configure_genesis() - await asyncio.sleep(1) - await gn1.run("start", home=gn1.data_dir, chain_id=gn1.chain_id) - await asyncio.sleep(1) - print(gn1.is_running()) - gn1.wait_for_block(5) - # await gn1.terminate() - # await asyncio.sleep(10) - if await gn1.terminate() == 0: - print("Success") + ## Create treasury account + treasury_acc = self.command_node.create_account( + "treasury", mnemonic=PREDEFINED_KEY_MNEMONIC_TREASURY + ) + if not self.faucet: + faucet_acc = self.command_node.create_account( + "faucet", mnemonic=PREDEFINED_KEY_MNEMONIC_FAUCET + ) + else: + faucet_acc = self.faucet + total_supply = GnetAmount("0gnet") + self.command_node.add_genesis_account( + treasury_acc["address"], GnetAmount("800gnet") + ) + self.command_node.add_genesis_account( + faucet_acc["address"], GnetAmount("100gnet") + ) + + total_supply += GnetAmount("800gnet") + GnetAmount("100gnet") + self.genesis = Genesis(self.command_node.data_dir / "config/genesis.json") + + ## Create accounts for nodes + for num, node in enumerate(self.nodes): + node.init_node() + self.genesis.save_to(node.data_dir / "config/genesis.json") + + node_genesis_supply = GnetAmount("200gnet") + node.account = node.create_account( + node.moniker, mnemonic=PREDEFINED_KEY_MNEMONIC_NODE_KEYS[num] + ) + node.add_genesis_account(node.account["address"], node_genesis_supply) + self.command_node.add_genesis_account( + node.account["address"], node_genesis_supply + ) + node.gentx( + node.moniker, + node_genesis_supply, + ip=node.node_addr, + commission_rate="0.02", + details=f"test.{node.moniker}.details", + ) + + total_supply += node_genesis_supply + + gentx_dir = self.command_node.data_dir / "config/gentx" + + os.makedirs(gentx_dir, exist_ok=True) + + for node in self.nodes: + node_gentx_dir = node.data_dir / "config/gentx" + for file_name in os.listdir(node_gentx_dir): + if file_name.endswith(".json"): + file_path = node_gentx_dir / file_name + dest_path = gentx_dir / file_name + shutil.move(file_path, dest_path) + + self.genesis.load() + self.genesis.edit( + { + "app_state": { + "bank": { + "supply": [ + { + "denom": BASE_DENOM, + "amount": total_supply.min_denom_amount_str(), + } + ], + }, + }, + } + ) + self.command_node.collect_gentxs(self.command_node.data_dir / "config/gentx") + self.command_node.validate_genesis() + self.genesis.load() + + ## configure nodes + for num, node in enumerate(self.nodes): + self.genesis.save_to(node.data_dir / "config/genesis.json") + node.load_config() + node.initial_configure_node() + node.config.apply_addr(node.node_addr) + + combine_seeds = self.combine_seeds() + for node in self.nodes: + node.config.edit({"p2p": {"persistent_peers": combine_seeds[node.moniker]}}) + + +async def main(): + chain_id = DEFAULT_TEST_CHAINID + g_network = GalaNetwork(3, chain_id=chain_id) + g_network.configure_network() + + print("start node...") + await g_network.start() + await asyncio.sleep(5) + + g_network.nodes[0].wait_for_block(5) + await g_network.stop() + + # await asyncio.sleep(1) + # await gn1.start() + # await asyncio.sleep(1) + # print(gn1.is_running()) + # gn1.wait_for_block(5, timeout=30) + # # await gn1.terminate() + # # await asyncio.sleep(10) + # if await gn1.terminate() == 0: + # print("Success") if __name__ == "__main__": diff --git a/tests/galacli/pyproject.toml b/tests/galacli/pyproject.toml index f8bf814..ffcf2bd 100644 --- a/tests/galacli/pyproject.toml +++ b/tests/galacli/pyproject.toml @@ -5,6 +5,7 @@ description = "Library for testing galacticad binary" readme = "README.md" requires-python = ">=3.12" dependencies = [ + "mnemonic>=0.21", "python-dateutil>=2.9.0.post0", "pyyaml>=6.0.2", "requests>=2.32.3", diff --git a/tests/galacli/uv.lock b/tests/galacli/uv.lock index daabf9b..2f8987d 100644 --- a/tests/galacli/uv.lock +++ b/tests/galacli/uv.lock @@ -54,6 +54,7 @@ name = "galacli" version = "0.1.0" source = { virtual = "." } dependencies = [ + { name = "mnemonic" }, { name = "python-dateutil" }, { name = "pyyaml" }, { name = "requests" }, @@ -64,6 +65,7 @@ dependencies = [ [package.metadata] requires-dist = [ + { name = "mnemonic", specifier = ">=0.21" }, { name = "python-dateutil", specifier = ">=2.9.0.post0" }, { name = "pyyaml", specifier = ">=6.0.2" }, { name = "requests", specifier = ">=2.32.3" }, @@ -81,6 +83,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, ] +[[package]] +name = "mnemonic" +version = "0.21" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ff/77/e6232ed59fbd7b90208bb8d4f89ed5aabcf30a524bc2fb8f0dafbe8e7df9/mnemonic-0.21.tar.gz", hash = "sha256:1fe496356820984f45559b1540c80ff10de448368929b9c60a2b55744cc88acf", size = 152462 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/57/48/5abb16ce7f9d97b728e6b97c704ceaa614362e0847651f379ed0511942a0/mnemonic-0.21-py3-none-any.whl", hash = "sha256:72dc9de16ec5ef47287237b9b6943da11647a03fe7cf1f139fc3d7c4a7439288", size = 92701 }, +] + [[package]] name = "python-dateutil" version = "2.9.0.post0" From ef206fbc5c012c5f1c273cf25e2c3e7c46b6f91c Mon Sep 17 00:00:00 2001 From: Ilya Bygrimov Date: Thu, 14 Nov 2024 00:03:11 +0100 Subject: [PATCH 15/20] dependecies in build & bump version of upload --- .github/workflows/ci.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 7d42caa..8bd33df 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -32,13 +32,15 @@ jobs: - name: Upload binary artifact if: github.event_name == 'release' - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: galacticad path: build/galacticad test: runs-on: self-hosted + needs: + - build steps: - name: Checkout code uses: actions/checkout@v2 From ae35a69e0e0f20cfe9a605462350b5b9cc5d4e8f Mon Sep 17 00:00:00 2001 From: Ilya Bygrimov Date: Thu, 14 Nov 2024 00:46:20 +0100 Subject: [PATCH 16/20] Trying to reuse env --- .github/workflows/ci.yaml | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 8bd33df..1b170d3 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -22,10 +22,13 @@ jobs: - name: Checkout code uses: actions/checkout@v4 + - name: Set GO_VERSION + run: echo "GO_VERSION=$(cat go.mod | grep '^go ' | cut -f2 -d' ')" >> $GITHUB_ENV + - name: Set up Go uses: actions/setup-go@v5 with: - go-version: "1.22" + go-version: ${{ env.GO_VERSION }} - name: Build binary run: make build @@ -43,7 +46,15 @@ jobs: - build steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 + + - name: Set GO_VERSION + run: echo "GO_VERSION=$(cat go.mod | grep '^go ' | cut -f2 -d' ')" >> $GITHUB_ENV + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: ${{ env.GO_VERSION }} - uses: astral-sh/setup-uv@v3 with: @@ -61,7 +72,7 @@ jobs: manual-tests: runs-on: self-hosted - needs: test + needs: build if: github.event_name == 'workflow_dispatch' steps: - name: Checkout code From fd1f252df68d4f5f917d2a81c63341332385fed0 Mon Sep 17 00:00:00 2001 From: Ilya Bygrimov Date: Thu, 14 Nov 2024 00:59:34 +0100 Subject: [PATCH 17/20] without mannual and upload artifact every time --- .github/workflows/ci.yaml | 30 ++++++------------------------ 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 1b170d3..649de06 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -34,7 +34,7 @@ jobs: run: make build - name: Upload binary artifact - if: github.event_name == 'release' + # if: github.event_name == 'release' uses: actions/upload-artifact@v4 with: name: galacticad @@ -47,14 +47,12 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v4 - - - name: Set GO_VERSION - run: echo "GO_VERSION=$(cat go.mod | grep '^go ' | cut -f2 -d' ')" >> $GITHUB_ENV - - - name: Set up Go - uses: actions/setup-go@v5 + + - name: Download binary artifact + uses: actions/download-artifact@v4 with: - go-version: ${{ env.GO_VERSION }} + name: galacticad + path: build - uses: astral-sh/setup-uv@v3 with: @@ -69,19 +67,3 @@ jobs: permissions: pull-requests: write - - manual-tests: - runs-on: self-hosted - needs: build - if: github.event_name == 'workflow_dispatch' - steps: - - name: Checkout code - uses: actions/checkout@v2 - - - name: Set up Go - uses: actions/setup-go@v3 - with: - go-version: "1.17" - - - name: Run manual tests - run: go test ./... From a74ecc636dad7c60071b6a3bf99911dff65fc651 Mon Sep 17 00:00:00 2001 From: Ilya Bygrimov Date: Thu, 14 Nov 2024 01:04:34 +0100 Subject: [PATCH 18/20] chmod +x build/galacticad --- tests/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/Makefile b/tests/Makefile index 3f6dd9c..125d3d2 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -1,2 +1,3 @@ test: ## some tests + chmod +x build/galacticad cd tests/galacli && $(MAKE) From 019794b95a4cc4a8c3af126703c4ef5b374cca45 Mon Sep 17 00:00:00 2001 From: Ilya Bygrimov Date: Fri, 15 Nov 2024 12:28:36 +0100 Subject: [PATCH 19/20] Test begin to work --- tests/galacli/.gitignore | 1 + tests/galacli/Makefile | 14 ++- tests/galacli/galacli.py | 145 +++++++++++++++----------- tests/galacli/pyproject.toml | 11 ++ tests/galacli/test_network.py | 46 ++++++++ tests/galacli/uv.lock | 190 ++++++++++++++++++++++++++++++++++ 6 files changed, 345 insertions(+), 62 deletions(-) create mode 100644 tests/galacli/test_network.py diff --git a/tests/galacli/.gitignore b/tests/galacli/.gitignore index c4f4f28..bec8674 100644 --- a/tests/galacli/.gitignore +++ b/tests/galacli/.gitignore @@ -3,3 +3,4 @@ data config node0* __pycache__ +.pytest_cache diff --git a/tests/galacli/Makefile b/tests/galacli/Makefile index c52ed11..c7c0ee2 100644 --- a/tests/galacli/Makefile +++ b/tests/galacli/Makefile @@ -1,17 +1,25 @@ uname=$(shell uname -s) is_darwin :=$(filter Darwin,$(uname)) -all: $(if $(is_darwin),network,) clean start +all: $(if $(is_darwin),network,) clean start clean: - rm -rf node0* + rm -rf node0* .pytest_cache __pycache__ .venv/bin/python: uv venv uv sync start: .venv/bin/python - .venv/bin/python galacli.py + pytest -v . +# .venv/bin/python galacli.py +ps: + -@ps | grep galacticad | grep -v 'grep' + +terminate: ps + ps | grep galacticad | grep -v 'grep' | awk '{print $$1}' | xargs -n1 kill -15 +kill: ps + ps | grep galacticad | grep -v 'grep' | awk '{print $$1}' | xargs -n1 sudo kill -9 ifeq ($(uname),Darwin) network: /tmp/127.0.0.2 /tmp/127.0.0.3 /tmp/127.0.0.4 diff --git a/tests/galacli/galacli.py b/tests/galacli/galacli.py index 8f21921..887c005 100644 --- a/tests/galacli/galacli.py +++ b/tests/galacli/galacli.py @@ -1,7 +1,9 @@ from collections import defaultdict import os import shutil +import signal import subprocess +import threading from typing import Dict, List from dateutil.parser import isoparse import json @@ -18,7 +20,6 @@ import toml from pprint import pprint import urllib.parse -import asyncio DEBUG = True DEFAULT_DENOM = "agnet" @@ -420,7 +421,9 @@ def node_rpc_http(self): return "http" + self.node_rpc.removeprefix("tcp") def status(self): - return json.loads(self.raw("status", node=self.node_rpc)) + return json.loads( + self.raw("status", node=self.node_rpc, chain_id=self.chain_id) + ) def block_height(self): return int(self.status()["sync_info"]["latest_block_height"]) @@ -622,7 +625,7 @@ def __init__( self.node_addr = node_addr self.account = None self.node_rpc = f"tcp://{self.node_addr}:26657" - self.process = None + self.process: subprocess.Popen = None def initial_configure_node(self): self.raw("config", "set", "client", "keyring-backend", "test") @@ -661,8 +664,8 @@ def initial_configure_node(self): } ) - async def start(self): - await self.run("start", home=self.data_dir, chain_id=self.chain_id) + def start(self): + self.run("start", home=self.data_dir, chain_id=self.chain_id) def node_info(self): return requests.get( @@ -679,46 +682,75 @@ def init_node(self, moniker=None): home=self.data_dir, ) - async def run(self, cmd, *args, **kwargs): + def set_address_in_configs(self, addr: str): + for c in (self.client_config, self.app_config, self.config): + if c: + c.apply_addr(addr) + + def run(self, cmd, *args, **kwargs): cmd_args = build_cli_args_safe(cmd, *args, **kwargs) - with open(self.data_dir / "stdout.log", "a") as out, open( - self.data_dir / "stderr.log", "a" - ) as err: - self.process = await asyncio.create_subprocess_exec( - self.cmd, - *cmd_args, - stdout=out, - stderr=err, - ) - return self - async def get_output(self): - stdout, stderr = await self.process.communicate() + def start_process(): + with (self.data_dir / "stdout.log").open("a") as out, ( + self.data_dir / "stderr.log" + ).open("a") as err: + self.process = subprocess.Popen( + [self.cmd] + cmd_args, + stdout=out, + stderr=err, + ) + try: + self.process.wait() + finally: + if self.process.poll() is None: + os.killpg(os.getpgid(self.process.pid), signal.SIGTERM) + self.process.wait() + + thread = threading.Thread(target=start_process) + thread.start() + return self + + def get_output(self): + stdout, stderr = self.process.communicate() return stdout.decode(), stderr.decode(), self.process.returncode def is_running(self): return self.process and self.process.returncode is None - async def terminate(self, timeout=30): - if self.is_running(): - self.process.terminate() + def terminate(self, timeout=30): + if not self.is_running(): + print(f"Instance {self.moniker} is already terminated.") + return 0 + + self.process.terminate() + start_time = time.perf_counter() + + def wait_for_process(): try: - await asyncio.wait_for(self.process.wait(), timeout=timeout) - exit_code = self.process.returncode - print(f"Instance {self.moniker} exited with code {exit_code}") - return exit_code - except asyncio.TimeoutError: - print( - f"Process exceeded timeout of {timeout} seconds. Killing the process." - ) - self.process.kill() - await self.process.wait() - return self.process.returncode + return_code = self.process.wait(timeout=timeout) + print(f"Instance {self.moniker} exited with code {return_code}") + return return_code + except subprocess.TimeoutExpired: + if time.perf_counter() - start_time >= timeout: + print( + f"Process exceeded timeout of {timeout} seconds. Killing the process." + ) + self.process.kill() + return self.process.wait() + return None - def set_address_in_configs(self, addr: str): - for c in (self.client_config, self.app_config, self.config): - if c: - c.apply_addr(addr) + wait_thread = threading.Thread(target=wait_for_process) + wait_thread.start() + + # Ожидаем завершения потока, чтобы гарантировать корректное завершение процесса + wait_thread.join() + + if self.is_running(): + print(f"Instance {self.moniker} failed to terminate.") + return -1 + + print(f"Instance {self.moniker} successfully terminated.") + return self.process.returncode class GalaNetwork: @@ -743,21 +775,25 @@ def __init__(self, n_nodes=3, chain_id=DEFAULT_TEST_CHAINID, *args, **kwargs): ) ) - async def initial_configure_network(self): + def initial_configure_network(self): for n in self.nodes: - await n.initial_configure_node() + n.initial_configure_node() - async def start(self): + def start_network(self): "### Start every node in network" - for node in self.nodes: - await node.start() + return all([node.start() for node in self.nodes]) - async def stop(self): + def stop_network(self): "### Stop every node in network" + return all([node.terminate() for node in self.nodes]) + + def clean(self): + shutil.rmtree(self.command_node.data_dir, ignore_errors=True) + for node in self.nodes: - await node.terminate() + shutil.rmtree(node.data_dir, ignore_errors=True) - async def check_live(self): + def check_live(self): "### Check that node is running" ... @@ -1008,30 +1044,21 @@ def configure_network(self): combine_seeds = self.combine_seeds() for node in self.nodes: node.config.edit({"p2p": {"persistent_peers": combine_seeds[node.moniker]}}) + return self -async def main(): +def main(): chain_id = DEFAULT_TEST_CHAINID g_network = GalaNetwork(3, chain_id=chain_id) g_network.configure_network() print("start node...") - await g_network.start() - await asyncio.sleep(5) + g_network.start_network() + time.sleep(5) g_network.nodes[0].wait_for_block(5) - await g_network.stop() - - # await asyncio.sleep(1) - # await gn1.start() - # await asyncio.sleep(1) - # print(gn1.is_running()) - # gn1.wait_for_block(5, timeout=30) - # # await gn1.terminate() - # # await asyncio.sleep(10) - # if await gn1.terminate() == 0: - # print("Success") + g_network.stop_network() if __name__ == "__main__": - asyncio.run(main()) + main() diff --git a/tests/galacli/pyproject.toml b/tests/galacli/pyproject.toml index ffcf2bd..5d62a8c 100644 --- a/tests/galacli/pyproject.toml +++ b/tests/galacli/pyproject.toml @@ -6,6 +6,8 @@ readme = "README.md" requires-python = ">=3.12" dependencies = [ "mnemonic>=0.21", + "pytest-md-report>=0.6.2", + "pytest>=8.3.3", "python-dateutil>=2.9.0.post0", "pyyaml>=6.0.2", "requests>=2.32.3", @@ -13,3 +15,12 @@ dependencies = [ "toml>=0.10.2", "tomlkit>=0.13.2", ] + +[tool.pytest.ini_options] +md_report_output = "report.md" +md_report = true +md_report_verbose = 0 +md_report_color = "never" + +# asyncio_mode = "auto" +# asyncio_default_fixture_loop_scope = "session" diff --git a/tests/galacli/test_network.py b/tests/galacli/test_network.py new file mode 100644 index 0000000..d41c5ea --- /dev/null +++ b/tests/galacli/test_network.py @@ -0,0 +1,46 @@ +import time +import pytest +from .galacli import DEFAULT_TEST_CHAINID, GalaNetwork + + +@pytest.fixture(scope="module") +def gala_network_fixture(): + chain_id = DEFAULT_TEST_CHAINID + g_network = GalaNetwork(3, chain_id=chain_id) + + try: + yield g_network + finally: + g_network.stop_network() + g_network.clean() + + +def test_001_gala_network_configure(gala_network_fixture: GalaNetwork): + gala_network_fixture.configure_network() + time.sleep(1) + is_configured = all(node.config for node in gala_network_fixture.nodes) + assert is_configured + + +def test_002_gala_network_is_starting(gala_network_fixture: GalaNetwork): + gala_network_fixture.start_network() + time.sleep(1) + is_starting = all(node.is_running() for node in gala_network_fixture.nodes) + assert is_starting + + +def test_003_gala_network_is_running(gala_network_fixture: GalaNetwork): + time.sleep(5) + is_starting = all(node.status() for node in gala_network_fixture.nodes) + assert is_starting + + +def test_004_gala_network_is_growing(gala_network_fixture: GalaNetwork): + height_before = gala_network_fixture.nodes[0].block_height() + time.sleep(5) + height_after = gala_network_fixture.nodes[0].block_height() + assert height_after > height_before + + +def test_010_add_account(gala_network_fixture: GalaNetwork): + """### Testing""" diff --git a/tests/galacli/uv.lock b/tests/galacli/uv.lock index 2f8987d..bfb32b3 100644 --- a/tests/galacli/uv.lock +++ b/tests/galacli/uv.lock @@ -10,6 +10,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/12/90/3c9ff0512038035f59d279fddeb79f5f1eccd8859f06d6163c58798b9487/certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8", size = 167321 }, ] +[[package]] +name = "chardet" +version = "5.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/0d/f7b6ab21ec75897ed80c17d79b15951a719226b9fababf1e40ea74d69079/chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7", size = 2069618 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/38/6f/f5fbc992a329ee4e0f288c1fe0e2ad9485ed064cac731ed2fe47dcc38cbf/chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970", size = 199385 }, +] + [[package]] name = "charset-normalizer" version = "3.4.0" @@ -49,12 +58,36 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/bf/9b/08c0432272d77b04803958a4598a51e2a4b51c06640af8b8f0f908c18bf2/charset_normalizer-3.4.0-py3-none-any.whl", hash = "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079", size = 49446 }, ] +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, +] + +[[package]] +name = "dataproperty" +version = "1.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mbstrdecoder" }, + { name = "typepy", extra = ["datetime"] }, +] +sdist = { url = "https://files.pythonhosted.org/packages/48/e2/31ffb67d2a9ab4ff70b106e08ad01a3e7696f8d409457042d1eb18244f82/DataProperty-1.0.1.tar.gz", hash = "sha256:723e5729fa6e885e127a771a983ee1e0e34bb141aca4ffe1f0bfa7cde34650a4", size = 35340 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b1/3b/90ebd66ad57c588d6087e86e327436343e9cc60776a9445b79c6e80a022d/DataProperty-1.0.1-py3-none-any.whl", hash = "sha256:0b8b07d4fb6453fcf975b53d35dea41f3cfd69c9d79b5010c3cf224ff0407a7a", size = 27380 }, +] + [[package]] name = "galacli" version = "0.1.0" source = { virtual = "." } dependencies = [ { name = "mnemonic" }, + { name = "pytest" }, + { name = "pytest-md-report" }, { name = "python-dateutil" }, { name = "pyyaml" }, { name = "requests" }, @@ -66,6 +99,8 @@ dependencies = [ [package.metadata] requires-dist = [ { name = "mnemonic", specifier = ">=0.21" }, + { name = "pytest", specifier = ">=8.3.3" }, + { name = "pytest-md-report", specifier = ">=0.6.2" }, { name = "python-dateutil", specifier = ">=2.9.0.post0" }, { name = "pyyaml", specifier = ">=6.0.2" }, { name = "requests", specifier = ">=2.32.3" }, @@ -83,6 +118,27 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, ] +[[package]] +name = "iniconfig" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 }, +] + +[[package]] +name = "mbstrdecoder" +version = "1.1.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "chardet" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/70/8f/dd5d4efbe3f90d2d38c948f0ca5c698e2d6cedc58ead2f5b90272cbcb4fa/mbstrdecoder-1.1.3.tar.gz", hash = "sha256:dcfd2c759322eb44fe193a9e0b1b86c5b87f3ec5ea8e1bb43b3e9ae423f1e8fe", size = 10460 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/0f/726229136022b154895138bb10ba35e8435c4143f614cb5ad4d4e3fc21ec/mbstrdecoder-1.1.3-py3-none-any.whl", hash = "sha256:d66c1ed3f2dc4e7c5d87cd44a75be10bc5af4250f95b38bbaedd7851308ce938", size = 7761 }, +] + [[package]] name = "mnemonic" version = "0.21" @@ -92,6 +148,81 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/57/48/5abb16ce7f9d97b728e6b97c704ceaa614362e0847651f379ed0511942a0/mnemonic-0.21-py3-none-any.whl", hash = "sha256:72dc9de16ec5ef47287237b9b6943da11647a03fe7cf1f139fc3d7c4a7439288", size = 92701 }, ] +[[package]] +name = "packaging" +version = "24.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 }, +] + +[[package]] +name = "pathvalidate" +version = "3.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b4/8c/8713d8dcd8e357b9358695b441ee974580a8addfaea4f01437df07577052/pathvalidate-3.2.1.tar.gz", hash = "sha256:f5d07b1e2374187040612a1fcd2bcb2919f8db180df254c9581bb90bf903377d", size = 59263 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d3/5e/76a9d08b4b4e4583f269cb9f64de267f9aeae0dacef23307f53a14211716/pathvalidate-3.2.1-py3-none-any.whl", hash = "sha256:9a6255eb8f63c9e2135b9be97a5ce08f10230128c4ae7b3e935378b82b22c4c9", size = 23833 }, +] + +[[package]] +name = "pluggy" +version = "1.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 }, +] + +[[package]] +name = "pytablewriter" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "dataproperty" }, + { name = "mbstrdecoder" }, + { name = "pathvalidate" }, + { name = "setuptools" }, + { name = "tabledata" }, + { name = "tcolorpy" }, + { name = "typepy", extra = ["datetime"] }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cc/a9/76aa4430d32ae10b23e4347006dc4c67a3e2a00621e4bb38a60c1a77f15e/pytablewriter-1.2.0.tar.gz", hash = "sha256:0204a4bb684a22140d640f2599f09e137bcdc18b3dd49426f4a555016e246b46", size = 230439 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/06/74/b39b823ee7dba155b117634e62733a0dfdfe5aa100a553b435062cee2062/pytablewriter-1.2.0-py3-none-any.whl", hash = "sha256:4a30e2bb4bf5bc1069b1d2b2bc41947577c4517ab0875b23a5b194d296f543d8", size = 111097 }, +] + +[[package]] +name = "pytest" +version = "8.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8b/6c/62bbd536103af674e227c41a8f3dcd022d591f6eed5facb5a0f31ee33bbc/pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181", size = 1442487 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6b/77/7440a06a8ead44c7757a64362dd22df5760f9b12dc5f11b6188cd2fc27a0/pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2", size = 342341 }, +] + +[[package]] +name = "pytest-md-report" +version = "0.6.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytablewriter" }, + { name = "pytest" }, + { name = "tcolorpy" }, + { name = "typepy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/62/b3/b68a712044b2096c28edf73e262e17e56c82a30f55abc16b8e9d39c24650/pytest_md_report-0.6.2.tar.gz", hash = "sha256:5e96c655ebc9b5c3c7b78bf7c5382c1f68056e96904430252790f8737de5ce99", size = 283407 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ee/e9/6fd1b512826377e17193289147d8a697e0b56bbba4dbee709e62e7d8e94a/pytest_md_report-0.6.2-py3-none-any.whl", hash = "sha256:66e27efa5c155c87eb4700d60876e61a85c13361448c4031fda964c43e63c9b9", size = 13905 }, +] + [[package]] name = "python-dateutil" version = "2.9.0.post0" @@ -104,6 +235,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 }, ] +[[package]] +name = "pytz" +version = "2024.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3a/31/3c70bf7603cc2dca0f19bdc53b4537a797747a58875b552c8c413d963a3f/pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a", size = 319692 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/c3/005fcca25ce078d2cc29fd559379817424e94885510568bc1bc53d7d5846/pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725", size = 508002 }, +] + [[package]] name = "pyyaml" version = "6.0.2" @@ -170,6 +310,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/79/7b/884553415e9f0a9bf358ed52fb68b934e67ef6c5a62397ace924a1afdf9a/ruff-0.7.1-py3-none-win_arm64.whl", hash = "sha256:19aa200ec824c0f36d0c9114c8ec0087082021732979a359d6f3c390a6ff2a37", size = 8717402 }, ] +[[package]] +name = "setuptools" +version = "75.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c8/db/722a42ffdc226e950c4757b3da7b56ff5c090bb265dccd707f7b8a3c6fee/setuptools-75.5.0.tar.gz", hash = "sha256:5c4ccb41111392671f02bb5f8436dfc5a9a7185e80500531b133f5775c4163ef", size = 1336032 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fe/df/88ccbee85aefbca071db004fdc8f8d2507d55d5a9dc27ebb93c92edb1bd8/setuptools-75.5.0-py3-none-any.whl", hash = "sha256:87cb777c3b96d638ca02031192d40390e0ad97737e27b6b4fa831bea86f2f829", size = 1222710 }, +] + [[package]] name = "six" version = "1.16.0" @@ -179,6 +328,28 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d9/5a/e7c31adbe875f2abbb91bd84cf2dc52d792b5a01506781dbcf25c91daf11/six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254", size = 11053 }, ] +[[package]] +name = "tabledata" +version = "1.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "dataproperty" }, + { name = "typepy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/48/6a/7f78fbf883f325b70ba9ce5c10d97652f5c371e224940097c2cea9d0f456/tabledata-1.3.3.tar.gz", hash = "sha256:c90daaba9a408e4397934b3ff2f6c06797d5289676420bf520c741ad43e6ff91", size = 16296 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/06/e2/96b10ebc00d20b55967200e3d95c2137d91f58af1af672627683431c9d5c/tabledata-1.3.3-py3-none-any.whl", hash = "sha256:4abad1c996d8607e23b045b44dc0c5f061668f3c37585302c5f6c84c93a89962", size = 11732 }, +] + +[[package]] +name = "tcolorpy" +version = "0.1.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/af/f6/b17885354c5addfb93c14f3ed7172c33494f989dd5f83c0340d2c8fa3b69/tcolorpy-0.1.6.tar.gz", hash = "sha256:8cea0bf5f8cf03f77528a9acfbf312df935573892ba5ea3b2516e61fa54de9a5", size = 298986 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/0f/3571e551b524b3d3ddfa7fd3ec8065941faf4ae1278efefcef976d93587c/tcolorpy-0.1.6-py3-none-any.whl", hash = "sha256:8c15cb3167f30b0a433d72297e9d68667c825bd9e2af41c8dd7dfbd3d7f7e207", size = 8111 }, +] + [[package]] name = "toml" version = "0.10.2" @@ -197,6 +368,25 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f9/b6/a447b5e4ec71e13871be01ba81f5dfc9d0af7e473da256ff46bc0e24026f/tomlkit-0.13.2-py3-none-any.whl", hash = "sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde", size = 37955 }, ] +[[package]] +name = "typepy" +version = "1.3.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mbstrdecoder" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cc/86/9672794fb1c87a17b839666976ed4c8cb779ce05d471bed3166a39a53c4d/typepy-1.3.2.tar.gz", hash = "sha256:b69fd48b9f50cdb3809906eef36b855b3134ff66c8893a4f8580abddb0b39517", size = 24233 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/10/0d6dc654bb4e0eca017bbaf43a315b464c888576a68a2883cd4a74bd1b6b/typepy-1.3.2-py3-none-any.whl", hash = "sha256:d5d1022a424132622993800f1d2cd16cfdb691ac4e3b9c325f0fcb37799db1ae", size = 31428 }, +] + +[package.optional-dependencies] +datetime = [ + { name = "packaging" }, + { name = "python-dateutil" }, + { name = "pytz" }, +] + [[package]] name = "urllib3" version = "2.2.3" From bb91a44e48f15fbe271ec7714972da4a9e453660 Mon Sep 17 00:00:00 2001 From: Ilya Bygrimov Date: Sat, 16 Nov 2024 00:58:36 +0100 Subject: [PATCH 20/20] Edit wf file --- .github/workflows/ci.yaml | 21 ++++++++++++++++----- Makefile | 2 +- tests/galacli/Makefile | 25 +++++++++++++++++-------- tests/galacli/galacli.py | 22 +--------------------- tests/galacli/pyproject.toml | 2 +- 5 files changed, 36 insertions(+), 36 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 649de06..3d45f3c 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -47,7 +47,7 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v4 - + - name: Download binary artifact uses: actions/download-artifact@v4 with: @@ -59,11 +59,22 @@ jobs: version: "latest" - name: Run tests - run: make test + run: | + make test + if [ -f "tests/galacli/report.md" ]; then + echo "
Test Report" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + cat tests/galacli/report.md >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "
" >> $GITHUB_STEP_SUMMARY + fi - - name: Test status check - if: failure() - run: exit 1 + - name: Render the report to the PR when tests fail + uses: marocchino/sticky-pull-request-comment@v2 + with: + header: test-report + recreate: true + path: tests/galacli/report.md permissions: pull-requests: write diff --git a/Makefile b/Makefile index 4b86891..8166d27 100644 --- a/Makefile +++ b/Makefile @@ -127,7 +127,7 @@ build-alpine: @GOOS=linux CGO_ENABLED=1 go build -ldflags="-w -s" -o $(BUILDDIR)/$(GALACTICA_BINARY) ./cmd/galacticad localnet-build: - @make build + @$(MAKE) build @echo "Build docker image for localnet..." @./localnet/build-docker.sh diff --git a/tests/galacli/Makefile b/tests/galacli/Makefile index c7c0ee2..1129442 100644 --- a/tests/galacli/Makefile +++ b/tests/galacli/Makefile @@ -1,28 +1,37 @@ uname=$(shell uname -s) is_darwin :=$(filter Darwin,$(uname)) +all: ## run tests +all: $(if $(is_darwin),network,) clean start_test -all: $(if $(is_darwin),network,) clean start +help: ## Show this help + @printf "\033[33m%s:\033[0m\n" 'Available commands' + @awk 'BEGIN {FS = ":.*?## "} /^[[:alpha:][:punct:]]+:.*?## / {printf " \033[32m%-18s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST) clean: rm -rf node0* .pytest_cache __pycache__ -.venv/bin/python: +.venv/bin/pytest .venv/bin/python: uv venv uv sync +start: ## just run galacli.py for debugging purpose start: .venv/bin/python - pytest -v . -# .venv/bin/python galacli.py -ps: + .venv/bin/python galacli.py + +start_test: .venv/bin/pytest ## start tests + .venv/bin/pytest -v . + +ps: ## show running galacticad processes -@ps | grep galacticad | grep -v 'grep' -terminate: ps +terminate: ps ## terminate running galacticad processes ps | grep galacticad | grep -v 'grep' | awk '{print $$1}' | xargs -n1 kill -15 -kill: ps + +kill: ps ## kill running galacticad processes ps | grep galacticad | grep -v 'grep' | awk '{print $$1}' | xargs -n1 sudo kill -9 ifeq ($(uname),Darwin) -network: /tmp/127.0.0.2 /tmp/127.0.0.3 /tmp/127.0.0.4 +network: /tmp/127.0.0.2 /tmp/127.0.0.3 /tmp/127.0.0.4 ## assume you are using macos for development /tmp/127.0.0.%: sudo ifconfig lo0 alias $(shell basename $@) up && touch $@ endif diff --git a/tests/galacli/galacli.py b/tests/galacli/galacli.py index 887c005..4799123 100644 --- a/tests/galacli/galacli.py +++ b/tests/galacli/galacli.py @@ -10,15 +10,11 @@ import time import socket import re - - -# import os import sys from pathlib import Path import tempfile import requests import toml -from pprint import pprint import urllib.parse DEBUG = True @@ -561,7 +557,7 @@ def unsaferesetall(self): def query_base_fee(self, **kwargs): default_kwargs = {"home": self.data_dir} - + ## evmos tests comment # TODO: is this assumption correct? Having the base fee turned off has caused some test failures # because it was returning `null` and not an `int(...)` -> we'll return 0 here. params = json.loads( @@ -936,22 +932,6 @@ def combine_seeds(self) -> Dict: def configure_network(self): """ ### func for configuring gala network - - - [X] Init first node to get blank genesis.json - - [X] Edit config files via first node to set common config - - [X] Configure genesis to needed state - - [X] edit genesis.json - - [X] add some gentx - - [X] collect gentx - - [X] validate genesis - - [ ] Get tendermint node-id of each node - - [X] Put node folder - - [X] configure node - - [X] put key into node - - [X] put genesis.json to node config - - [ ] get tendermint node-id - - [ ] Edit individual configs to set some parameters throgh network - - [ ] Persistent peers """ self.command_node.init_node( diff --git a/tests/galacli/pyproject.toml b/tests/galacli/pyproject.toml index 5d62a8c..a37c658 100644 --- a/tests/galacli/pyproject.toml +++ b/tests/galacli/pyproject.toml @@ -19,7 +19,7 @@ dependencies = [ [tool.pytest.ini_options] md_report_output = "report.md" md_report = true -md_report_verbose = 0 +md_report_verbose = 1 md_report_color = "never" # asyncio_mode = "auto"