diff --git a/CHANGELOG.md b/CHANGELOG.md index 95bb7829b4..2c4284ecd7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,20 @@ ## [Unreleased](https://github.com/CosmWasm/wasmd/tree/HEAD) -[Full Changelog](https://github.com/CosmWasm/wasmd/compare/v0.24.0...master) +[Full Changelog](https://github.com/CosmWasm/wasmd/compare/v0.25.0...master) + +## [v0.25.0](https://github.com/CosmWasm/wasmd/tree/v0.25.0) (2022-04-06) + +**API Breaking** +- Upgrade wasmvm to v1.0.0-beta10 [\#790](https://github.com/CosmWasm/wasmd/pull/790), [\#800](https://github.com/CosmWasm/wasmd/pull/800) + +**Implemented Enhancements** +- Fix: close iterators [\#792](https://github.com/CosmWasm/wasmd/pull/792) +- Use callback pattern for contract state iterator [\#794](https://github.com/CosmWasm/wasmd/pull/794) +- Bump github.com/stretchr/testify from 1.7.0 to 1.7.1 [\#787](https://github.com/CosmWasm/wasmd/pull/787) +- Bump github.com/cosmos/ibc-go/v2 from 2.0.3 to 2.2.0 [\#786](https://github.com/CosmWasm/wasmd/pull/786) + +[Full Changelog](https://github.com/CosmWasm/wasmd/compare/v0.24.0...v0.25.0) ## [v0.24.0](https://github.com/CosmWasm/wasmd/tree/v0.24.0) (2022-03-09) diff --git a/Dockerfile b/Dockerfile index 2ddbaab817..c528d432f0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,7 @@ # docker build . -t cosmwasm/wasmd:latest # docker run --rm -it cosmwasm/wasmd:latest /bin/sh FROM golang:1.17-alpine3.15 AS go-builder +ARG arch=x86_64 # this comes from standard alpine nightly file # https://github.com/rust-lang/docker-rust-nightly/blob/master/alpine3.12/Dockerfile @@ -15,11 +16,18 @@ WORKDIR /code COPY . /code/ # See https://github.com/CosmWasm/wasmvm/releases -ADD https://github.com/CosmWasm/wasmvm/releases/download/v1.0.0-beta7/libwasmvm_muslc.a /lib/libwasmvm_muslc.a -RUN sha256sum /lib/libwasmvm_muslc.a | grep d0152067a5609bfdfb3f0d5d6c0f2760f79d5f2cd7fd8513cafa9932d22eb350 +ADD https://github.com/CosmWasm/wasmvm/releases/download/v1.0.0-beta10/libwasmvm_muslc.aarch64.a /lib/libwasmvm_muslc.aarch64.a +ADD https://github.com/CosmWasm/wasmvm/releases/download/v1.0.0-beta10/libwasmvm_muslc.x86_64.a /lib/libwasmvm_muslc.x86_64.a +RUN sha256sum /lib/libwasmvm_muslc.aarch64.a | grep 5b7abfdd307568f5339e2bea1523a6aa767cf57d6a8c72bc813476d790918e44 +RUN sha256sum /lib/libwasmvm_muslc.x86_64.a | grep 2f44efa9c6c1cda138bd1f46d8d53c5ebfe1f4a53cf3457b01db86472c4917ac + +# Copy the library you want to the final location that will be found by the linker flag `-lwasmvm_muslc` +RUN cp /lib/libwasmvm_muslc.${arch}.a /lib/libwasmvm_muslc.a # force it to use static lib (from above) not standard libgo_cosmwasm.so file -RUN LEDGER_ENABLED=false BUILD_TAGS=muslc make build +RUN LEDGER_ENABLED=false BUILD_TAGS=muslc LINK_STATICALLY=true make build +RUN echo "Ensuring binary is statically linked ..." \ + && (file /code/build/wasmd | grep "statically linked") # -------------------------------------------------------- FROM alpine:3.15 diff --git a/Makefile b/Makefile index e11ea1b456..b9433d972b 100644 --- a/Makefile +++ b/Makefile @@ -65,6 +65,9 @@ ldflags = -X github.com/cosmos/cosmos-sdk/version.Name=wasm \ ifeq ($(WITH_CLEVELDB),yes) ldflags += -X github.com/cosmos/cosmos-sdk/types.DBBackend=cleveldb endif +ifeq ($(LINK_STATICALLY),true) + ldflags += -linkmode=external -extldflags "-Wl,-z,muldefs -static" +endif ldflags += $(LDFLAGS) ldflags := $(strip $(ldflags)) diff --git a/SECURITY.md b/SECURITY.md index 4ad89c7c0c..021b58b510 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,39 +1,13 @@ # Security Policy -## Reporting a Vulnerability - -Please report any security issues via email to security@confio.gmbh. - -You will receive a response from us within 2 working days. If the issue is confirmed, we will release a patch as soon as possible depending on complexity but historically within a few days. - -Please avoid opening public issues on GitHub that contain information about a potential security vulnerability as this makes it difficult to reduce the impact and harm of valid security issues. +This repository is maintained by Confio as part of the CosmWasm stack. +Please see https://github.com/CosmWasm/advisories/blob/main/SECURITY.md +for our security policy. ## Supported Versions This is alpha software, do not run on a production system. Notably, we currently provide no migration path not even "dump state and restart" to move to future versions. -We will have a stable v0.x version before the final v1.0.0 version with the same API as the v1.0 version in order to run last testnets and manual testing on it. We have not yet committed to that version number. wasmd 0.22 will support Cosmos SDK 0.44/0.45 and should be quite close to a final API, minus some minor details. +We will have a stable v0.x version before the final v1.0.0 version with the same API as the v1.0 version in order to run last testnets and manual testing on it. We have not yet committed to that version number. wasmd 0.25 will support Cosmos SDK 0.44/0.45 and should be quite close to a final API, minus some minor details. Our v1.0.0 release plans were also delayed by upstream release cycles, and we have continued to refine APIs while we can. - -## Coordinated Vulnerability Disclosure Policy - -We ask security researchers to keep vulnerabilities and communications around vulnerability submissions private and confidential until a patch is developed. In addition to this, we ask that you: - - - Allow us a reasonable amount of time to correct or address security vulnerabilities. - - Avoid exploiting any vulnerabilities that you discover. - - Demonstrate good faith by not disrupting or degrading services built on top of this software. - -## Vulnerability Disclosure Process - -Confio uses the following disclosure process for the various CosmWasm-related repos: - - - Once a security report is received, the core development team works to verify the issue. - - Patches are prepared for eligible releases in private repositories. - - We notify the community that a security release is coming, to give users time to prepare their systems for the update. Notifications can include Discord messages, tweets, and emails to partners and validators. Please also see [CosmWasm/advisories](https://github.com/CosmWasm/advisories) if you want to receive notifications. - - No less than 24 hours following this notification, the fixes are applied publicly and new releases are issued. - - Once releases are available, we notify the community, again, through the same channels as above. - - Once the patches have been properly rolled out, we will publish a post with further details on the vulnerability as well as our response to it. - - Note that we are working on a concept for bug bounties and they are not currently available. - - This process can take some time. Every effort will be made to handle the bug as quickly and thoroughly as possible. However, it's important that we follow the process described above to ensure that disclosures are handled consistently and to keep this codebase and the projects that depend on them secure. \ No newline at end of file diff --git a/go.mod b/go.mod index cee0343345..8a47a86431 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/CosmWasm/wasmd go 1.17 require ( - github.com/CosmWasm/wasmvm v1.0.0-beta7 + github.com/CosmWasm/wasmvm v1.0.0-beta10 github.com/cosmos/cosmos-sdk v0.45.1 github.com/cosmos/iavl v0.17.3 github.com/cosmos/ibc-go/v3 v3.0.0 diff --git a/go.sum b/go.sum index 0d78c7c4c6..01ce45b6e3 100644 --- a/go.sum +++ b/go.sum @@ -70,8 +70,8 @@ github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbi github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d h1:nalkkPQcITbvhmL4+C4cKA87NW0tfm3Kl9VXRoPywFg= github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d/go.mod h1:URdX5+vg25ts3aCh8H5IFZybJYKWhJHYMTnf+ULtoC4= -github.com/CosmWasm/wasmvm v1.0.0-beta7 h1:hCa6P8tUTh8viabzeXfede5iPlopSav9Guh+hupXjvU= -github.com/CosmWasm/wasmvm v1.0.0-beta7/go.mod h1:y+yd9piV8KlrB7ISRZz1sDwH4UVm4Q9rEX9501dBNog= +github.com/CosmWasm/wasmvm v1.0.0-beta10 h1:N99+PRcrh4FeDP1xQuJGaAsr+7U+TZAHKG8mybnAsKU= +github.com/CosmWasm/wasmvm v1.0.0-beta10/go.mod h1:y+yd9piV8KlrB7ISRZz1sDwH4UVm4Q9rEX9501dBNog= github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= diff --git a/x/wasm/Governance.md b/x/wasm/Governance.md index f26c0c5a5a..7d7a32abee 100644 --- a/x/wasm/Governance.md +++ b/x/wasm/Governance.md @@ -57,18 +57,110 @@ See [params.go](https://github.com/CosmWasm/wasmd/blob/master/x/wasm/types/param }, "instantiate_default_permission": "Everybody" } - }, + }, ``` The values can be updated via gov proposal implemented in the `params` module. +### Update Params Via [ParamChangeProposal](https://github.com/cosmos/cosmos-sdk/blob/v0.45.3/proto/cosmos/params/v1beta1/params.proto#L10) +Example to submit a parameter change gov proposal: +```sh +wasmd tx gov submit-proposal param-change --from validator --chain-id=testing -b block +``` +#### Content examples +* Disable wasm code uploads +```json +{ + "title": "Foo", + "description": "Bar", + "changes": [ + { + "subspace": "wasm", + "key": "uploadAccess", + "value": { + "permission": "Nobody" + } + } + ], + "deposit": "" +} +``` +* Allow wasm code uploads for everybody +```json +{ + "title": "Foo", + "description": "Bar", + "changes": [ + { + "subspace": "wasm", + "key": "uploadAccess", + "value": { + "permission": "Everybody" + } + } + ], + "deposit": "" +} +``` + +* Restrict code uploads to a single address +```json +{ + "title": "Foo", + "description": "Bar", + "changes": [ + { + "subspace": "wasm", + "key": "uploadAccess", + "value": { + "permission": "OnlyAddress", + "address": "cosmos1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq0fr2sh" + } + } + ], + "deposit": "" +} +``` +* Set chain **default** instantiation settings to nobody +```json +{ + "title": "Foo", + "description": "Bar", + "changes": [ + { + "subspace": "wasm", + "key": "instantiateAccess", + "value": "Nobody" + } + ], + "deposit": "" +} +``` +* Set chain **default** instantiation settings to everybody +```json +{ + "title": "Foo", + "description": "Bar", + "changes": [ + { + "subspace": "wasm", + "key": "instantiateAccess", + "value": "Everybody" + } + ], + "deposit": "" +} +``` + ### Enable gov proposals at **compile time**. -As gov proposals bypass the existing authorzation policy they are diabled and require to be enabled at compile time. +As gov proposals bypass the existing authorization policy they are disabled and require to be enabled at compile time. ``` -X github.com/CosmWasm/wasmd/app.ProposalsEnabled=true - enable all x/wasm governance proposals (default false) -X github.com/CosmWasm/wasmd/app.EnableSpecificProposals=MigrateContract,UpdateAdmin,ClearAdmin - enable a subset of the x/wasm governance proposal types (overrides ProposalsEnabled) ``` +The `ParamChangeProposal` is always enabled. + ### Tests * [params validation unit tests](https://github.com/CosmWasm/wasmd/blob/master/x/wasm/types/params_test.go) * [genesis validation tests](https://github.com/CosmWasm/wasmd/blob/master/x/wasm/types/genesis_test.go) diff --git a/x/wasm/keeper/events_test.go b/x/wasm/keeper/events_test.go index 8c59516343..e10074a0c2 100644 --- a/x/wasm/keeper/events_test.go +++ b/x/wasm/keeper/events_test.go @@ -183,6 +183,13 @@ func TestNewCustomEvents(t *testing.T) { sdk.NewAttribute("_contract_address", myContract.String()), sdk.NewAttribute("my Key", "myVal"))}, }, + "empty event elements": { + src: make(wasmvmtypes.Events, 10), + isError: true, + }, + "nil": { + exp: sdk.Events{}, + }, } for name, spec := range specs { t.Run(name, func(t *testing.T) { @@ -240,6 +247,15 @@ func TestNewWasmModuleEvent(t *testing.T) { sdk.NewAttribute("_contract_address", myContract.String()), sdk.NewAttribute("my-real-key", "some-val"))}, }, + "empty elements": { + src: make([]wasmvmtypes.EventAttribute, 10), + isError: true, + }, + "nil": { + exp: sdk.Events{sdk.NewEvent("wasm", + sdk.NewAttribute("_contract_address", myContract.String()), + )}, + }, } for name, spec := range specs { t.Run(name, func(t *testing.T) { diff --git a/x/wasm/keeper/gas_register_test.go b/x/wasm/keeper/gas_register_test.go index 1ecabc5fba..b3ca5b9c86 100644 --- a/x/wasm/keeper/gas_register_test.go +++ b/x/wasm/keeper/gas_register_test.go @@ -283,6 +283,26 @@ func TestReplyCost(t *testing.T) { srcConfig: DefaultGasRegisterConfig(), exp: sdk.Gas(DefaultInstanceCost + 3*DefaultContractMessageDataCost), }, + "subcall response with empty events": { + src: wasmvmtypes.Reply{ + Result: wasmvmtypes.SubcallResult{ + Ok: &wasmvmtypes.SubcallResponse{ + Events: make([]wasmvmtypes.Event, 10), + }, + }, + }, + srcConfig: DefaultGasRegisterConfig(), + exp: DefaultInstanceCost, + }, + "subcall response with events unset": { + src: wasmvmtypes.Reply{ + Result: wasmvmtypes.SubcallResult{ + Ok: &wasmvmtypes.SubcallResponse{}, + }, + }, + srcConfig: DefaultGasRegisterConfig(), + exp: DefaultInstanceCost, + }, } for name, spec := range specs { t.Run(name, func(t *testing.T) { @@ -298,6 +318,33 @@ func TestReplyCost(t *testing.T) { } } +func TestEventCosts(t *testing.T) { + // most cases are covered in TestReplyCost already. This ensures some edge cases + specs := map[string]struct { + srcAttrs []wasmvmtypes.EventAttribute + srcEvents wasmvmtypes.Events + expGas sdk.Gas + }{ + "empty events": { + srcEvents: make([]wasmvmtypes.Event, 1), + expGas: DefaultPerCustomEventCost, + }, + "empty attributes": { + srcAttrs: make([]wasmvmtypes.EventAttribute, 1), + expGas: DefaultPerAttributeCost, + }, + "both nil": { + expGas: 0, + }, + } + for name, spec := range specs { + t.Run(name, func(t *testing.T) { + gotGas := NewDefaultWasmGasRegister().EventCosts(spec.srcAttrs, spec.srcEvents) + assert.Equal(t, spec.expGas, gotGas) + }) + } +} + func TestToWasmVMGasConversion(t *testing.T) { specs := map[string]struct { src storetypes.Gas diff --git a/x/wasm/keeper/genesis.go b/x/wasm/keeper/genesis.go index 15978a9170..7fa5280b9e 100644 --- a/x/wasm/keeper/genesis.go +++ b/x/wasm/keeper/genesis.go @@ -102,15 +102,11 @@ func ExportGenesis(ctx sdk.Context, keeper *Keeper) *types.GenesisState { }) keeper.IterateContractInfo(ctx, func(addr sdk.AccAddress, contract types.ContractInfo) bool { - contractStateIterator := keeper.GetContractState(ctx, addr) var state []types.Model - for ; contractStateIterator.Valid(); contractStateIterator.Next() { - m := types.Model{ - Key: contractStateIterator.Key(), - Value: contractStateIterator.Value(), - } - state = append(state, m) - } + keeper.IterateContractState(ctx, addr, func(key, value []byte) bool { + state = append(state, types.Model{Key: key, Value: value}) + return false + }) // redact contract info contract.Created = nil genState.Contracts = append(genState.Contracts, types.Contract{ @@ -118,7 +114,6 @@ func ExportGenesis(ctx sdk.Context, keeper *Keeper) *types.GenesisState { ContractInfo: contract, ContractState: state, }) - return false }) diff --git a/x/wasm/keeper/genesis_test.go b/x/wasm/keeper/genesis_test.go index b1850e044d..9711741d3b 100644 --- a/x/wasm/keeper/genesis_test.go +++ b/x/wasm/keeper/genesis_test.go @@ -113,7 +113,9 @@ func TestGenesisExportImport(t *testing.T) { wasmKeeper.IterateContractInfo(srcCtx, func(address sdk.AccAddress, info wasmTypes.ContractInfo) bool { wasmKeeper.removeFromContractCodeSecondaryIndex(srcCtx, address, wasmKeeper.getLastContractHistoryEntry(srcCtx, address)) prefixStore := prefix.NewStore(srcCtx.KVStore(wasmKeeper.storeKey), types.GetContractCodeHistoryElementPrefix(address)) - for iter := prefixStore.Iterator(nil, nil); iter.Valid(); iter.Next() { + iter := prefixStore.Iterator(nil, nil) + + for ; iter.Valid(); iter.Next() { prefixStore.Delete(iter.Key()) } x := &info @@ -121,6 +123,7 @@ func TestGenesisExportImport(t *testing.T) { wasmKeeper.storeContractInfo(srcCtx, address, x) wasmKeeper.addToContractCodeSecondaryIndex(srcCtx, address, newHistory) wasmKeeper.appendToContractHistory(srcCtx, address, newHistory) + iter.Close() return false }) @@ -145,6 +148,8 @@ func TestGenesisExportImport(t *testing.T) { if !assert.False(t, dstIT.Valid()) { t.Fatalf("dest Iterator still has key :%X", dstIT.Key()) } + srcIT.Close() + dstIT.Close() } } diff --git a/x/wasm/keeper/keeper.go b/x/wasm/keeper/keeper.go index b56faa1638..5e6fe1b8b2 100644 --- a/x/wasm/keeper/keeper.go +++ b/x/wasm/keeper/keeper.go @@ -524,7 +524,10 @@ func (k Keeper) removeFromContractCodeSecondaryIndex(ctx sdk.Context, contractAd // IterateContractsByCode iterates over all contracts with given codeID ASC on code update time. func (k Keeper) IterateContractsByCode(ctx sdk.Context, codeID uint64, cb func(address sdk.AccAddress) bool) { prefixStore := prefix.NewStore(ctx.KVStore(k.storeKey), types.GetContractByCodeIDSecondaryIndexPrefix(codeID)) - for iter := prefixStore.Iterator(nil, nil); iter.Valid(); iter.Next() { + iter := prefixStore.Iterator(nil, nil) + defer iter.Close() + + for ; iter.Valid(); iter.Next() { key := iter.Key() if cb(key[types.AbsoluteTxPositionLen:]) { return @@ -550,7 +553,10 @@ func (k Keeper) appendToContractHistory(ctx sdk.Context, contractAddr sdk.AccAdd // find last element position var pos uint64 prefixStore := prefix.NewStore(store, types.GetContractCodeHistoryElementPrefix(contractAddr)) - if iter := prefixStore.ReverseIterator(nil, nil); iter.Valid() { + iter := prefixStore.ReverseIterator(nil, nil) + defer iter.Close() + + if iter.Valid() { pos = sdk.BigEndianToUint64(iter.Value()) } // then store with incrementing position @@ -565,6 +571,8 @@ func (k Keeper) GetContractHistory(ctx sdk.Context, contractAddr sdk.AccAddress) prefixStore := prefix.NewStore(ctx.KVStore(k.storeKey), types.GetContractCodeHistoryElementPrefix(contractAddr)) r := make([]types.ContractCodeHistoryEntry, 0) iter := prefixStore.Iterator(nil, nil) + defer iter.Close() + for ; iter.Valid(); iter.Next() { var e types.ContractCodeHistoryEntry k.cdc.MustUnmarshal(iter.Value(), &e) @@ -577,6 +585,8 @@ func (k Keeper) GetContractHistory(ctx sdk.Context, contractAddr sdk.AccAddress) func (k Keeper) getLastContractHistoryEntry(ctx sdk.Context, contractAddr sdk.AccAddress) types.ContractCodeHistoryEntry { prefixStore := prefix.NewStore(ctx.KVStore(k.storeKey), types.GetContractCodeHistoryElementPrefix(contractAddr)) iter := prefixStore.ReverseIterator(nil, nil) + defer iter.Close() + var r types.ContractCodeHistoryEntry if !iter.Valid() { // all contracts have a history @@ -666,6 +676,8 @@ func (k Keeper) storeContractInfo(ctx sdk.Context, contractAddress sdk.AccAddres func (k Keeper) IterateContractInfo(ctx sdk.Context, cb func(sdk.AccAddress, types.ContractInfo) bool) { prefixStore := prefix.NewStore(ctx.KVStore(k.storeKey), types.ContractKeyPrefix) iter := prefixStore.Iterator(nil, nil) + defer iter.Close() + for ; iter.Valid(); iter.Next() { var contract types.ContractInfo k.cdc.MustUnmarshal(iter.Value(), &contract) @@ -676,10 +688,19 @@ func (k Keeper) IterateContractInfo(ctx sdk.Context, cb func(sdk.AccAddress, typ } } -func (k Keeper) GetContractState(ctx sdk.Context, contractAddress sdk.AccAddress) sdk.Iterator { +// IterateContractState iterates through all elements of the key value store for the given contract address and passes +// them to the provided callback function. The callback method can return true to abort early. +func (k Keeper) IterateContractState(ctx sdk.Context, contractAddress sdk.AccAddress, cb func(key, value []byte) bool) { prefixStoreKey := types.GetContractStorePrefix(contractAddress) prefixStore := prefix.NewStore(ctx.KVStore(k.storeKey), prefixStoreKey) - return prefixStore.Iterator(nil, nil) + iter := prefixStore.Iterator(nil, nil) + defer iter.Close() + + for ; iter.Valid(); iter.Next() { + if cb(iter.Key(), iter.Value()) { + break + } + } } func (k Keeper) importContractState(ctx sdk.Context, contractAddress sdk.AccAddress, models []types.Model) error { @@ -716,6 +737,8 @@ func (k Keeper) containsCodeInfo(ctx sdk.Context, codeID uint64) bool { func (k Keeper) IterateCodeInfos(ctx sdk.Context, cb func(uint64, types.CodeInfo) bool) { prefixStore := prefix.NewStore(ctx.KVStore(k.storeKey), types.CodeKeyPrefix) iter := prefixStore.Iterator(nil, nil) + defer iter.Close() + for ; iter.Valid(); iter.Next() { var c types.CodeInfo k.cdc.MustUnmarshal(iter.Value(), &c) @@ -788,6 +811,8 @@ func (k Keeper) IsPinnedCode(ctx sdk.Context, codeID uint64) bool { func (k Keeper) InitializePinnedCodes(ctx sdk.Context) error { store := prefix.NewStore(ctx.KVStore(k.storeKey), types.PinnedCodeIndexPrefix) iter := store.Iterator(nil, nil) + defer iter.Close() + for ; iter.Valid(); iter.Next() { codeInfo := k.GetCodeInfo(ctx, types.ParsePinnedCodeIndex(iter.Key())) if codeInfo == nil { @@ -959,7 +984,7 @@ func moduleLogger(ctx sdk.Context) log.Logger { } // Querier creates a new grpc querier instance -func Querier(k *Keeper) *grpcQuerier { +func Querier(k *Keeper) *grpcQuerier { //nolint:revive return NewGrpcQuerier(k.cdc, k.storeKey, k, k.queryGasLimit) } diff --git a/x/wasm/keeper/keeper_test.go b/x/wasm/keeper/keeper_test.go index eeddfe8f38..1951e22849 100644 --- a/x/wasm/keeper/keeper_test.go +++ b/x/wasm/keeper/keeper_test.go @@ -313,7 +313,7 @@ func TestInstantiate(t *testing.T) { gasAfter := ctx.GasMeter().GasConsumed() if types.EnableGasVerification { - require.Equal(t, uint64(0x18dab), gasAfter-gasBefore) + require.Equal(t, uint64(0x18db5), gasAfter-gasBefore) } // ensure it is stored properly diff --git a/x/wasm/keeper/legacy_querier.go b/x/wasm/keeper/legacy_querier.go index 768c3def4d..f12e8b0ebc 100644 --- a/x/wasm/keeper/legacy_querier.go +++ b/x/wasm/keeper/legacy_querier.go @@ -93,12 +93,10 @@ func queryContractState(ctx sdk.Context, bech, queryMethod string, data []byte, case QueryMethodContractStateAll: resultData := make([]types.Model, 0) // this returns a serialized json object (which internally encoded binary fields properly) - for iter := keeper.GetContractState(ctx, contractAddr); iter.Valid(); iter.Next() { - resultData = append(resultData, types.Model{ - Key: iter.Key(), - Value: iter.Value(), - }) - } + keeper.IterateContractState(ctx, contractAddr, func(key, value []byte) bool { + resultData = append(resultData, types.Model{Key: key, Value: value}) + return false + }) bz, err := json.Marshal(resultData) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) diff --git a/x/wasm/keeper/proposal_integration_test.go b/x/wasm/keeper/proposal_integration_test.go index a943003e55..f01dc6ba6f 100644 --- a/x/wasm/keeper/proposal_integration_test.go +++ b/x/wasm/keeper/proposal_integration_test.go @@ -8,6 +8,8 @@ import ( "io/ioutil" "testing" + "github.com/cosmos/cosmos-sdk/x/params/client/utils" + wasmvm "github.com/CosmWasm/wasmvm" "github.com/CosmWasm/wasmd/x/wasm/keeper/wasmtesting" @@ -410,13 +412,11 @@ func TestUpdateParamsProposal(t *testing.T) { govKeeper, wasmKeeper := keepers.GovKeeper, keepers.WasmKeeper var ( - cdc = keepers.WasmKeeper.cdc + legacyAmino = keepers.EncodingConfig.Amino myAddress sdk.AccAddress = make([]byte, types.ContractAddrLen) oneAddressAccessConfig = types.AccessTypeOnlyAddress.With(myAddress) ) - nobodyJson, err := json.Marshal(types.AccessTypeNobody) - require.NoError(t, err) specs := map[string]struct { src proposal.ParamChange expUploadConfig types.AccessConfig @@ -426,16 +426,25 @@ func TestUpdateParamsProposal(t *testing.T) { src: proposal.ParamChange{ Subspace: types.ModuleName, Key: string(types.ParamStoreKeyUploadAccess), - Value: string(cdc.MustMarshalJSON(&types.AllowNobody)), + Value: string(legacyAmino.MustMarshalJSON(&types.AllowNobody)), }, expUploadConfig: types.AllowNobody, expInstantiateType: types.AccessTypeEverybody, }, + "update upload permission with same as current value": { + src: proposal.ParamChange{ + Subspace: types.ModuleName, + Key: string(types.ParamStoreKeyUploadAccess), + Value: string(legacyAmino.MustMarshalJSON(&types.AllowEverybody)), + }, + expUploadConfig: types.AllowEverybody, + expInstantiateType: types.AccessTypeEverybody, + }, "update upload permission param with address": { src: proposal.ParamChange{ Subspace: types.ModuleName, Key: string(types.ParamStoreKeyUploadAccess), - Value: string(cdc.MustMarshalJSON(&oneAddressAccessConfig)), + Value: string(legacyAmino.MustMarshalJSON(&oneAddressAccessConfig)), }, expUploadConfig: oneAddressAccessConfig, expInstantiateType: types.AccessTypeEverybody, @@ -444,22 +453,40 @@ func TestUpdateParamsProposal(t *testing.T) { src: proposal.ParamChange{ Subspace: types.ModuleName, Key: string(types.ParamStoreKeyInstantiateAccess), - Value: string(nobodyJson), + Value: string(legacyAmino.MustMarshalJSON(types.AccessTypeNobody)), }, expUploadConfig: types.AllowEverybody, expInstantiateType: types.AccessTypeNobody, }, + "update instantiate param as default": { + src: proposal.ParamChange{ + Subspace: types.ModuleName, + Key: string(types.ParamStoreKeyInstantiateAccess), + Value: string(legacyAmino.MustMarshalJSON(types.AccessTypeEverybody)), + }, + expUploadConfig: types.AllowEverybody, + expInstantiateType: types.AccessTypeEverybody, + }, } for msg, spec := range specs { t.Run(msg, func(t *testing.T) { wasmKeeper.SetParams(ctx, types.DefaultParams()) - proposal := proposal.ParameterChangeProposal{ + // encode + decode as CLI to play nice with amino + bz := legacyAmino.MustMarshalJSON(&utils.ParamChangeProposalJSON{ Title: "Foo", Description: "Bar", - Changes: []proposal.ParamChange{spec.src}, - } + Changes: []utils.ParamChangeJSON{{Subspace: spec.src.Subspace, Key: spec.src.Key, Value: json.RawMessage(spec.src.Value)}}, + }) + t.Log(string(bz)) + var jsonProposal utils.ParamChangeProposalJSON + require.NoError(t, legacyAmino.UnmarshalJSON(bz, &jsonProposal)) + proposal := proposal.ParameterChangeProposal{ + Title: jsonProposal.Title, + Description: jsonProposal.Description, + Changes: jsonProposal.Changes.ToParamChanges(), + } // when stored storedProposal, err := govKeeper.SubmitProposal(ctx, &proposal) require.NoError(t, err) @@ -564,6 +591,7 @@ func TestPinCodesProposal(t *testing.T) { }) } } + func TestUnpinCodesProposal(t *testing.T) { ctx, keepers := CreateTestInput(t, false, "staking") govKeeper, wasmKeeper := keepers.GovKeeper, keepers.WasmKeeper diff --git a/x/wasm/types/exported_keepers.go b/x/wasm/types/exported_keepers.go index 525d219404..515b946fa8 100644 --- a/x/wasm/types/exported_keepers.go +++ b/x/wasm/types/exported_keepers.go @@ -15,7 +15,7 @@ type ViewKeeper interface { GetContractInfo(ctx sdk.Context, contractAddress sdk.AccAddress) *ContractInfo IterateContractInfo(ctx sdk.Context, cb func(sdk.AccAddress, ContractInfo) bool) IterateContractsByCode(ctx sdk.Context, codeID uint64, cb func(address sdk.AccAddress) bool) - GetContractState(ctx sdk.Context, contractAddress sdk.AccAddress) sdk.Iterator + IterateContractState(ctx sdk.Context, contractAddress sdk.AccAddress, cb func(key, value []byte) bool) GetCodeInfo(ctx sdk.Context, codeID uint64) *CodeInfo IterateCodeInfos(ctx sdk.Context, cb func(uint64, CodeInfo) bool) GetByteCode(ctx sdk.Context, codeID uint64) ([]byte, error) diff --git a/x/wasm/types/genesis.go b/x/wasm/types/genesis.go index 98df6fd6ad..ba973c6f02 100644 --- a/x/wasm/types/genesis.go +++ b/x/wasm/types/genesis.go @@ -1,6 +1,7 @@ package types import ( + codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) @@ -98,3 +99,22 @@ func (m GenesisState_GenMsgs) ValidateBasic() error { func ValidateGenesis(data GenesisState) error { return data.ValidateBasic() } + +var _ codectypes.UnpackInterfacesMessage = GenesisState{} + +// UnpackInterfaces implements codectypes.UnpackInterfaces +func (s GenesisState) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + for _, v := range s.Contracts { + if err := v.UnpackInterfaces(unpacker); err != nil { + return err + } + } + return nil +} + +var _ codectypes.UnpackInterfacesMessage = &Contract{} + +// UnpackInterfaces implements codectypes.UnpackInterfaces +func (c *Contract) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + return c.ContractInfo.UnpackInterfaces(unpacker) +} diff --git a/x/wasm/types/genesis_test.go b/x/wasm/types/genesis_test.go index d8da55c56e..208ee7e2e0 100644 --- a/x/wasm/types/genesis_test.go +++ b/x/wasm/types/genesis_test.go @@ -3,6 +3,14 @@ package types import ( "bytes" "testing" + "time" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + "github.com/stretchr/testify/assert" + "github.com/tendermint/tendermint/libs/rand" "github.com/stretchr/testify/require" ) @@ -170,3 +178,52 @@ func TestContractValidateBasic(t *testing.T) { }) } } + +func TestGenesisContractInfoMarshalUnmarshal(t *testing.T) { + var myAddr sdk.AccAddress = rand.Bytes(ContractAddrLen) + var myOtherAddr sdk.AccAddress = rand.Bytes(ContractAddrLen) + var anyPos = AbsoluteTxPosition{BlockHeight: 1, TxIndex: 2} + + anyTime := time.Now().UTC() + // using gov proposal here as a random protobuf types as it contains an Any type inside for nested unpacking + myExtension, err := govtypes.NewProposal(&govtypes.TextProposal{Title: "bar"}, 1, anyTime, anyTime) + require.NoError(t, err) + myExtension.TotalDeposit = nil + + src := NewContractInfo(1, myAddr, myOtherAddr, "bar", &anyPos) + err = src.SetExtension(&myExtension) + require.NoError(t, err) + + interfaceRegistry := types.NewInterfaceRegistry() + marshaler := codec.NewProtoCodec(interfaceRegistry) + RegisterInterfaces(interfaceRegistry) + // register proposal as extension type + interfaceRegistry.RegisterImplementations( + (*ContractInfoExtension)(nil), + &govtypes.Proposal{}, + ) + // register gov types for nested Anys + govtypes.RegisterInterfaces(interfaceRegistry) + + // when encode + gs := GenesisState{ + Contracts: []Contract{{ + ContractInfo: src, + }}, + } + + bz, err := marshaler.Marshal(&gs) + require.NoError(t, err) + // and decode + var destGs GenesisState + err = marshaler.Unmarshal(bz, &destGs) + require.NoError(t, err) + // then + require.Len(t, destGs.Contracts, 1) + dest := destGs.Contracts[0].ContractInfo + assert.Equal(t, src, dest) + // and sanity check nested any + var destExt govtypes.Proposal + require.NoError(t, dest.ReadExtension(&destExt)) + assert.Equal(t, destExt.GetTitle(), "bar") +} diff --git a/x/wasm/types/iavl_range_test.go b/x/wasm/types/iavl_range_test.go index aedfaea4f3..1e5bdc92ab 100644 --- a/x/wasm/types/iavl_range_test.go +++ b/x/wasm/types/iavl_range_test.go @@ -63,6 +63,7 @@ func TestIavlRangeBounds(t *testing.T) { } items := consume(iter) require.Equal(t, tc.expected, items) + iter.Close() }) } } @@ -73,8 +74,6 @@ type KV struct { } func consume(itr store.Iterator) []KV { - defer itr.Close() - var res []KV for ; itr.Valid(); itr.Next() { k, v := itr.Key(), itr.Value()