Co-authored-by: Alexander Peters <alpe@users.noreply.github.com> Co-authored-by: Julien Robert <julien@rbrt.fr>
This commit is contained in:
parent
0d526b4339
commit
4c6e34be28
11
.github/workflows/build.yml
vendored
11
.github/workflows/build.yml
vendored
@ -18,7 +18,6 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
# go-arch: ["amd64", "arm", "arm64"]
|
||||
go-arch: ["amd64", "arm64"] # drop 32 bit support for now (and maybe forever)
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@ -32,14 +31,16 @@ jobs:
|
||||
###################
|
||||
#### Build App ####
|
||||
###################
|
||||
- name: Build with sqlite backend
|
||||
run: GOARCH=${{ matrix.go-arch }} COSMOS_BUILD_OPTIONS=v2,sqlite make build
|
||||
- name: Build
|
||||
run: GOARCH=${{ matrix.go-arch }} make build
|
||||
- name: Build with BLS12381
|
||||
if: matrix.go-arch == 'amd64'
|
||||
run: GOARCH=${{ matrix.go-arch }} COSMOS_BUILD_OPTIONS="bls12381" make build
|
||||
run: GOARCH=${{ matrix.go-arch }} COSMOS_BUILD_OPTIONS=bls12381 make build
|
||||
- name: Build with Secp_cgo
|
||||
if: matrix.go-arch == 'amd64'
|
||||
run: GOARCH=${{ matrix.go-arch }} COSMOS_BUILD_OPTIONS="secp" make build
|
||||
run: GOARCH=${{ matrix.go-arch }} COSMOS_BUILD_OPTIONS=secp make build
|
||||
- name: Build v2 with sqlite backend
|
||||
run: GOARCH=${{ matrix.go-arch }} COSMOS_BUILD_OPTIONS=v2,sqlite make build
|
||||
###################
|
||||
## Build Tooling ##
|
||||
###################
|
||||
|
||||
@ -40,6 +40,10 @@ Ref: https://keepachangelog.com/en/1.0.0/
|
||||
|
||||
Every module contains its own CHANGELOG.md. Please refer to the module you are interested in.
|
||||
|
||||
### Features
|
||||
|
||||
* (sims) [#23013](https://github.com/cosmos/cosmos-sdk/pull/23013) Integration with app v2
|
||||
|
||||
### Improvements
|
||||
|
||||
* (codec) [#22988](https://github.com/cosmos/cosmos-sdk/pull/22988) Improve edge case handling for recursion limits.
|
||||
|
||||
@ -83,6 +83,7 @@ const (
|
||||
FlagTip = "tip"
|
||||
FlagAux = "aux"
|
||||
FlagInitHeight = "initial-height"
|
||||
FlagInvCheckPeriod = "inv-check-period"
|
||||
// FlagOutput is the flag to set the output format.
|
||||
// This differs from FlagOutputDocument that is used to set the output file.
|
||||
FlagOutput = "output"
|
||||
|
||||
@ -72,6 +72,14 @@ ifeq (bls12381,$(findstring bls12381,$(COSMOS_BUILD_OPTIONS)))
|
||||
build_tags += bls12381
|
||||
endif
|
||||
|
||||
# handle sqlite
|
||||
ifeq (sqlite,$(findstring sqlite,$(COSMOS_BUILD_OPTIONS)))
|
||||
CGO_ENABLED=1
|
||||
ifeq (arm64,$(shell go env GOARCH))
|
||||
CC=aarch64-linux-gnu-gcc
|
||||
endif
|
||||
endif
|
||||
|
||||
# benchmark module
|
||||
ifeq (benchmark,$(findstring benchmark,$(COSMOS_BUILD_OPTIONS)))
|
||||
build_tags += benchmark
|
||||
@ -110,7 +118,7 @@ ifeq (debug,$(findstring debug,$(COSMOS_BUILD_OPTIONS)))
|
||||
BUILD_FLAGS += -gcflags "all=-N -l"
|
||||
endif
|
||||
|
||||
#? all: Run tools build
|
||||
#? all: Run tools build
|
||||
all: build
|
||||
|
||||
|
||||
@ -128,7 +136,10 @@ build-linux-arm64:
|
||||
GOOS=linux GOARCH=arm64 LEDGER_ENABLED=false $(MAKE) build
|
||||
|
||||
$(BUILD_TARGETS): go.sum $(BUILDDIR)/
|
||||
cd ${CURRENT_DIR}/${SIMAPP} && go $@ -mod=readonly $(BUILD_FLAGS) $(BUILD_ARGS) ./...
|
||||
cd ${CURRENT_DIR}/${SIMAPP} && \
|
||||
$(if $(CGO_ENABLED),CGO_ENABLED=$(CGO_ENABLED)) \
|
||||
$(if $(CC),CC=$(CC)) \
|
||||
go $@ -mod=readonly $(BUILD_FLAGS) $(BUILD_ARGS) ./...
|
||||
|
||||
$(BUILDDIR)/:
|
||||
mkdir -p $(BUILDDIR)/
|
||||
|
||||
@ -52,6 +52,11 @@ test-sim-multi-seed-short:
|
||||
@cd ${CURRENT_DIR}/simapp && go test -failfast -mod=readonly -timeout 30m -tags='sims' -run TestFullAppSimulation \
|
||||
-NumBlocks=50 -Period=10 -FauxMerkle=true
|
||||
|
||||
test-v2-sim:
|
||||
@echo "Running short multi-seed application simulation. This may take awhile!"
|
||||
@cd ${CURRENT_DIR}/simapp/v2 && go test -failfast -mod=readonly -timeout 30m -tags='sims' -run TestSimsAppV2
|
||||
.PHONY: test-v2-sim
|
||||
|
||||
|
||||
test-sim-benchmark-invariants:
|
||||
@echo "Running simulation invariant benchmarks..."
|
||||
|
||||
@ -12,9 +12,9 @@ require (
|
||||
cosmossdk.io/errors/v2 v2.0.0
|
||||
cosmossdk.io/log v1.5.0
|
||||
cosmossdk.io/schema v1.0.0 //main
|
||||
cosmossdk.io/server/v2 v2.0.0-beta.1 // main
|
||||
cosmossdk.io/server/v2/appmanager v1.0.0-beta.1 // main
|
||||
cosmossdk.io/server/v2/stf v1.0.0-beta.1 // main
|
||||
cosmossdk.io/server/v2 v2.0.0-beta.1.0.20250109081935-cf721a654090 // main
|
||||
cosmossdk.io/server/v2/appmanager v1.0.0-beta.1.0.20250109081935-cf721a654090 // main
|
||||
cosmossdk.io/server/v2/stf v1.0.0-beta.1.0.20250109081935-cf721a654090 // main
|
||||
cosmossdk.io/store/v2 v2.0.0-beta.1 // main
|
||||
cosmossdk.io/x/consensus v0.2.0-rc.1
|
||||
github.com/cometbft/cometbft v1.0.0
|
||||
@ -25,7 +25,7 @@ require (
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/stretchr/testify v1.10.0
|
||||
google.golang.org/grpc v1.69.2
|
||||
google.golang.org/protobuf v1.36.1
|
||||
google.golang.org/protobuf v1.36.2
|
||||
sigs.k8s.io/yaml v1.4.0
|
||||
)
|
||||
|
||||
|
||||
@ -24,12 +24,12 @@ cosmossdk.io/math v1.5.0 h1:sbOASxee9Zxdjd6OkzogvBZ25/hP929vdcYcBJQbkLc=
|
||||
cosmossdk.io/math v1.5.0/go.mod h1:AAwwBmUhqtk2nlku174JwSll+/DepUXW3rWIXN5q+Nw=
|
||||
cosmossdk.io/schema v1.0.0 h1:/diH4XJjpV1JQwuIozwr+A4uFuuwanFdnw2kKeiXwwQ=
|
||||
cosmossdk.io/schema v1.0.0/go.mod h1:RDAhxIeNB4bYqAlF4NBJwRrgtnciMcyyg0DOKnhNZQQ=
|
||||
cosmossdk.io/server/v2 v2.0.0-beta.1 h1:mMfWf7wVEv6oVZcgxEZ52x8h/bOuGRjdSrUAEqNQmf4=
|
||||
cosmossdk.io/server/v2 v2.0.0-beta.1/go.mod h1:lfkdTKM2tMlt6KhNi5f9zT+Ww5HcRpnINWjeKXCHjmc=
|
||||
cosmossdk.io/server/v2/appmanager v1.0.0-beta.1 h1:EISWki+z9SDAt3OJnUl2y5Ow4qjOb+epYjLb1C7CN/E=
|
||||
cosmossdk.io/server/v2/appmanager v1.0.0-beta.1/go.mod h1:RVYxIaEdIT10nWSRqbwKDLFWfvCVx+cwAorCyPAQg9A=
|
||||
cosmossdk.io/server/v2/stf v1.0.0-beta.1 h1:s+nRgjhKVC08/qpr51eFVodLhyyQ9ASvJBanLBfQVNI=
|
||||
cosmossdk.io/server/v2/stf v1.0.0-beta.1/go.mod h1:nfjihbofEF2GGadkYSFmgy5tqrAnSrmGcXUDZmmWyi8=
|
||||
cosmossdk.io/server/v2 v2.0.0-beta.1.0.20250109081935-cf721a654090 h1:Votc1YWghrdTWsKipvNt6lLCGr9EXPkptlrwG83PyGE=
|
||||
cosmossdk.io/server/v2 v2.0.0-beta.1.0.20250109081935-cf721a654090/go.mod h1:EOOB4eUcxbfkhOLWyqA0kWHltrh6OnNp0VYhY1+8xDc=
|
||||
cosmossdk.io/server/v2/appmanager v1.0.0-beta.1.0.20250109081935-cf721a654090 h1:BlNFJZGtVsyaxydK3dIpklhQubbj5Sr2Eqh1Wow0lL4=
|
||||
cosmossdk.io/server/v2/appmanager v1.0.0-beta.1.0.20250109081935-cf721a654090/go.mod h1:l6oCGNcucF6/U949UwRj+RemNzq5475ovNHohcvN1YM=
|
||||
cosmossdk.io/server/v2/stf v1.0.0-beta.1.0.20250109081935-cf721a654090 h1:K0HN3TD7jz0zJIbvv1X5gkvEqhcBLqz71T4Al2ujols=
|
||||
cosmossdk.io/server/v2/stf v1.0.0-beta.1.0.20250109081935-cf721a654090/go.mod h1:O6Njxje0LbvC0RxdgwTmvBlbGcpKOfhbkdAguyq0ntQ=
|
||||
cosmossdk.io/store v1.10.0-rc.1.0.20241218084712-ca559989da43 h1:glZ6MpmD+5AhwJYV4jzx+rn7cgUB2owHgk9o+93luz0=
|
||||
cosmossdk.io/store v1.10.0-rc.1.0.20241218084712-ca559989da43/go.mod h1:XCWpgfueHSBY+B7Cf2Aq/CcsU+6XoFH+EmseCKglFrU=
|
||||
cosmossdk.io/store/v2 v2.0.0-beta.1 h1:p1fdZ9uNijhpXZXdqs0QS6NmXNDVPNyT4DHV4yQnF64=
|
||||
@ -710,8 +710,8 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk=
|
||||
google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
google.golang.org/protobuf v1.36.2 h1:R8FeyR1/eLmkutZOM5CWghmo5itiG9z0ktFlTVLuTmU=
|
||||
google.golang.org/protobuf v1.36.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@ -11,10 +11,11 @@ require (
|
||||
cosmossdk.io/indexer/postgres v0.1.0
|
||||
cosmossdk.io/log v1.5.0
|
||||
cosmossdk.io/math v1.5.0
|
||||
cosmossdk.io/runtime/v2 v2.0.0-20241219154748-69025c556666 // main
|
||||
cosmossdk.io/server/v2 v2.0.0-beta.1.0.20250106081242-884a7a51c3c3 // main
|
||||
cosmossdk.io/runtime/v2 v2.0.0-20250109081935-cf721a654090 // main
|
||||
cosmossdk.io/server/v2 v2.0.0-beta.1.0.20250109081935-cf721a654090 // main
|
||||
cosmossdk.io/server/v2/appmanager v1.0.0-beta.1.0.20250109081935-cf721a654090
|
||||
cosmossdk.io/server/v2/cometbft v1.0.0-beta.1
|
||||
cosmossdk.io/store/v2 v2.0.0-beta.1 // main
|
||||
cosmossdk.io/store/v2 v2.0.0-beta.1.0.20250109081935-cf721a654090 // main
|
||||
cosmossdk.io/tools/benchmark v0.2.0-rc.1
|
||||
cosmossdk.io/tools/confix v0.2.0-rc.1
|
||||
cosmossdk.io/x/accounts v0.2.0-rc.1
|
||||
@ -38,6 +39,7 @@ require (
|
||||
cosmossdk.io/x/staking v0.2.0-rc.1
|
||||
cosmossdk.io/x/upgrade v0.2.0-rc.1
|
||||
github.com/cometbft/cometbft v1.0.0
|
||||
github.com/cometbft/cometbft/api v1.0.0
|
||||
// this version is not used as it is always replaced by the latest Cosmos SDK version
|
||||
github.com/cosmos/cosmos-sdk v0.52.0
|
||||
github.com/jackc/pgx/v5 v5.7.1
|
||||
@ -45,7 +47,7 @@ require (
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/spf13/viper v1.19.0
|
||||
github.com/stretchr/testify v1.10.0
|
||||
google.golang.org/protobuf v1.36.1
|
||||
google.golang.org/protobuf v1.36.2
|
||||
)
|
||||
|
||||
require (
|
||||
@ -61,8 +63,7 @@ require (
|
||||
cosmossdk.io/errors v1.0.1 // indirect
|
||||
cosmossdk.io/errors/v2 v2.0.0 // indirect
|
||||
cosmossdk.io/schema v1.0.0 // indirect
|
||||
cosmossdk.io/server/v2/appmanager v1.0.0-beta.1 // indirect; main
|
||||
cosmossdk.io/server/v2/stf v1.0.0-beta.1 // indirect; main
|
||||
cosmossdk.io/server/v2/stf v1.0.0-beta.1.0.20250109081935-cf721a654090 // indirect; main
|
||||
cosmossdk.io/store v1.10.0-rc.1.0.20241218084712-ca559989da43 // indirect; main
|
||||
cosmossdk.io/x/tx v1.0.0 // indirect; main
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
@ -91,7 +92,6 @@ require (
|
||||
github.com/cockroachdb/redact v1.1.5 // indirect
|
||||
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect
|
||||
github.com/cometbft/cometbft-db v1.0.1 // indirect
|
||||
github.com/cometbft/cometbft/api v1.0.0 // indirect
|
||||
github.com/cosmos/btcutil v1.0.5 // indirect
|
||||
github.com/cosmos/cosmos-db v1.1.1 // indirect
|
||||
github.com/cosmos/cosmos-proto v1.0.0-beta.5 // indirect
|
||||
@ -246,11 +246,19 @@ require (
|
||||
sigs.k8s.io/yaml v1.4.0 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/aybabtme/uniplot v0.0.0-20151203143629-039c559e5e7e // indirect
|
||||
github.com/bvinc/go-sqlite-lite v0.6.1 // indirect
|
||||
github.com/cosmos/iavl/v2 v2.0.0-alpha.4 // indirect
|
||||
github.com/kocubinski/costor-api v1.1.1 // indirect
|
||||
)
|
||||
|
||||
// Here are the short-lived replace from the SimApp
|
||||
// Replace here are pending PRs, or version to be tagged
|
||||
// replace (
|
||||
// <temporary replace>
|
||||
// )
|
||||
replace cosmossdk.io/server/v2/cometbft => ../../server/v2/cometbft
|
||||
|
||||
// Below are the long-lived replace of the SimApp
|
||||
replace (
|
||||
|
||||
@ -214,22 +214,20 @@ cosmossdk.io/log v1.5.0 h1:dVdzPJW9kMrnAYyMf1duqacoidB9uZIl+7c6z0mnq0g=
|
||||
cosmossdk.io/log v1.5.0/go.mod h1:Tr46PUJjiUthlwQ+hxYtUtPn4D/oCZXAkYevBeh5+FI=
|
||||
cosmossdk.io/math v1.5.0 h1:sbOASxee9Zxdjd6OkzogvBZ25/hP929vdcYcBJQbkLc=
|
||||
cosmossdk.io/math v1.5.0/go.mod h1:AAwwBmUhqtk2nlku174JwSll+/DepUXW3rWIXN5q+Nw=
|
||||
cosmossdk.io/runtime/v2 v2.0.0-20241219154748-69025c556666 h1:zFMi1URvs3NiJdSf2zPFe2mQINfBmC07F99LHQ4asyM=
|
||||
cosmossdk.io/runtime/v2 v2.0.0-20241219154748-69025c556666/go.mod h1:C0MTcmQlZFPQvYoXzYiWP+h9lFOgezzafEFiQBa6xYo=
|
||||
cosmossdk.io/runtime/v2 v2.0.0-20250109081935-cf721a654090 h1:zAgv9XPOMdSkwqd2Vh1AKV8MlB9iWAWCide5ShK+f2o=
|
||||
cosmossdk.io/runtime/v2 v2.0.0-20250109081935-cf721a654090/go.mod h1:YRfC3bBBnZYvz0W8VubR/uImEYqi/qBH6cVAMVx+KOA=
|
||||
cosmossdk.io/schema v1.0.0 h1:/diH4XJjpV1JQwuIozwr+A4uFuuwanFdnw2kKeiXwwQ=
|
||||
cosmossdk.io/schema v1.0.0/go.mod h1:RDAhxIeNB4bYqAlF4NBJwRrgtnciMcyyg0DOKnhNZQQ=
|
||||
cosmossdk.io/server/v2 v2.0.0-beta.1.0.20250106081242-884a7a51c3c3 h1:r7zBzz+Xs0rAGx/ia/5tNgM2Y4xa+SkU+Rli1mIsTRI=
|
||||
cosmossdk.io/server/v2 v2.0.0-beta.1.0.20250106081242-884a7a51c3c3/go.mod h1:vqgLhoQRRFyRUrLrDj7S18ojvgP8f0X+S1t6CME6GcQ=
|
||||
cosmossdk.io/server/v2/appmanager v1.0.0-beta.1 h1:EISWki+z9SDAt3OJnUl2y5Ow4qjOb+epYjLb1C7CN/E=
|
||||
cosmossdk.io/server/v2/appmanager v1.0.0-beta.1/go.mod h1:RVYxIaEdIT10nWSRqbwKDLFWfvCVx+cwAorCyPAQg9A=
|
||||
cosmossdk.io/server/v2/cometbft v1.0.0-beta.1 h1:kzTMGqIwcpcynbnU0Wyoe0HObyUUBZbkD/sHBUrbZl8=
|
||||
cosmossdk.io/server/v2/cometbft v1.0.0-beta.1/go.mod h1:EUvCCLYfSMEMp48KqYoHJIGMjg4Md+NcZRq4HIhjq9E=
|
||||
cosmossdk.io/server/v2/stf v1.0.0-beta.1 h1:s+nRgjhKVC08/qpr51eFVodLhyyQ9ASvJBanLBfQVNI=
|
||||
cosmossdk.io/server/v2/stf v1.0.0-beta.1/go.mod h1:nfjihbofEF2GGadkYSFmgy5tqrAnSrmGcXUDZmmWyi8=
|
||||
cosmossdk.io/server/v2 v2.0.0-beta.1.0.20250109081935-cf721a654090 h1:Votc1YWghrdTWsKipvNt6lLCGr9EXPkptlrwG83PyGE=
|
||||
cosmossdk.io/server/v2 v2.0.0-beta.1.0.20250109081935-cf721a654090/go.mod h1:EOOB4eUcxbfkhOLWyqA0kWHltrh6OnNp0VYhY1+8xDc=
|
||||
cosmossdk.io/server/v2/appmanager v1.0.0-beta.1.0.20250109081935-cf721a654090 h1:BlNFJZGtVsyaxydK3dIpklhQubbj5Sr2Eqh1Wow0lL4=
|
||||
cosmossdk.io/server/v2/appmanager v1.0.0-beta.1.0.20250109081935-cf721a654090/go.mod h1:l6oCGNcucF6/U949UwRj+RemNzq5475ovNHohcvN1YM=
|
||||
cosmossdk.io/server/v2/stf v1.0.0-beta.1.0.20250109081935-cf721a654090 h1:K0HN3TD7jz0zJIbvv1X5gkvEqhcBLqz71T4Al2ujols=
|
||||
cosmossdk.io/server/v2/stf v1.0.0-beta.1.0.20250109081935-cf721a654090/go.mod h1:O6Njxje0LbvC0RxdgwTmvBlbGcpKOfhbkdAguyq0ntQ=
|
||||
cosmossdk.io/store v1.10.0-rc.1.0.20241218084712-ca559989da43 h1:glZ6MpmD+5AhwJYV4jzx+rn7cgUB2owHgk9o+93luz0=
|
||||
cosmossdk.io/store v1.10.0-rc.1.0.20241218084712-ca559989da43/go.mod h1:XCWpgfueHSBY+B7Cf2Aq/CcsU+6XoFH+EmseCKglFrU=
|
||||
cosmossdk.io/store/v2 v2.0.0-beta.1 h1:p1fdZ9uNijhpXZXdqs0QS6NmXNDVPNyT4DHV4yQnF64=
|
||||
cosmossdk.io/store/v2 v2.0.0-beta.1/go.mod h1:qHQmf/9mnsXwo/Ypp2u2Zs6BmkYcx1R/Jrpyn9Ro13A=
|
||||
cosmossdk.io/store/v2 v2.0.0-beta.1.0.20250109081935-cf721a654090 h1:ZXFcStI+zFuHEBvuIPaLzPUp54PCxCAmR2RUA8/1KQM=
|
||||
cosmossdk.io/store/v2 v2.0.0-beta.1.0.20250109081935-cf721a654090/go.mod h1:G+ZRihxC4jFhLXnYt4K16tSYtkvh7ayjSaYGWJFObAo=
|
||||
cosmossdk.io/tools/benchmark v0.2.0-rc.1 h1:Jgk0FLvnMQJrivrSMhKQBwaTiJz6MGq5ZbaHag7Sqq0=
|
||||
cosmossdk.io/tools/benchmark v0.2.0-rc.1/go.mod h1:tnGa8L7xHFMp26FbLvU5MRlS89BFrCxSOKR8jCi7kVU=
|
||||
cosmossdk.io/tools/confix v0.2.0-rc.1 h1:sVYXR89OKW19oCnr232m9/pE3+oJllNTZlTypWhXHNI=
|
||||
@ -305,6 +303,8 @@ github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kd
|
||||
github.com/aws/aws-sdk-go v1.44.122/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
|
||||
github.com/aws/aws-sdk-go v1.54.6 h1:HEYUib3yTt8E6vxjMWM3yAq5b+qjj/6aKA62mkgux9g=
|
||||
github.com/aws/aws-sdk-go v1.54.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
|
||||
github.com/aybabtme/uniplot v0.0.0-20151203143629-039c559e5e7e h1:dSeuFcs4WAJJnswS8vXy7YY1+fdlbVPuEVmDAfqvFOQ=
|
||||
github.com/aybabtme/uniplot v0.0.0-20151203143629-039c559e5e7e/go.mod h1:uh71c5Vc3VNIplXOFXsnDy21T1BepgT32c5X/YPrOyc=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
@ -324,6 +324,8 @@ github.com/btcsuite/btcd/btcutil v1.1.6 h1:zFL2+c3Lb9gEgqKNzowKUPQNb8jV7v5Oaodi/
|
||||
github.com/btcsuite/btcd/btcutil v1.1.6/go.mod h1:9dFymx8HpuLqBnsPELrImQeTQfKBQqzqGbbV3jK55aE=
|
||||
github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA=
|
||||
github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8=
|
||||
github.com/bvinc/go-sqlite-lite v0.6.1 h1:JU8Rz5YAOZQiU3WEulKF084wfXpytRiqD2IaW2QjPz4=
|
||||
github.com/bvinc/go-sqlite-lite v0.6.1/go.mod h1:2GiE60NUdb0aNhDdY+LXgrqAVDpi2Ijc6dB6ZMp9x6s=
|
||||
github.com/bytedance/sonic v1.12.6 h1:/isNmCUF2x3Sh8RAp/4mh4ZGkcFAX/hLrzrK3AvpRzk=
|
||||
github.com/bytedance/sonic v1.12.6/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
|
||||
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||
@ -402,6 +404,10 @@ github.com/cosmos/gogoproto v1.7.0 h1:79USr0oyXAbxg3rspGh/m4SWNyoz/GLaAh0QlCe2fr
|
||||
github.com/cosmos/gogoproto v1.7.0/go.mod h1:yWChEv5IUEYURQasfyBW5ffkMHR/90hiHgbNgrtp4j0=
|
||||
github.com/cosmos/iavl v1.3.5 h1:wTDFbaa/L0FVUrwTlzMnjN3fphtKgWxgcZmTc45MZuA=
|
||||
github.com/cosmos/iavl v1.3.5/go.mod h1:T6SfBcyhulVIY2G/ZtAtQm/QiJvsuhIos52V4dWYk88=
|
||||
github.com/cosmos/iavl-bench/bench v0.0.4 h1:J6zQPiBqF4CXMM3QBsLqZgQEBGY0taX85vLIZMhmAfQ=
|
||||
github.com/cosmos/iavl-bench/bench v0.0.4/go.mod h1:j2rLae77EffacWcp7mmj3Uaa4AOAmZA7ymvhsuBQKKI=
|
||||
github.com/cosmos/iavl/v2 v2.0.0-alpha.4 h1:PfpQt7xl4hojw2UFS2JdJppJnx8sjlmcxRQ7Hxk7Cl0=
|
||||
github.com/cosmos/iavl/v2 v2.0.0-alpha.4/go.mod h1:7RSm0aeApe3S1x4TrLffvUL6pjOtMYV4glYnpAhr2lw=
|
||||
github.com/cosmos/ics23/go v0.11.0 h1:jk5skjT0TqX5e5QJbEnwXIS2yI2vnmLOgpQPeM5RtnU=
|
||||
github.com/cosmos/ics23/go v0.11.0/go.mod h1:A8OjxPE67hHST4Icw94hOxxFEJMBG031xIGF/JHNIY0=
|
||||
github.com/cosmos/keyring v1.2.0 h1:8C1lBP9xhImmIabyXW4c3vFjjLiBdGCmfLUfeZlV1Yo=
|
||||
@ -702,6 +708,8 @@ github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa02
|
||||
github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
|
||||
github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
|
||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||
github.com/kocubinski/costor-api v1.1.1 h1:sgfJA7T/8IfZ59zxiMrED0xdjerAFuPNBTqyO90GiEE=
|
||||
github.com/kocubinski/costor-api v1.1.1/go.mod h1:ESMBMDkKfN+9vvvhhNVdKLhbOmzI3O/i16iXvRM9Tuc=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
@ -1484,8 +1492,8 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk=
|
||||
google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
google.golang.org/protobuf v1.36.2 h1:R8FeyR1/eLmkutZOM5CWghmo5itiG9z0ktFlTVLuTmU=
|
||||
google.golang.org/protobuf v1.36.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
492
simapp/v2/sim_runner.go
Normal file
492
simapp/v2/sim_runner.go
Normal file
@ -0,0 +1,492 @@
|
||||
package simapp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"iter"
|
||||
"maps"
|
||||
"math/rand"
|
||||
"slices"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
cmtproto "github.com/cometbft/cometbft/api/cometbft/types/v1"
|
||||
cmttypes "github.com/cometbft/cometbft/types"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"cosmossdk.io/core/appmodule"
|
||||
appmodulev2 "cosmossdk.io/core/appmodule/v2"
|
||||
"cosmossdk.io/core/comet"
|
||||
corecontext "cosmossdk.io/core/context"
|
||||
"cosmossdk.io/core/server"
|
||||
"cosmossdk.io/core/store"
|
||||
"cosmossdk.io/core/transaction"
|
||||
"cosmossdk.io/depinject"
|
||||
"cosmossdk.io/log"
|
||||
"cosmossdk.io/runtime/v2"
|
||||
"cosmossdk.io/server/v2/appmanager"
|
||||
cometbfttypes "cosmossdk.io/server/v2/cometbft/types"
|
||||
storev2 "cosmossdk.io/store/v2"
|
||||
consensustypes "cosmossdk.io/x/consensus/types"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
"github.com/cosmos/cosmos-sdk/simsx"
|
||||
simsxv2 "github.com/cosmos/cosmos-sdk/simsx/v2"
|
||||
simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
|
||||
"github.com/cosmos/cosmos-sdk/types/module"
|
||||
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
|
||||
"github.com/cosmos/cosmos-sdk/x/simulation"
|
||||
"github.com/cosmos/cosmos-sdk/x/simulation/client/cli"
|
||||
)
|
||||
|
||||
type Tx = transaction.Tx
|
||||
type (
|
||||
HasWeightedOperationsX = simsx.HasWeightedOperationsX
|
||||
HasWeightedOperationsXWithProposals = simsx.HasWeightedOperationsXWithProposals
|
||||
HasProposalMsgsX = simsx.HasProposalMsgsX
|
||||
)
|
||||
|
||||
const SimAppChainID = "simulation-app"
|
||||
|
||||
// this list of seeds was imported from the original simulation runner: https://github.com/cosmos/tools/blob/v1.0.0/cmd/runsim/main.go#L32
|
||||
var defaultSeeds = []int64{
|
||||
1, 2, 4, 7,
|
||||
32, 123, 124, 582, 1893, 2989,
|
||||
3012, 4728, 37827, 981928, 87821, 891823782,
|
||||
989182, 89182391, 11, 22, 44, 77, 99, 2020,
|
||||
3232, 123123, 124124, 582582, 18931893,
|
||||
29892989, 30123012, 47284728, 7601778, 8090485,
|
||||
977367484, 491163361, 424254581, 673398983,
|
||||
}
|
||||
|
||||
const (
|
||||
maxTimePerBlock = 10_000 * time.Second
|
||||
minTimePerBlock = maxTimePerBlock / 2
|
||||
timeRangePerBlock = maxTimePerBlock - minTimePerBlock
|
||||
)
|
||||
|
||||
type (
|
||||
AuthKeeper interface {
|
||||
simsx.ModuleAccountSource
|
||||
simsx.AccountSource
|
||||
}
|
||||
|
||||
BankKeeper interface {
|
||||
simsx.BalanceSource
|
||||
GetBlockedAddresses() map[string]bool
|
||||
}
|
||||
|
||||
StakingKeeper interface {
|
||||
UnbondingTime(ctx context.Context) (time.Duration, error)
|
||||
}
|
||||
|
||||
ModuleManager interface {
|
||||
Modules() map[string]appmodulev2.AppModule
|
||||
}
|
||||
|
||||
// SimulationApp abstract blockchain app
|
||||
SimulationApp[T Tx] interface {
|
||||
GetApp() *runtime.App[T]
|
||||
TxConfig() client.TxConfig
|
||||
AppCodec() codec.Codec
|
||||
DefaultGenesis() map[string]json.RawMessage
|
||||
Store() storev2.RootStore
|
||||
Close() error
|
||||
}
|
||||
|
||||
// TestInstance system under test
|
||||
TestInstance[T Tx] struct {
|
||||
App SimulationApp[T]
|
||||
TxDecoder transaction.Codec[T]
|
||||
BankKeeper BankKeeper
|
||||
AuthKeeper AuthKeeper
|
||||
StakingKeeper StakingKeeper
|
||||
TXBuilder simsxv2.TXBuilder[T]
|
||||
AppManager appmanager.AppManager[T]
|
||||
ModuleManager ModuleManager
|
||||
}
|
||||
|
||||
AppFactory[T Tx, V SimulationApp[T]] func(config depinject.Config, outputs ...any) (V, error)
|
||||
)
|
||||
|
||||
// SetupTestInstance initializes and returns the system under test.
|
||||
func SetupTestInstance[T Tx, V SimulationApp[T]](t *testing.T, factory AppFactory[T, V], appConfig depinject.Config) TestInstance[T] {
|
||||
t.Helper()
|
||||
vp := viper.New()
|
||||
vp.Set("store.app-db-backend", "memdb")
|
||||
vp.Set("home", t.TempDir())
|
||||
|
||||
depInjCfg := depinject.Configs(
|
||||
depinject.Supply(log.NewNopLogger(), runtime.GlobalConfig(vp.AllSettings())),
|
||||
appConfig,
|
||||
)
|
||||
var (
|
||||
bankKeeper BankKeeper
|
||||
authKeeper AuthKeeper
|
||||
stKeeper StakingKeeper
|
||||
)
|
||||
|
||||
err := depinject.Inject(depInjCfg,
|
||||
&authKeeper,
|
||||
&bankKeeper,
|
||||
&stKeeper,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
xapp, err := factory(depinject.Configs(depinject.Supply(log.NewNopLogger(), runtime.GlobalConfig(vp.AllSettings()))))
|
||||
require.NoError(t, err)
|
||||
return TestInstance[T]{
|
||||
App: xapp,
|
||||
BankKeeper: bankKeeper,
|
||||
AuthKeeper: authKeeper,
|
||||
StakingKeeper: stKeeper,
|
||||
AppManager: xapp.GetApp(),
|
||||
ModuleManager: xapp.GetApp().ModuleManager(),
|
||||
TxDecoder: simsxv2.NewGenericTxDecoder[T](xapp.TxConfig()),
|
||||
TXBuilder: simsxv2.NewSDKTXBuilder[T](xapp.TxConfig(), simsxv2.DefaultGenTxGas),
|
||||
}
|
||||
}
|
||||
|
||||
// RunWithSeeds runs a series of subtests using the default set of random seeds for deterministic simulation testing.
|
||||
func RunWithSeeds[T Tx](
|
||||
t *testing.T,
|
||||
seeds []int64,
|
||||
postRunActions ...func(t testing.TB, app TestInstance[T], accs []simtypes.Account),
|
||||
) {
|
||||
t.Helper()
|
||||
cfg := cli.NewConfigFromFlags()
|
||||
cfg.ChainID = SimAppChainID
|
||||
for i := range seeds {
|
||||
seed := seeds[i]
|
||||
t.Run(fmt.Sprintf("seed: %d", seed), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
RunWithSeed(t, NewSimApp[T], AppConfig(), cfg, seed, postRunActions...)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// RunWithSeed initializes and executes a simulation run with the given seed, generating blocks and transactions.
|
||||
func RunWithSeed[T Tx, V SimulationApp[T]](
|
||||
t *testing.T,
|
||||
appFactory AppFactory[T, V],
|
||||
appConfig depinject.Config,
|
||||
tCfg simtypes.Config,
|
||||
seed int64,
|
||||
postRunActions ...func(t testing.TB, app TestInstance[T], accs []simtypes.Account),
|
||||
) {
|
||||
t.Helper()
|
||||
r := rand.New(rand.NewSource(seed))
|
||||
testInstance := SetupTestInstance[T, V](t, appFactory, appConfig)
|
||||
accounts, genesisAppState, chainID, genesisTimestamp := prepareInitialGenesisState(testInstance.App, r, testInstance.BankKeeper, tCfg, testInstance.ModuleManager)
|
||||
|
||||
appManager := testInstance.AppManager
|
||||
appStore := testInstance.App.Store()
|
||||
txConfig := testInstance.App.TxConfig()
|
||||
rootCtx, done := context.WithCancel(context.Background())
|
||||
defer done()
|
||||
initRsp, stateRoot := doChainInitWithGenesis(t, rootCtx, chainID, genesisTimestamp, appManager, testInstance.TxDecoder, genesisAppState, appStore)
|
||||
activeValidatorSet := simsxv2.NewValSet().Update(initRsp.ValidatorUpdates)
|
||||
valsetHistory := simsxv2.NewValSetHistory(1)
|
||||
valsetHistory.Add(genesisTimestamp, activeValidatorSet)
|
||||
|
||||
emptySimParams := make(map[string]json.RawMessage) // todo read sims params from disk as before
|
||||
|
||||
modules := testInstance.ModuleManager.Modules()
|
||||
msgFactoriesFn := prepareSimsMsgFactories(r, modules, simsx.ParamWeightSource(emptySimParams))
|
||||
|
||||
cs := chainState[T]{
|
||||
chainID: chainID,
|
||||
blockTime: genesisTimestamp,
|
||||
activeValidatorSet: activeValidatorSet,
|
||||
valsetHistory: valsetHistory,
|
||||
stateRoot: stateRoot,
|
||||
app: appManager,
|
||||
appStore: appStore,
|
||||
txConfig: txConfig,
|
||||
}
|
||||
doMainLoop(
|
||||
t,
|
||||
rootCtx,
|
||||
cs,
|
||||
msgFactoriesFn,
|
||||
r,
|
||||
testInstance.AuthKeeper,
|
||||
testInstance.BankKeeper,
|
||||
accounts,
|
||||
testInstance.TXBuilder,
|
||||
testInstance.StakingKeeper,
|
||||
)
|
||||
require.NoError(t, testInstance.App.Close(), "closing app")
|
||||
|
||||
for _, step := range postRunActions {
|
||||
step(t, testInstance, accounts)
|
||||
}
|
||||
}
|
||||
|
||||
// prepareInitialGenesisState initializes the genesis state for simulation by generating accounts, app state, chain ID, and timestamp.
|
||||
// It uses a random seed, configuration parameters, and module manager to customize the state.
|
||||
// Blocked accounts are removed from the simulation accounts list based on the bank keeper's configuration.
|
||||
func prepareInitialGenesisState[T Tx](
|
||||
app SimulationApp[T],
|
||||
r *rand.Rand,
|
||||
bankKeeper BankKeeper,
|
||||
tCfg simtypes.Config,
|
||||
moduleManager ModuleManager,
|
||||
) ([]simtypes.Account, json.RawMessage, string, time.Time) {
|
||||
txConfig := app.TxConfig()
|
||||
// todo: replace legacy testdata functions ?
|
||||
appStateFn := simtestutil.AppStateFn(
|
||||
app.AppCodec(),
|
||||
txConfig.SigningContext().AddressCodec(),
|
||||
txConfig.SigningContext().ValidatorAddressCodec(),
|
||||
toLegacySimsModule(moduleManager.Modules()),
|
||||
app.DefaultGenesis(),
|
||||
)
|
||||
params := simulation.RandomParams(r)
|
||||
accounts := slices.DeleteFunc(simtypes.RandomAccounts(r, params.NumKeys()),
|
||||
func(acc simtypes.Account) bool { // remove blocked accounts
|
||||
return bankKeeper.GetBlockedAddresses()[acc.AddressBech32]
|
||||
})
|
||||
|
||||
appState, accounts, chainID, genesisTimestamp := appStateFn(r, accounts, tCfg)
|
||||
return accounts, appState, chainID, genesisTimestamp
|
||||
}
|
||||
|
||||
// doChainInitWithGenesis initializes the blockchain state with the provided genesis data and returns the initial block response and state root.
|
||||
func doChainInitWithGenesis[T Tx](
|
||||
t *testing.T,
|
||||
ctx context.Context,
|
||||
chainID string,
|
||||
genesisTimestamp time.Time,
|
||||
app appmanager.AppManager[T],
|
||||
txDecoder transaction.Codec[T],
|
||||
genesisAppState json.RawMessage,
|
||||
appStore cometbfttypes.Store,
|
||||
) (*server.BlockResponse, store.Hash) {
|
||||
t.Helper()
|
||||
genesisReq := &server.BlockRequest[T]{
|
||||
Height: 0,
|
||||
Time: genesisTimestamp,
|
||||
Hash: make([]byte, 32),
|
||||
ChainId: chainID,
|
||||
AppHash: make([]byte, 32),
|
||||
IsGenesis: true,
|
||||
}
|
||||
|
||||
initialConsensusParams := &consensustypes.MsgUpdateParams{
|
||||
Block: &cmtproto.BlockParams{
|
||||
MaxBytes: 200000,
|
||||
MaxGas: 100_000_000,
|
||||
},
|
||||
Evidence: &cmtproto.EvidenceParams{
|
||||
MaxAgeNumBlocks: 302400,
|
||||
MaxAgeDuration: 504 * time.Hour, // 3 weeks is the max duration
|
||||
MaxBytes: 10000,
|
||||
},
|
||||
Validator: &cmtproto.ValidatorParams{PubKeyTypes: []string{cmttypes.ABCIPubKeyTypeEd25519, cmttypes.ABCIPubKeyTypeSecp256k1}},
|
||||
}
|
||||
genesisCtx := context.WithValue(ctx, corecontext.CometParamsInitInfoKey, initialConsensusParams)
|
||||
initRsp, genesisStateChanges, err := app.InitGenesis(genesisCtx, genesisReq, genesisAppState, txDecoder)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, appStore.SetInitialVersion(0))
|
||||
changeSet, err := genesisStateChanges.GetStateChanges()
|
||||
require.NoError(t, err)
|
||||
|
||||
stateRoot, err := appStore.Commit(&store.Changeset{Changes: changeSet})
|
||||
require.NoError(t, err)
|
||||
return initRsp, stateRoot
|
||||
}
|
||||
|
||||
// chainState represents the state of a blockchain during a simulation run.
|
||||
type chainState[T Tx] struct {
|
||||
chainID string
|
||||
blockTime time.Time
|
||||
activeValidatorSet simsxv2.WeightedValidators
|
||||
valsetHistory *simsxv2.ValSetHistory
|
||||
stateRoot store.Hash
|
||||
app appmanager.TransactionFuzzer[T]
|
||||
appStore storev2.RootStore
|
||||
txConfig client.TxConfig
|
||||
}
|
||||
|
||||
// doMainLoop executes the main simulation loop after chain setup with genesis block.
|
||||
// Based on the initial seed and configurations, a deterministic set of messages is generated
|
||||
// and executed. Events like validators missing votes or double signing are included in this
|
||||
// process. The runtime tracks the validator's state and history.
|
||||
func doMainLoop[T Tx](
|
||||
t *testing.T,
|
||||
rootCtx context.Context,
|
||||
cs chainState[T],
|
||||
nextMsgFactory func() simsx.SimMsgFactoryX,
|
||||
r *rand.Rand,
|
||||
authKeeper AuthKeeper,
|
||||
bankKeeper simsx.BalanceSource,
|
||||
accounts []simtypes.Account,
|
||||
txBuilder simsxv2.TXBuilder[T],
|
||||
stakingKeeper StakingKeeper,
|
||||
) {
|
||||
t.Helper()
|
||||
blockTime := cs.blockTime
|
||||
activeValidatorSet := cs.activeValidatorSet
|
||||
if len(activeValidatorSet) == 0 {
|
||||
t.Fatal("no active validators in chain setup")
|
||||
return
|
||||
}
|
||||
valsetHistory := cs.valsetHistory
|
||||
stateRoot := cs.stateRoot
|
||||
chainID := cs.chainID
|
||||
app := cs.app
|
||||
appStore := cs.appStore
|
||||
|
||||
const ( // todo: read from CLI instead
|
||||
numBlocks = 100 // 500 default
|
||||
maxTXPerBlock = 200 // 200 default
|
||||
)
|
||||
|
||||
var (
|
||||
txSkippedCounter int
|
||||
txTotalCounter int
|
||||
)
|
||||
rootReporter := simsx.NewBasicSimulationReporter()
|
||||
futureOpsReg := simsxv2.NewFutureOpsRegistry()
|
||||
|
||||
for i := 0; i < numBlocks; i++ {
|
||||
if len(activeValidatorSet) == 0 {
|
||||
t.Skipf("run out of validators in block: %d\n", i+1)
|
||||
return
|
||||
}
|
||||
blockTime = blockTime.Add(minTimePerBlock)
|
||||
blockTime = blockTime.Add(time.Duration(int64(r.Intn(int(timeRangePerBlock/time.Second)))) * time.Second)
|
||||
valsetHistory.Add(blockTime, activeValidatorSet)
|
||||
blockReqN := &server.BlockRequest[T]{
|
||||
Height: uint64(1 + i),
|
||||
Time: blockTime,
|
||||
Hash: stateRoot,
|
||||
AppHash: stateRoot,
|
||||
ChainId: chainID,
|
||||
}
|
||||
|
||||
cometInfo := comet.Info{
|
||||
ValidatorsHash: nil,
|
||||
Evidence: valsetHistory.MissBehaviour(r),
|
||||
ProposerAddress: activeValidatorSet[0].Address,
|
||||
LastCommit: activeValidatorSet.NewCommitInfo(r),
|
||||
}
|
||||
fOps, pos := futureOpsReg.PopScheduledFor(blockTime), 0
|
||||
addressCodec := cs.txConfig.SigningContext().AddressCodec()
|
||||
simsCtx := context.WithValue(rootCtx, corecontext.CometInfoKey, cometInfo) // required for ContextAwareCometInfoService
|
||||
resultHandlers := make([]simsx.SimDeliveryResultHandler, 0, maxTXPerBlock)
|
||||
var txPerBlockCounter int
|
||||
blockRsp, updates, err := app.DeliverSims(simsCtx, blockReqN, func(ctx context.Context) iter.Seq[T] {
|
||||
return func(yield func(T) bool) {
|
||||
unbondingTime, err := stakingKeeper.UnbondingTime(ctx)
|
||||
require.NoError(t, err)
|
||||
valsetHistory.SetMaxHistory(minBlocksInUnbondingPeriod(unbondingTime))
|
||||
testData := simsx.NewChainDataSource(ctx, r, authKeeper, bankKeeper, addressCodec, accounts...)
|
||||
|
||||
for txPerBlockCounter < maxTXPerBlock {
|
||||
txPerBlockCounter++
|
||||
mergedMsgFactory := func() simsx.SimMsgFactoryX {
|
||||
if pos < len(fOps) {
|
||||
pos++
|
||||
return fOps[pos-1]
|
||||
}
|
||||
return nextMsgFactory()
|
||||
}()
|
||||
reporter := rootReporter.WithScope(mergedMsgFactory.MsgType())
|
||||
if fx, ok := mergedMsgFactory.(simsx.HasFutureOpsRegistry); ok {
|
||||
fx.SetFutureOpsRegistry(futureOpsReg)
|
||||
continue
|
||||
}
|
||||
|
||||
// the stf context is required to access state via keepers
|
||||
signers, msg := mergedMsgFactory.Create()(ctx, testData, reporter)
|
||||
if reporter.IsSkipped() {
|
||||
txSkippedCounter++
|
||||
require.NoError(t, reporter.Close())
|
||||
continue
|
||||
}
|
||||
resultHandlers = append(resultHandlers, mergedMsgFactory.DeliveryResultHandler())
|
||||
reporter.Success(msg)
|
||||
require.NoError(t, reporter.Close())
|
||||
|
||||
tx, err := txBuilder.Build(ctx, authKeeper, signers, msg, r, chainID)
|
||||
require.NoError(t, err)
|
||||
if !yield(tx) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
require.NoError(t, err, "%d, %s", blockReqN.Height, blockReqN.Time)
|
||||
changeSet, err := updates.GetStateChanges()
|
||||
require.NoError(t, err)
|
||||
stateRoot, err = appStore.Commit(&store.Changeset{
|
||||
Version: blockReqN.Height,
|
||||
Changes: changeSet,
|
||||
})
|
||||
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, len(resultHandlers), len(blockRsp.TxResults), "txPerBlockCounter: %d, totalSkipped: %d", txPerBlockCounter, txSkippedCounter)
|
||||
for i, v := range blockRsp.TxResults {
|
||||
require.NoError(t, resultHandlers[i](v.Error))
|
||||
}
|
||||
txTotalCounter += txPerBlockCounter
|
||||
activeValidatorSet = activeValidatorSet.Update(blockRsp.ValidatorUpdates)
|
||||
}
|
||||
fmt.Println("+++ reporter:\n" + rootReporter.Summary().String())
|
||||
fmt.Printf("Tx total: %d skipped: %d\n", txTotalCounter, txSkippedCounter)
|
||||
}
|
||||
|
||||
// prepareSimsMsgFactories constructs and returns a function to retrieve simulation message factories for all modules.
|
||||
// It initializes proposal and factory registries, registers proposals and weighted operations, and sorts deterministically.
|
||||
func prepareSimsMsgFactories(
|
||||
r *rand.Rand,
|
||||
modules map[string]appmodulev2.AppModule,
|
||||
weights simsx.WeightSource,
|
||||
) func() simsx.SimMsgFactoryX {
|
||||
moduleNames := slices.Collect(maps.Keys(modules))
|
||||
slices.Sort(moduleNames) // make deterministic
|
||||
|
||||
// get all proposal types
|
||||
proposalRegistry := simsx.NewUniqueTypeRegistry()
|
||||
for _, n := range moduleNames {
|
||||
switch xm := modules[n].(type) { // nolint: gocritic // extended in the future
|
||||
case HasProposalMsgsX:
|
||||
xm.ProposalMsgsX(weights, proposalRegistry)
|
||||
// todo: register legacy and v1 msg proposals
|
||||
}
|
||||
}
|
||||
// register all msg factories
|
||||
factoryRegistry := simsx.NewUnorderedRegistry()
|
||||
for _, n := range moduleNames {
|
||||
switch xm := modules[n].(type) {
|
||||
case HasWeightedOperationsX:
|
||||
xm.WeightedOperationsX(weights, factoryRegistry)
|
||||
case HasWeightedOperationsXWithProposals:
|
||||
xm.WeightedOperationsX(weights, factoryRegistry, proposalRegistry.Iterator(), nil)
|
||||
}
|
||||
}
|
||||
return simsxv2.NextFactoryFn(factoryRegistry.Elements(), r)
|
||||
}
|
||||
|
||||
func toLegacySimsModule(modules map[string]appmodule.AppModule) []module.AppModuleSimulation {
|
||||
r := make([]module.AppModuleSimulation, 0, len(modules))
|
||||
names := slices.Collect(maps.Keys(modules))
|
||||
slices.Sort(names) // make deterministic
|
||||
for _, v := range names {
|
||||
if m, ok := modules[v].(module.AppModuleSimulation); ok {
|
||||
r = append(r, m)
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func minBlocksInUnbondingPeriod(unbondingTime time.Duration) int {
|
||||
maxblocks := unbondingTime / maxTimePerBlock
|
||||
return max(int(maxblocks)-1, 1)
|
||||
}
|
||||
7
simapp/v2/sim_test.go
Normal file
7
simapp/v2/sim_test.go
Normal file
@ -0,0 +1,7 @@
|
||||
package simapp
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestSimsAppV2(t *testing.T) {
|
||||
RunWithSeeds[Tx](t, defaultSeeds)
|
||||
}
|
||||
@ -61,24 +61,20 @@ type ResultHandlingSimMsgFactory[T sdk.Msg] struct {
|
||||
|
||||
// NewSimMsgFactoryWithDeliveryResultHandler constructor
|
||||
func NewSimMsgFactoryWithDeliveryResultHandler[T sdk.Msg](f FactoryMethodWithDeliveryResultHandler[T]) *ResultHandlingSimMsgFactory[T] {
|
||||
// the result handler is always called after the factory. so we initialize it lazy for syntactic sugar and simplicity
|
||||
// in the message factory function that is implemented by the users
|
||||
var lazyResultHandler SimDeliveryResultHandler
|
||||
r := &ResultHandlingSimMsgFactory[T]{
|
||||
resultHandler: func(err error) error {
|
||||
return lazyResultHandler(err)
|
||||
},
|
||||
resultHandler: expectNoError,
|
||||
}
|
||||
r.SimMsgFactoryFn = func(ctx context.Context, testData *ChainDataSource, reporter SimulationReporter) (signer []SimAccount, msg T) {
|
||||
signer, msg, lazyResultHandler = f(ctx, testData, reporter)
|
||||
if lazyResultHandler == nil {
|
||||
lazyResultHandler = expectNoError
|
||||
signer, msg, r.resultHandler = f(ctx, testData, reporter)
|
||||
if r.resultHandler == nil {
|
||||
r.resultHandler = expectNoError
|
||||
}
|
||||
return
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// DeliveryResultHandler result handler of the last msg factory invocation
|
||||
func (f ResultHandlingSimMsgFactory[T]) DeliveryResultHandler() SimDeliveryResultHandler {
|
||||
return f.resultHandler
|
||||
}
|
||||
|
||||
@ -160,6 +160,12 @@ func NewUniqueTypeRegistry() UniqueTypeRegistry {
|
||||
}
|
||||
|
||||
func (s UniqueTypeRegistry) Add(weight uint32, f SimMsgFactoryX) {
|
||||
if weight == 0 {
|
||||
return
|
||||
}
|
||||
if f == nil {
|
||||
panic("message factory must not be nil")
|
||||
}
|
||||
msgType := f.MsgType()
|
||||
msgTypeURL := sdk.MsgTypeURL(msgType)
|
||||
if _, exists := s[msgTypeURL]; exists {
|
||||
@ -170,8 +176,7 @@ func (s UniqueTypeRegistry) Add(weight uint32, f SimMsgFactoryX) {
|
||||
|
||||
// Iterator returns an iterator function for a Go for loop sorted by weight desc.
|
||||
func (s UniqueTypeRegistry) Iterator() WeightedProposalMsgIter {
|
||||
x := maps.Values(s)
|
||||
sortedWeightedFactory := slices.SortedFunc(x, func(a, b WeightedFactory) int {
|
||||
sortedWeightedFactory := slices.SortedFunc(maps.Values(s), func(a, b WeightedFactory) int {
|
||||
return a.Compare(b)
|
||||
})
|
||||
|
||||
@ -184,6 +189,33 @@ func (s UniqueTypeRegistry) Iterator() WeightedProposalMsgIter {
|
||||
}
|
||||
}
|
||||
|
||||
var _ Registry = &UnorderedRegistry{}
|
||||
|
||||
// UnorderedRegistry represents a collection of WeightedFactory elements without guaranteed order.
|
||||
// It is used to maintain factories coupled with their associated weights for simulation purposes.
|
||||
type UnorderedRegistry []WeightedFactory
|
||||
|
||||
func NewUnorderedRegistry() *UnorderedRegistry {
|
||||
r := make(UnorderedRegistry, 0)
|
||||
return &r
|
||||
}
|
||||
|
||||
// Add appends a new WeightedFactory with the provided weight and factory to the UnorderedRegistry.
|
||||
func (x *UnorderedRegistry) Add(weight uint32, f SimMsgFactoryX) {
|
||||
if weight == 0 {
|
||||
return
|
||||
}
|
||||
if f == nil {
|
||||
panic("message factory must not be nil")
|
||||
}
|
||||
*x = append(*x, WeightedFactory{Weight: weight, Factory: f})
|
||||
}
|
||||
|
||||
// Elements returns all collected elements
|
||||
func (x *UnorderedRegistry) Elements() []WeightedFactory {
|
||||
return *x
|
||||
}
|
||||
|
||||
// WeightedFactory is a Weight Factory tuple
|
||||
type WeightedFactory struct {
|
||||
Weight uint32
|
||||
|
||||
@ -119,6 +119,9 @@ func TestUniqueTypeRegistry(t *testing.T) {
|
||||
exampleFactory := SimMsgFactoryFn[*testdata.TestMsg](func(ctx context.Context, testData *ChainDataSource, reporter SimulationReporter) (signer []SimAccount, msg *testdata.TestMsg) {
|
||||
return []SimAccount{}, nil
|
||||
})
|
||||
exampleFactory2 := SimMsgFactoryFn[*testdata.MsgCreateDog](func(ctx context.Context, testData *ChainDataSource, reporter SimulationReporter) (signer []SimAccount, msg *testdata.MsgCreateDog) {
|
||||
return []SimAccount{}, nil
|
||||
})
|
||||
|
||||
specs := map[string]struct {
|
||||
src []WeightedFactory
|
||||
@ -129,6 +132,10 @@ func TestUniqueTypeRegistry(t *testing.T) {
|
||||
src: []WeightedFactory{{Weight: 1, Factory: exampleFactory}},
|
||||
exp: []WeightedFactoryMethod{{Weight: 1, Factory: exampleFactory.Create()}},
|
||||
},
|
||||
"sorted": {
|
||||
src: []WeightedFactory{{Weight: 2, Factory: exampleFactory2}, {Weight: 1, Factory: exampleFactory}},
|
||||
exp: []WeightedFactoryMethod{{Weight: 1, Factory: exampleFactory.Create()}, {Weight: 2, Factory: exampleFactory2.Create()}},
|
||||
},
|
||||
"duplicate": {
|
||||
src: []WeightedFactory{{Weight: 1, Factory: exampleFactory}, {Weight: 2, Factory: exampleFactory}},
|
||||
expErr: true,
|
||||
|
||||
@ -11,6 +11,7 @@ import (
|
||||
dbm "github.com/cosmos/cosmos-db"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"cosmossdk.io/core/server"
|
||||
corestore "cosmossdk.io/core/store"
|
||||
"cosmossdk.io/log"
|
||||
|
||||
@ -19,8 +20,6 @@ import (
|
||||
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
"github.com/cosmos/cosmos-sdk/runtime"
|
||||
"github.com/cosmos/cosmos-sdk/server"
|
||||
servertypes "github.com/cosmos/cosmos-sdk/server/types"
|
||||
simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/types/module"
|
||||
@ -77,7 +76,7 @@ func Run[T SimulationApp](
|
||||
db corestore.KVStoreWithBatch,
|
||||
traceStore io.Writer,
|
||||
loadLatest bool,
|
||||
appOpts servertypes.AppOptions,
|
||||
appOpts server.DynamicConfig,
|
||||
baseAppOptions ...func(*baseapp.BaseApp),
|
||||
) T,
|
||||
setupStateFactory func(app T) SimStateFactory,
|
||||
@ -103,7 +102,7 @@ func RunWithSeeds[T SimulationApp](
|
||||
db corestore.KVStoreWithBatch,
|
||||
traceStore io.Writer,
|
||||
loadLatest bool,
|
||||
appOpts servertypes.AppOptions,
|
||||
appOpts server.DynamicConfig,
|
||||
baseAppOptions ...func(*baseapp.BaseApp),
|
||||
) T,
|
||||
setupStateFactory func(app T) SimStateFactory,
|
||||
@ -123,7 +122,7 @@ func RunWithSeedsAndRandAcc[T SimulationApp](
|
||||
db corestore.KVStoreWithBatch,
|
||||
traceStore io.Writer,
|
||||
loadLatest bool,
|
||||
appOpts servertypes.AppOptions,
|
||||
appOpts server.DynamicConfig,
|
||||
baseAppOptions ...func(*baseapp.BaseApp),
|
||||
) T,
|
||||
setupStateFactory func(app T) SimStateFactory,
|
||||
@ -153,7 +152,7 @@ func RunWithSeedsAndRandAcc[T SimulationApp](
|
||||
func RunWithSeed[T SimulationApp](
|
||||
tb testing.TB,
|
||||
cfg simtypes.Config,
|
||||
appFactory func(logger log.Logger, db corestore.KVStoreWithBatch, traceStore io.Writer, loadLatest bool, appOpts servertypes.AppOptions, baseAppOptions ...func(*baseapp.BaseApp)) T,
|
||||
appFactory func(logger log.Logger, db corestore.KVStoreWithBatch, traceStore io.Writer, loadLatest bool, appOpts server.DynamicConfig, baseAppOptions ...func(*baseapp.BaseApp)) T,
|
||||
setupStateFactory func(app T) SimStateFactory,
|
||||
seed int64,
|
||||
fuzzSeed []byte,
|
||||
@ -167,7 +166,7 @@ func RunWithSeed[T SimulationApp](
|
||||
func RunWithSeedAndRandAcc[T SimulationApp](
|
||||
tb testing.TB,
|
||||
cfg simtypes.Config,
|
||||
appFactory func(logger log.Logger, db corestore.KVStoreWithBatch, traceStore io.Writer, loadLatest bool, appOpts servertypes.AppOptions, baseAppOptions ...func(*baseapp.BaseApp)) T,
|
||||
appFactory func(logger log.Logger, db corestore.KVStoreWithBatch, traceStore io.Writer, loadLatest bool, appOpts server.DynamicConfig, baseAppOptions ...func(*baseapp.BaseApp)) T,
|
||||
setupStateFactory func(app T) SimStateFactory,
|
||||
seed int64,
|
||||
fuzzSeed []byte,
|
||||
@ -330,7 +329,7 @@ func prepareWeightedOps(
|
||||
func NewSimulationAppInstance[T SimulationApp](
|
||||
tb testing.TB,
|
||||
tCfg simtypes.Config,
|
||||
appFactory func(logger log.Logger, db corestore.KVStoreWithBatch, traceStore io.Writer, loadLatest bool, appOpts servertypes.AppOptions, baseAppOptions ...func(*baseapp.BaseApp)) T,
|
||||
appFactory func(logger log.Logger, db corestore.KVStoreWithBatch, traceStore io.Writer, loadLatest bool, appOpts server.DynamicConfig, baseAppOptions ...func(*baseapp.BaseApp)) T,
|
||||
) TestInstance[T] {
|
||||
tb.Helper()
|
||||
workDir := tb.TempDir()
|
||||
@ -350,7 +349,7 @@ func NewSimulationAppInstance[T SimulationApp](
|
||||
})
|
||||
appOptions := make(simtestutil.AppOptionsMap)
|
||||
appOptions[flags.FlagHome] = workDir
|
||||
appOptions[server.FlagInvCheckPeriod] = cli.FlagPeriodValue
|
||||
appOptions[flags.FlagInvCheckPeriod] = cli.FlagPeriodValue
|
||||
opts := []func(*baseapp.BaseApp){baseapp.SetChainID(tCfg.ChainID)}
|
||||
if tCfg.FauxMerkle {
|
||||
opts = append(opts, FauxMerkleModeOpt)
|
||||
|
||||
31
simsx/v2/msg_factory.go
Normal file
31
simsx/v2/msg_factory.go
Normal file
@ -0,0 +1,31 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/simsx"
|
||||
)
|
||||
|
||||
// NextFactoryFn shuffles and processes a list of weighted factories, returning a selection function for factory objects.
|
||||
func NextFactoryFn(factories []simsx.WeightedFactory, r *rand.Rand) func() simsx.SimMsgFactoryX {
|
||||
factCount := len(factories)
|
||||
r.Shuffle(factCount, func(i, j int) {
|
||||
factories[i], factories[j] = factories[j], factories[i]
|
||||
})
|
||||
var totalWeight int
|
||||
for _, f := range factories {
|
||||
totalWeight += int(f.Weight)
|
||||
}
|
||||
return func() simsx.SimMsgFactoryX {
|
||||
// this is copied from old sims WeightedOperations.getSelectOpFn
|
||||
x := r.Intn(totalWeight)
|
||||
for i := 0; i < factCount; i++ {
|
||||
if x <= int(factories[i].Weight) {
|
||||
return factories[i].Factory
|
||||
}
|
||||
x -= int(factories[i].Weight)
|
||||
}
|
||||
// shouldn't happen
|
||||
return factories[0].Factory
|
||||
}
|
||||
}
|
||||
65
simsx/v2/registry.go
Normal file
65
simsx/v2/registry.go
Normal file
@ -0,0 +1,65 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/huandu/skiplist"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/simsx"
|
||||
)
|
||||
|
||||
// FutureOpsRegistry is a registry for scheduling and retrieving operations mapped to future block times.
|
||||
type FutureOpsRegistry struct {
|
||||
list *skiplist.SkipList
|
||||
}
|
||||
|
||||
// NewFutureOpsRegistry constructor
|
||||
func NewFutureOpsRegistry() *FutureOpsRegistry {
|
||||
list := skiplist.New(timeComparator{})
|
||||
list.SetRandSource(rand.NewSource(1))
|
||||
return &FutureOpsRegistry{list: list}
|
||||
}
|
||||
|
||||
// Add schedules a new operation for the given block time
|
||||
func (l *FutureOpsRegistry) Add(blockTime time.Time, fx simsx.SimMsgFactoryX) {
|
||||
if fx == nil {
|
||||
panic("message factory must not be nil")
|
||||
}
|
||||
if blockTime.IsZero() {
|
||||
return
|
||||
}
|
||||
var scheduledOps []simsx.SimMsgFactoryX
|
||||
if e := l.list.Get(blockTime); e != nil {
|
||||
scheduledOps = e.Value.([]simsx.SimMsgFactoryX)
|
||||
}
|
||||
scheduledOps = append(scheduledOps, fx)
|
||||
l.list.Set(blockTime, scheduledOps)
|
||||
}
|
||||
|
||||
// PopScheduledFor retrieves and removes all scheduled operations up to the specified block time from the registry.
|
||||
func (l *FutureOpsRegistry) PopScheduledFor(blockTime time.Time) []simsx.SimMsgFactoryX {
|
||||
var r []simsx.SimMsgFactoryX
|
||||
for {
|
||||
e := l.list.Front()
|
||||
if e == nil || e.Key().(time.Time).After(blockTime) {
|
||||
break
|
||||
}
|
||||
r = append(r, e.Value.([]simsx.SimMsgFactoryX)...)
|
||||
l.list.RemoveFront()
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
var _ skiplist.Comparable = timeComparator{}
|
||||
|
||||
// used for SkipList
|
||||
type timeComparator struct{}
|
||||
|
||||
func (t timeComparator) Compare(lhs, rhs interface{}) int {
|
||||
return lhs.(time.Time).Compare(rhs.(time.Time))
|
||||
}
|
||||
|
||||
func (t timeComparator) CalcScore(key interface{}) float64 {
|
||||
return float64(key.(time.Time).UnixNano())
|
||||
}
|
||||
198
simsx/v2/txutils.go
Normal file
198
simsx/v2/txutils.go
Normal file
@ -0,0 +1,198 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
|
||||
"cosmossdk.io/core/transaction"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
|
||||
"github.com/cosmos/cosmos-sdk/simsx"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/types/simulation"
|
||||
"github.com/cosmos/cosmos-sdk/types/tx/signing"
|
||||
authsign "github.com/cosmos/cosmos-sdk/x/auth/signing"
|
||||
)
|
||||
|
||||
const DefaultGenTxGas = 10_000_000
|
||||
|
||||
type Tx = transaction.Tx
|
||||
|
||||
// TXBuilder abstract transaction builder
|
||||
type TXBuilder[T Tx] interface {
|
||||
// Build creates a signed transaction
|
||||
Build(ctx context.Context,
|
||||
ak simsx.AccountSource,
|
||||
senders []simsx.SimAccount,
|
||||
msg sdk.Msg,
|
||||
r *rand.Rand,
|
||||
chainID string,
|
||||
) (T, error)
|
||||
}
|
||||
|
||||
var _ TXBuilder[Tx] = TXBuilderFn[Tx](nil)
|
||||
|
||||
// TXBuilderFn adapter that implements the TXBuilder interface
|
||||
type TXBuilderFn[T Tx] func(ctx context.Context, ak simsx.AccountSource, senders []simsx.SimAccount, msg sdk.Msg, r *rand.Rand, chainID string) (T, error)
|
||||
|
||||
func (b TXBuilderFn[T]) Build(ctx context.Context, ak simsx.AccountSource, senders []simsx.SimAccount, msg sdk.Msg, r *rand.Rand, chainID string) (T, error) {
|
||||
return b(ctx, ak, senders, msg, r, chainID)
|
||||
}
|
||||
|
||||
// NewSDKTXBuilder constructor to create a signed transaction builder for sdk.Tx type.
|
||||
func NewSDKTXBuilder[T Tx](txConfig client.TxConfig, defaultGas uint64) TXBuilder[T] {
|
||||
return TXBuilderFn[T](func(ctx context.Context, ak simsx.AccountSource, senders []simsx.SimAccount, msg sdk.Msg, r *rand.Rand, chainID string) (tx T, err error) {
|
||||
accountNumbers := make([]uint64, len(senders))
|
||||
sequenceNumbers := make([]uint64, len(senders))
|
||||
for i := 0; i < len(senders); i++ {
|
||||
acc := ak.GetAccount(ctx, senders[i].Address)
|
||||
accountNumbers[i] = acc.GetAccountNumber()
|
||||
sequenceNumbers[i] = acc.GetSequence()
|
||||
}
|
||||
fees := senders[0].LiquidBalance().RandFees()
|
||||
sdkTx, err := GenSignedMockTx(
|
||||
r,
|
||||
txConfig,
|
||||
[]sdk.Msg{msg},
|
||||
fees,
|
||||
defaultGas,
|
||||
chainID,
|
||||
accountNumbers,
|
||||
sequenceNumbers,
|
||||
simsx.Collect(senders, func(a simsx.SimAccount) cryptotypes.PrivKey { return a.PrivKey })...,
|
||||
)
|
||||
if err != nil {
|
||||
return tx, err
|
||||
}
|
||||
out, ok := sdkTx.(T)
|
||||
if !ok {
|
||||
return out, errors.New("unexpected Tx type")
|
||||
}
|
||||
return out, nil
|
||||
})
|
||||
}
|
||||
|
||||
// GenSignedMockTx generates a signed mock transaction.
|
||||
func GenSignedMockTx(
|
||||
r *rand.Rand,
|
||||
txConfig client.TxConfig,
|
||||
msgs []sdk.Msg,
|
||||
feeAmt sdk.Coins,
|
||||
gas uint64,
|
||||
chainID string,
|
||||
accNums, accSeqs []uint64,
|
||||
priv ...cryptotypes.PrivKey,
|
||||
) (sdk.Tx, error) {
|
||||
sigs := make([]signing.SignatureV2, len(priv))
|
||||
|
||||
// create a random length memo
|
||||
memo := simulation.RandStringOfLength(r, simulation.RandIntBetween(r, 0, 100))
|
||||
|
||||
signMode, err := authsign.APISignModeToInternal(txConfig.SignModeHandler().DefaultMode())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 1st round: set SignatureV2 with empty signatures, to set correct
|
||||
// signer infos.
|
||||
for i, p := range priv {
|
||||
sigs[i] = signing.SignatureV2{
|
||||
PubKey: p.PubKey(),
|
||||
Data: &signing.SingleSignatureData{
|
||||
SignMode: signMode,
|
||||
},
|
||||
Sequence: accSeqs[i],
|
||||
}
|
||||
}
|
||||
|
||||
tx := txConfig.NewTxBuilder()
|
||||
err = tx.SetMsgs(msgs...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = tx.SetSignatures(sigs...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tx.SetMemo(memo)
|
||||
tx.SetFeeAmount(feeAmt)
|
||||
tx.SetGasLimit(gas)
|
||||
|
||||
// 2nd round: once all signer infos are set, every signer can sign.
|
||||
for i, p := range priv {
|
||||
signerData := authsign.SignerData{
|
||||
Address: sdk.AccAddress(p.PubKey().Address()).String(),
|
||||
ChainID: chainID,
|
||||
AccountNumber: accNums[i],
|
||||
Sequence: accSeqs[i],
|
||||
PubKey: p.PubKey(),
|
||||
}
|
||||
|
||||
signBytes, err := authsign.GetSignBytesAdapter(
|
||||
context.Background(), txConfig.SignModeHandler(), signMode, signerData,
|
||||
tx.GetTx())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("sign bytes: %w", err)
|
||||
}
|
||||
sig, err := p.Sign(signBytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("sign: %w", err)
|
||||
}
|
||||
sigs[i].Data.(*signing.SingleSignatureData).Signature = sig
|
||||
}
|
||||
|
||||
if err = tx.SetSignatures(sigs...); err != nil {
|
||||
return nil, fmt.Errorf("signature: %w", err)
|
||||
}
|
||||
|
||||
return tx.GetTx(), nil
|
||||
}
|
||||
|
||||
var _ transaction.Codec[Tx] = &GenericTxDecoder[Tx]{}
|
||||
|
||||
// GenericTxDecoder Encoder type that implements transaction.Codec
|
||||
type GenericTxDecoder[T Tx] struct {
|
||||
txConfig client.TxConfig
|
||||
}
|
||||
|
||||
// NewGenericTxDecoder constructor
|
||||
func NewGenericTxDecoder[T Tx](txConfig client.TxConfig) *GenericTxDecoder[T] {
|
||||
return &GenericTxDecoder[T]{txConfig: txConfig}
|
||||
}
|
||||
|
||||
// Decode implements transaction.Codec.
|
||||
func (t *GenericTxDecoder[T]) Decode(bz []byte) (T, error) {
|
||||
var out T
|
||||
tx, err := t.txConfig.TxDecoder()(bz)
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
|
||||
var ok bool
|
||||
out, ok = tx.(T)
|
||||
if !ok {
|
||||
return out, errors.New("unexpected Tx type")
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// DecodeJSON implements transaction.Codec.
|
||||
func (t *GenericTxDecoder[T]) DecodeJSON(bz []byte) (T, error) {
|
||||
var out T
|
||||
tx, err := t.txConfig.TxJSONDecoder()(bz)
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
|
||||
var ok bool
|
||||
out, ok = tx.(T)
|
||||
if !ok {
|
||||
return out, errors.New("unexpected Tx type")
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
108
simsx/v2/valset.go
Normal file
108
simsx/v2/valset.go
Normal file
@ -0,0 +1,108 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"math/rand"
|
||||
"slices"
|
||||
|
||||
appmodulev2 "cosmossdk.io/core/appmodule/v2"
|
||||
"cosmossdk.io/core/comet"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/simsx"
|
||||
)
|
||||
|
||||
// WeightedValidator represents a validator for usage in the sims runner.
|
||||
type WeightedValidator struct {
|
||||
Power int64
|
||||
Address []byte
|
||||
Offline bool
|
||||
}
|
||||
|
||||
// Compare determines the order between two WeightedValidator instances.
|
||||
// Returns -1 if the caller has higher Power, 1 if it has lower Power, and defaults to comparing their Address bytes.
|
||||
func (a WeightedValidator) Compare(b WeightedValidator) int {
|
||||
switch {
|
||||
case a.Power < b.Power:
|
||||
return 1
|
||||
case a.Power > b.Power:
|
||||
return -1
|
||||
default:
|
||||
return bytes.Compare(a.Address, b.Address)
|
||||
}
|
||||
}
|
||||
|
||||
// NewValSet constructor
|
||||
func NewValSet() WeightedValidators {
|
||||
return make(WeightedValidators, 0)
|
||||
}
|
||||
|
||||
// WeightedValidators represents a slice of WeightedValidator, used for managing and processing validator sets.
|
||||
type WeightedValidators []WeightedValidator
|
||||
|
||||
func (v WeightedValidators) Update(updates []appmodulev2.ValidatorUpdate) WeightedValidators {
|
||||
if len(updates) == 0 {
|
||||
return v
|
||||
}
|
||||
const truncatedSize = 20
|
||||
valUpdates := simsx.Collect(updates, func(u appmodulev2.ValidatorUpdate) WeightedValidator {
|
||||
hash := sha256.Sum256(u.PubKey)
|
||||
return WeightedValidator{Power: u.Power, Address: hash[:truncatedSize]}
|
||||
})
|
||||
newValset := slices.Clone(v)
|
||||
for _, u := range valUpdates {
|
||||
pos := slices.IndexFunc(newValset, func(val WeightedValidator) bool {
|
||||
return bytes.Equal(u.Address, val.Address)
|
||||
})
|
||||
if pos == -1 { // new address
|
||||
if u.Power > 0 {
|
||||
newValset = append(newValset, u)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if u.Power == 0 {
|
||||
newValset = append(newValset[0:pos], newValset[pos+1:]...)
|
||||
continue
|
||||
}
|
||||
newValset[pos].Power = u.Power
|
||||
}
|
||||
|
||||
newValset = slices.DeleteFunc(newValset, func(validator WeightedValidator) bool {
|
||||
return validator.Power == 0
|
||||
})
|
||||
|
||||
// sort vals by Power
|
||||
slices.SortFunc(newValset, func(a, b WeightedValidator) int {
|
||||
return a.Compare(b)
|
||||
})
|
||||
return newValset
|
||||
}
|
||||
|
||||
// NewCommitInfo build Comet commit info for the validator set
|
||||
func (v WeightedValidators) NewCommitInfo(r *rand.Rand) comet.CommitInfo {
|
||||
if len(v) == 0 {
|
||||
return comet.CommitInfo{Votes: make([]comet.VoteInfo, 0)}
|
||||
}
|
||||
if r.Intn(10) == 0 {
|
||||
v[r.Intn(len(v))].Offline = r.Intn(2) == 0
|
||||
}
|
||||
votes := make([]comet.VoteInfo, 0, len(v))
|
||||
for i := range v {
|
||||
if v[i].Offline {
|
||||
continue
|
||||
}
|
||||
votes = append(votes, comet.VoteInfo{
|
||||
Validator: comet.Validator{Address: v[i].Address, Power: v[i].Power},
|
||||
BlockIDFlag: comet.BlockIDFlagCommit,
|
||||
})
|
||||
}
|
||||
return comet.CommitInfo{Round: int32(r.Uint32()), Votes: votes}
|
||||
}
|
||||
|
||||
func (v WeightedValidators) TotalPower() int64 {
|
||||
var r int64
|
||||
for _, val := range v {
|
||||
r += val.Power
|
||||
}
|
||||
return r
|
||||
}
|
||||
78
simsx/v2/valset_history.go
Normal file
78
simsx/v2/valset_history.go
Normal file
@ -0,0 +1,78 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
"cosmossdk.io/core/comet"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/simsx"
|
||||
)
|
||||
|
||||
type historicValSet struct {
|
||||
blockTime time.Time
|
||||
vals WeightedValidators
|
||||
}
|
||||
type ValSetHistory struct {
|
||||
maxElements int
|
||||
blockOffset int
|
||||
vals []historicValSet
|
||||
}
|
||||
|
||||
func NewValSetHistory(maxElements int) *ValSetHistory {
|
||||
return &ValSetHistory{
|
||||
maxElements: maxElements,
|
||||
blockOffset: 1, // start at height 1
|
||||
vals: make([]historicValSet, 0, maxElements),
|
||||
}
|
||||
}
|
||||
|
||||
func (h *ValSetHistory) Add(blockTime time.Time, vals WeightedValidators) {
|
||||
vals = slices.DeleteFunc(vals, func(validator WeightedValidator) bool {
|
||||
return validator.Power == 0
|
||||
})
|
||||
slices.SortFunc(vals, func(a, b WeightedValidator) int {
|
||||
return b.Compare(a)
|
||||
})
|
||||
newEntry := historicValSet{blockTime: blockTime, vals: vals}
|
||||
if len(h.vals) >= h.maxElements {
|
||||
h.vals = append(h.vals[1:], newEntry)
|
||||
h.blockOffset++
|
||||
return
|
||||
}
|
||||
h.vals = append(h.vals, newEntry)
|
||||
}
|
||||
|
||||
// MissBehaviour determines if a random validator misbehaves, creating and returning evidence for duplicate voting.
|
||||
// Returns a slice of comet.Evidence if misbehavior is detected; otherwise, returns nil.
|
||||
// Has a 1% chance of generating evidence for a validator's misbehavior.
|
||||
// Recursively checks for other misbehavior instances and combines their evidence if any.
|
||||
// Utilizes a random generator to select a validator and evidence-related attributes.
|
||||
func (h *ValSetHistory) MissBehaviour(r *rand.Rand) []comet.Evidence {
|
||||
if r.Intn(100) != 0 { // 1% chance
|
||||
return nil
|
||||
}
|
||||
n := r.Intn(len(h.vals))
|
||||
badVal := simsx.OneOf(r, h.vals[n].vals)
|
||||
evidence := comet.Evidence{
|
||||
Type: comet.DuplicateVote,
|
||||
Validator: comet.Validator{Address: badVal.Address, Power: badVal.Power},
|
||||
Height: int64(h.blockOffset + n),
|
||||
Time: h.vals[n].blockTime,
|
||||
TotalVotingPower: h.vals[n].vals.TotalPower(),
|
||||
}
|
||||
if otherEvidence := h.MissBehaviour(r); otherEvidence != nil {
|
||||
return append([]comet.Evidence{evidence}, otherEvidence...)
|
||||
}
|
||||
return []comet.Evidence{evidence}
|
||||
}
|
||||
|
||||
func (h *ValSetHistory) SetMaxHistory(v int) {
|
||||
h.maxElements = v
|
||||
if len(h.vals) > h.maxElements {
|
||||
diff := len(h.vals) - h.maxElements
|
||||
h.vals = h.vals[diff:]
|
||||
h.blockOffset += diff
|
||||
}
|
||||
}
|
||||
@ -212,6 +212,7 @@ func AppStateRandomizedFn(
|
||||
|
||||
// AppStateFromGenesisFileFn util function to generate the genesis AppState
|
||||
// from a genesis.json file.
|
||||
// Deprecated: the private keys are not matching the accounts read from app state
|
||||
func AppStateFromGenesisFileFn(_ io.Reader, cdc codec.JSONCodec, genesisFile string) (genutiltypes.AppGenesis, []simtypes.Account, error) {
|
||||
file, err := os.Open(filepath.Clean(genesisFile))
|
||||
if err != nil {
|
||||
@ -233,6 +234,8 @@ func AppStateFromGenesisFileFn(_ io.Reader, cdc codec.JSONCodec, genesisFile str
|
||||
return *genesis, newAccs, nil
|
||||
}
|
||||
|
||||
// AccountsFromAppState
|
||||
// Deprecated: the private keys are not matching the accounts read from app state
|
||||
func AccountsFromAppState(cdc codec.JSONCodec, appStateJSON json.RawMessage) ([]simtypes.Account, error) {
|
||||
var appState map[string]json.RawMessage
|
||||
if err := json.Unmarshal(appStateJSON, &appState); err != nil {
|
||||
|
||||
@ -22,7 +22,16 @@ import (
|
||||
)
|
||||
|
||||
// GenSignedMockTx generates a signed mock transaction.
|
||||
func GenSignedMockTx(r *rand.Rand, txConfig client.TxConfig, msgs []sdk.Msg, feeAmt sdk.Coins, gas uint64, chainID string, accNums, accSeqs []uint64, priv ...cryptotypes.PrivKey) (sdk.Tx, error) {
|
||||
func GenSignedMockTx(
|
||||
r *rand.Rand,
|
||||
txConfig client.TxConfig,
|
||||
msgs []sdk.Msg,
|
||||
feeAmt sdk.Coins,
|
||||
gas uint64,
|
||||
chainID string,
|
||||
accNums, accSeqs []uint64,
|
||||
priv ...cryptotypes.PrivKey,
|
||||
) (sdk.Tx, error) {
|
||||
sigs := make([]signing.SignatureV2, len(priv))
|
||||
|
||||
// create a random length memo
|
||||
|
||||
3
testutil/testdata/tx.go
vendored
3
testutil/testdata/tx.go
vendored
@ -1,12 +1,13 @@
|
||||
package testdata
|
||||
|
||||
import (
|
||||
"cosmossdk.io/math"
|
||||
"testing"
|
||||
|
||||
"gotest.tools/v3/assert"
|
||||
"pgregory.net/rapid"
|
||||
|
||||
"cosmossdk.io/math"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256r1"
|
||||
|
||||
@ -131,8 +131,7 @@ func submitProposalWithVotesScheduled(
|
||||
proposalMsgs ...sdk.Msg,
|
||||
) ([]simsx.SimAccount, *v1.MsgSubmitProposal) {
|
||||
r := testData.Rand()
|
||||
expedited := true
|
||||
// expedited := r.Bool()
|
||||
expedited := r.Bool()
|
||||
params := must(k.Params.Get(ctx))
|
||||
minDeposits := params.MinDeposit
|
||||
if expedited {
|
||||
|
||||
@ -14,7 +14,9 @@ func MsgSendFactory(k keeper.Keeper) simsx.SimMsgFactoryFn[*nft.MsgSend] {
|
||||
return func(ctx context.Context, testData *simsx.ChainDataSource, reporter simsx.SimulationReporter) ([]simsx.SimAccount, *nft.MsgSend) {
|
||||
from := testData.AnyAccount(reporter, simsx.WithSpendableBalance())
|
||||
to := testData.AnyAccount(reporter, simsx.ExcludeAccounts(from))
|
||||
|
||||
if reporter.IsSkipped() {
|
||||
return nil, nil
|
||||
}
|
||||
n, err := randNFT(ctx, testData.Rand(), k, from.Address)
|
||||
if err != nil {
|
||||
reporter.Skip(err.Error())
|
||||
|
||||
@ -313,21 +313,11 @@ func MsgRotateConsPubKeyFactory(k *keeper.Keeper) simsx.SimMsgFactoryFn[*types.M
|
||||
return nil, nil
|
||||
}
|
||||
// check whether the new cons key associated with another validator
|
||||
newConsAddr := sdk.ConsAddress(otherAccount.ConsKey.PubKey().Address())
|
||||
|
||||
if _, err := k.GetValidatorByConsAddr(ctx, newConsAddr); err == nil {
|
||||
reporter.Skip("cons key already used")
|
||||
assertKeyUnused(ctx, reporter, k, otherAccount.ConsKey.PubKey())
|
||||
if reporter.IsSkipped() {
|
||||
return nil, nil
|
||||
}
|
||||
msg := must(types.NewMsgRotateConsPubKey(val.GetOperator(), otherAccount.ConsKey.PubKey()))
|
||||
|
||||
// check if there's another key rotation for this same key in the same block
|
||||
for _, r := range must(k.GetBlockConsPubKeyRotationHistory(ctx)) {
|
||||
if r.NewConsPubkey.Compare(msg.NewPubkey) == 0 {
|
||||
reporter.Skip("cons key already used in this block")
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
return []simsx.SimAccount{valOper}, msg
|
||||
}
|
||||
}
|
||||
@ -363,6 +353,16 @@ func randomValidator(ctx context.Context, reporter simsx.SimulationReporter, k *
|
||||
|
||||
// skips execution if there's another key rotation for the same key in the same block
|
||||
func assertKeyUnused(ctx context.Context, reporter simsx.SimulationReporter, k *keeper.Keeper, newPubKey cryptotypes.PubKey) {
|
||||
newConsAddr := sdk.ConsAddress(newPubKey.Address())
|
||||
if rotatedTo, _ := k.ConsAddrToValidatorIdentifierMap.Get(ctx, newConsAddr); rotatedTo != nil {
|
||||
reporter.Skip("consensus key already used")
|
||||
return
|
||||
}
|
||||
if _, err := k.GetValidatorByConsAddr(ctx, newConsAddr); err == nil {
|
||||
reporter.Skip("cons key already used")
|
||||
return
|
||||
}
|
||||
|
||||
allRotations, err := k.GetBlockConsPubKeyRotationHistory(ctx)
|
||||
if err != nil {
|
||||
reporter.Skipf("cannot get block cons key rotation history: %s", err.Error())
|
||||
|
||||
Loading…
Reference in New Issue
Block a user