Merge pull request #179 from CosmWasm/gov_restrict_upload

Restrict code upload
This commit is contained in:
Alexander Peters 2020-07-09 10:53:13 +02:00 committed by GitHub
commit f5d4622266
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 521 additions and 120 deletions

View File

@ -37,6 +37,8 @@ Ref: https://keepachangelog.com/en/1.0.0/
## [Unreleased]
### Features
* (wasmd) [\#164](https://github.com/CosmWasm/wasmd/issues/164) Control who can upload code
* (wasmd) [\#173](https://github.com/CosmWasm/wasmd/issues/173) Gov proposal types and handler
* (wasmd) [\#122](https://github.com/CosmWasm/wasmd/pull/122]) Migrate contract backend functionality with go-cosmwasm stub impl
* (wasmd)[\#2](https://github.com/cosmwasm/wasmd/pull/22) Improve wasm contract queries (all, raw, smart)
* (wasmd) [\#119](https://github.com/cosmwasm/wasmd/pull/119) Add support for the `--inter-block-cache` CLI

View File

@ -179,6 +179,7 @@ func NewWasmApp(
app.subspaces[gov.ModuleName] = app.paramsKeeper.Subspace(gov.DefaultParamspace).WithKeyTable(gov.ParamKeyTable())
app.subspaces[crisis.ModuleName] = app.paramsKeeper.Subspace(crisis.DefaultParamspace)
app.subspaces[evidence.ModuleName] = app.paramsKeeper.Subspace(evidence.DefaultParamspace)
app.subspaces[wasm.ModuleName] = app.paramsKeeper.Subspace(wasm.DefaultParamspace)
// add keepers
app.accountKeeper = auth.NewAccountKeeper(
@ -249,7 +250,7 @@ func NewWasmApp(
// The last arguments can contain custom message handlers, and custom query handlers,
// if we want to allow any custom callbacks
supportedFeatures := "staking"
app.wasmKeeper = wasm.NewKeeper(app.cdc, keys[wasm.StoreKey], app.accountKeeper, app.bankKeeper, app.stakingKeeper, wasmRouter, wasmDir, wasmConfig, supportedFeatures, nil, nil)
app.wasmKeeper = wasm.NewKeeper(app.cdc, keys[wasm.StoreKey], app.subspaces[wasm.ModuleName], app.accountKeeper, app.bankKeeper, app.stakingKeeper, wasmRouter, wasmDir, wasmConfig, supportedFeatures, nil, nil)
if len(wasm.DefaultEnabledProposals) != 0 {
govRouter.AddRoute(wasm.RouterKey, wasm.NewWasmProposalHandler(app.wasmKeeper, wasm.DefaultEnabledProposals))

View File

@ -4,6 +4,7 @@ import (
"os"
"testing"
"github.com/CosmWasm/wasmd/x/wasm"
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/libs/log"
db "github.com/tendermint/tm-db"
@ -38,6 +39,7 @@ func TestBlackListedAddrs(t *testing.T) {
func setGenesis(gapp *WasmApp) error {
genesisState := simapp.NewDefaultGenesisState()
genesisState[wasm.ModuleName] = wasm.AppModuleBasic{}.DefaultGenesis()
stateBytes, err := codec.MarshalJSONIndent(gapp.Codec(), genesisState)
if err != nil {
return err

1
go.mod
View File

@ -28,6 +28,7 @@ require (
go.etcd.io/bbolt v1.3.4 // indirect
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 // indirect
golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980 // indirect
gopkg.in/yaml.v2 v2.2.8
)
replace github.com/keybase/go-keychain => github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4

View File

@ -11,6 +11,7 @@ import (
)
const (
DefaultParamspace = types.DefaultParamspace
ModuleName = types.ModuleName
StoreKey = types.StoreKey
TStoreKey = types.TStoreKey
@ -48,6 +49,7 @@ var (
NewWasmCoins = types.NewWasmCoins
ParseEvents = types.ParseEvents
DefaultWasmConfig = types.DefaultWasmConfig
DefaultParams = types.DefaultParams
InitGenesis = keeper.InitGenesis
ExportGenesis = keeper.ExportGenesis
NewMessageHandler = keeper.NewMessageHandler

View File

@ -75,7 +75,7 @@ func handleStoreCode(ctx sdk.Context, k Keeper, msg *MsgStoreCode) (*sdk.Result,
return nil, err
}
codeID, err := k.Create(ctx, msg.Sender, msg.WASMByteCode, msg.Source, msg.Builder)
codeID, err := k.Create(ctx, msg.Sender, msg.WASMByteCode, msg.Source, msg.Builder, msg.InstantiatePermission)
if err != nil {
return nil, err
}

View File

@ -46,6 +46,7 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) error
if keeper.peekAutoIncrementID(ctx, types.KeyLastInstanceID) <= uint64(maxContractID) {
return sdkerrors.Wrapf(types.ErrInvalid, "seq %s must be greater %d ", string(types.KeyLastInstanceID), maxContractID)
}
keeper.setParams(ctx, data.Params)
return nil
}
@ -54,6 +55,8 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) error
func ExportGenesis(ctx sdk.Context, keeper Keeper) types.GenesisState {
var genState types.GenesisState
genState.Params = keeper.GetParams(ctx)
maxCodeID := keeper.GetNextCodeID(ctx)
for i := uint64(1); i < maxCodeID; i++ {
if !keeper.containsCodeInfo(ctx, i) {

View File

@ -13,6 +13,7 @@ import (
"github.com/cosmos/cosmos-sdk/store"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/params"
"github.com/cosmos/cosmos-sdk/x/staking"
fuzz "github.com/google/gofuzz"
"github.com/stretchr/testify/require"
@ -22,13 +23,13 @@ import (
)
func TestGenesisExportImport(t *testing.T) {
srcKeeper, srcCtx, srcCleanup := setupKeeper(t)
srcKeeper, srcCtx, srcStoreKeys, srcCleanup := setupKeeper(t)
defer srcCleanup()
wasmCode, err := ioutil.ReadFile("./testdata/contract.wasm")
require.NoError(t, err)
// store some test data
f := fuzz.New().Funcs(FuzzAddr, FuzzAbsoluteTxPosition, FuzzContractInfo, FuzzStateModel)
f := fuzz.New().Funcs(FuzzAddr, FuzzAbsoluteTxPosition, FuzzContractInfo, FuzzStateModel, FuzzAccessType, FuzzAccessConfig)
for i := 0; i < 25; i++ {
var (
codeInfo types.CodeInfo
@ -39,13 +40,16 @@ func TestGenesisExportImport(t *testing.T) {
f.Fuzz(&contract)
f.Fuzz(&stateModels)
codeID, err := srcKeeper.Create(srcCtx, codeInfo.Creator, wasmCode, codeInfo.Source, codeInfo.Builder)
codeID, err := srcKeeper.Create(srcCtx, codeInfo.Creator, wasmCode, codeInfo.Source, codeInfo.Builder, &codeInfo.InstantiateConfig)
require.NoError(t, err)
contract.CodeID = codeID
contractAddr := srcKeeper.generateContractAddress(srcCtx, codeID)
srcKeeper.setContractInfo(srcCtx, contractAddr, &contract)
srcKeeper.importContractState(srcCtx, contractAddr, stateModels)
}
var wasmParams types.Params
f.Fuzz(&wasmParams)
srcKeeper.setParams(srcCtx, wasmParams)
// export
genesisState := ExportGenesis(srcCtx, srcKeeper)
@ -62,23 +66,25 @@ func TestGenesisExportImport(t *testing.T) {
})
// re-import
dstKeeper, dstCtx, dstCleanup := setupKeeper(t)
dstKeeper, dstCtx, dstStoreKeys, dstCleanup := setupKeeper(t)
defer dstCleanup()
InitGenesis(dstCtx, dstKeeper, genesisState)
// compare whole DB
srcIT := srcCtx.KVStore(srcKeeper.storeKey).Iterator(nil, nil)
dstIT := dstCtx.KVStore(dstKeeper.storeKey).Iterator(nil, nil)
for j := range srcStoreKeys {
srcIT := srcCtx.KVStore(srcStoreKeys[j]).Iterator(nil, nil)
dstIT := dstCtx.KVStore(dstStoreKeys[j]).Iterator(nil, nil)
for i := 0; srcIT.Valid(); i++ {
require.True(t, dstIT.Valid(), "destination DB has less elements than source. Missing: %q", srcIT.Key())
require.Equal(t, srcIT.Key(), dstIT.Key(), i)
require.Equal(t, srcIT.Value(), dstIT.Value(), "element (%d): %s", i, srcIT.Key())
srcIT.Next()
dstIT.Next()
for i := 0; srcIT.Valid(); i++ {
require.True(t, dstIT.Valid(), "destination DB has less elements than source. Missing: %q", srcIT.Key())
require.Equal(t, srcIT.Key(), dstIT.Key(), i)
require.Equal(t, srcIT.Value(), dstIT.Value(), "[%s] element (%d): %s", srcStoreKeys[j].Name(), i, srcIT.Key())
srcIT.Next()
dstIT.Next()
}
require.False(t, dstIT.Valid())
}
require.False(t, dstIT.Valid())
}
func TestFailFastImport(t *testing.T) {
@ -101,11 +107,11 @@ func TestFailFastImport(t *testing.T) {
},
CodesBytes: wasmCode,
}},
Contracts: nil,
Sequences: []types.Sequence{
{IDKey: types.KeyLastCodeID, Value: 2},
{IDKey: types.KeyLastInstanceID, Value: 1},
},
Params: types.DefaultParams(),
},
expSuccess: true,
},
@ -126,11 +132,11 @@ func TestFailFastImport(t *testing.T) {
},
CodesBytes: wasmCode,
}},
Contracts: nil,
Sequences: []types.Sequence{
{IDKey: types.KeyLastCodeID, Value: 10},
{IDKey: types.KeyLastInstanceID, Value: 1},
},
Params: types.DefaultParams(),
},
expSuccess: true,
},
@ -156,6 +162,7 @@ func TestFailFastImport(t *testing.T) {
{IDKey: types.KeyLastCodeID, Value: 3},
{IDKey: types.KeyLastInstanceID, Value: 1},
},
Params: types.DefaultParams(),
},
expSuccess: true,
},
@ -168,7 +175,7 @@ func TestFailFastImport(t *testing.T) {
},
CodesBytes: wasmCode,
}},
Contracts: nil,
Params: types.DefaultParams(),
}},
"prevent duplicate codeIDs": {src: types.GenesisState{
Codes: []types.Code{
@ -189,6 +196,7 @@ func TestFailFastImport(t *testing.T) {
CodesBytes: wasmCode,
},
},
Params: types.DefaultParams(),
}},
"happy path: code id in info and contract do match": {
src: types.GenesisState{
@ -210,6 +218,7 @@ func TestFailFastImport(t *testing.T) {
{IDKey: types.KeyLastCodeID, Value: 2},
{IDKey: types.KeyLastInstanceID, Value: 2},
},
Params: types.DefaultParams(),
},
expSuccess: true,
},
@ -236,6 +245,7 @@ func TestFailFastImport(t *testing.T) {
{IDKey: types.KeyLastCodeID, Value: 2},
{IDKey: types.KeyLastInstanceID, Value: 3},
},
Params: types.DefaultParams(),
},
expSuccess: true,
},
@ -247,6 +257,7 @@ func TestFailFastImport(t *testing.T) {
ContractInfo: types.ContractInfoFixture(func(c *wasmTypes.ContractInfo) { c.CodeID = 1 }),
},
},
Params: types.DefaultParams(),
},
},
"prevent duplicate contract address": {
@ -268,6 +279,7 @@ func TestFailFastImport(t *testing.T) {
ContractInfo: types.ContractInfoFixture(func(c *wasmTypes.ContractInfo) { c.CodeID = 1 }),
},
},
Params: types.DefaultParams(),
},
},
"prevent duplicate contract model keys": {
@ -296,6 +308,7 @@ func TestFailFastImport(t *testing.T) {
},
},
},
Params: types.DefaultParams(),
},
},
"prevent duplicate sequences": {
@ -304,6 +317,7 @@ func TestFailFastImport(t *testing.T) {
{IDKey: []byte("foo"), Value: 1},
{IDKey: []byte("foo"), Value: 9999},
},
Params: types.DefaultParams(),
},
},
"prevent code id seq init value == max codeID used": {
@ -319,6 +333,7 @@ func TestFailFastImport(t *testing.T) {
Sequences: []types.Sequence{
{IDKey: types.KeyLastCodeID, Value: 1},
},
Params: types.DefaultParams(),
},
},
"prevent contract id seq init value == count contracts": {
@ -341,13 +356,14 @@ func TestFailFastImport(t *testing.T) {
{IDKey: types.KeyLastCodeID, Value: 2},
{IDKey: types.KeyLastInstanceID, Value: 1},
},
Params: types.DefaultParams(),
},
},
}
for msg, spec := range specs {
t.Run(msg, func(t *testing.T) {
keeper, ctx, cleanup := setupKeeper(t)
keeper, ctx, _, cleanup := setupKeeper(t)
defer cleanup()
require.NoError(t, types.ValidateGenesis(spec.src))
@ -361,26 +377,34 @@ func TestFailFastImport(t *testing.T) {
}
}
func setupKeeper(t *testing.T) (Keeper, sdk.Context, func()) {
func setupKeeper(t *testing.T) (Keeper, sdk.Context, []sdk.StoreKey, func()) {
t.Helper()
tempDir, err := ioutil.TempDir("", "wasm")
require.NoError(t, err)
cleanup := func() { os.RemoveAll(tempDir) }
//t.Cleanup(cleanup) todo: add with Go 1.14
var (
keyParams = sdk.NewKVStoreKey(params.StoreKey)
tkeyParams = sdk.NewTransientStoreKey(params.TStoreKey)
keyWasm = sdk.NewKVStoreKey(wasmTypes.StoreKey)
)
keyContract := sdk.NewKVStoreKey(wasmTypes.StoreKey)
db := dbm.NewMemDB()
ms := store.NewCommitMultiStore(db)
ms.MountStoreWithDB(keyContract, sdk.StoreTypeIAVL, db)
ms.MountStoreWithDB(keyWasm, sdk.StoreTypeIAVL, db)
ms.MountStoreWithDB(keyParams, sdk.StoreTypeIAVL, db)
ms.MountStoreWithDB(tkeyParams, sdk.StoreTypeTransient, db)
require.NoError(t, ms.LoadLatestVersion())
ctx := sdk.NewContext(ms, abci.Header{
Height: 1234567,
Time: time.Date(2020, time.April, 22, 12, 0, 0, 0, time.UTC),
}, false, log.NewNopLogger())
cdc := MakeTestCodec()
pk := params.NewKeeper(cdc, keyParams, tkeyParams)
wasmConfig := wasmTypes.DefaultWasmConfig()
srcKeeper := NewKeeper(cdc, keyWasm, pk.Subspace(wasmTypes.DefaultParamspace), auth.AccountKeeper{}, nil, staking.Keeper{}, nil, tempDir, wasmConfig, "", nil, nil)
srcKeeper.setParams(ctx, wasmTypes.DefaultParams())
srcKeeper := NewKeeper(cdc, keyContract, auth.AccountKeeper{}, nil, staking.Keeper{}, nil, tempDir, wasmConfig, "", nil, nil)
return srcKeeper, ctx, cleanup
return srcKeeper, ctx, []sdk.StoreKey{keyWasm, keyParams}, cleanup
}

View File

@ -5,20 +5,21 @@ import (
"encoding/binary"
"path/filepath"
"github.com/cosmos/cosmos-sdk/x/params/subspace"
"github.com/cosmos/cosmos-sdk/x/staking"
"github.com/pkg/errors"
wasm "github.com/CosmWasm/go-cosmwasm"
wasmTypes "github.com/CosmWasm/go-cosmwasm/types"
"github.com/CosmWasm/wasmd/x/wasm/internal/types"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/store/prefix"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/bank"
"github.com/cosmos/cosmos-sdk/x/params"
"github.com/tendermint/tendermint/crypto"
"github.com/CosmWasm/wasmd/x/wasm/internal/types"
)
// GasMultiplier is how many cosmwasm gas points = 1 sdk gas point
@ -50,11 +51,12 @@ type Keeper struct {
// queryGasLimit is the max wasm gas that can be spent on executing a query with a contract
queryGasLimit uint64
authZPolicy AuthorizationPolicy
paramSpace subspace.Subspace
}
// NewKeeper creates a new contract Keeper instance
// If customEncoders is non-nil, we can use this to override some of the message handler, especially custom
func NewKeeper(cdc *codec.Codec, storeKey sdk.StoreKey, accountKeeper auth.AccountKeeper, bankKeeper bank.Keeper,
func NewKeeper(cdc *codec.Codec, storeKey sdk.StoreKey, paramSpace params.Subspace, accountKeeper auth.AccountKeeper, bankKeeper bank.Keeper,
stakingKeeper staking.Keeper,
router sdk.Router, homeDir string, wasmConfig types.WasmConfig, supportedFeatures string, customEncoders *MessageEncoders, customPlugins *QueryPlugins) Keeper {
wasmer, err := wasm.NewWasmer(filepath.Join(homeDir, "wasm"), supportedFeatures, wasmConfig.CacheSize)
@ -62,7 +64,10 @@ func NewKeeper(cdc *codec.Codec, storeKey sdk.StoreKey, accountKeeper auth.Accou
panic(err)
}
messenger := NewMessageHandler(router, customEncoders)
// set KeyTable if it has not already been set
if !paramSpace.HasKeyTable() {
paramSpace = paramSpace.WithKeyTable(types.ParamKeyTable())
}
keeper := Keeper{
storeKey: storeKey,
@ -70,16 +75,47 @@ func NewKeeper(cdc *codec.Codec, storeKey sdk.StoreKey, accountKeeper auth.Accou
wasmer: *wasmer,
accountKeeper: accountKeeper,
bankKeeper: bankKeeper,
messenger: messenger,
messenger: NewMessageHandler(router, customEncoders),
queryGasLimit: wasmConfig.SmartQueryGasLimit,
authZPolicy: DefaultAuthorizationPolicy{},
paramSpace: paramSpace,
}
keeper.queryPlugins = DefaultQueryPlugins(bankKeeper, stakingKeeper, keeper).Merge(customPlugins)
return keeper
}
func (k Keeper) getUploadAccessConfig(ctx sdk.Context) types.AccessConfig {
var a types.AccessConfig
k.paramSpace.Get(ctx, types.ParamStoreKeyUploadAccess, &a)
return a
}
func (k Keeper) getInstantiateAccessConfig(ctx sdk.Context) types.AccessType {
var a types.AccessType
k.paramSpace.Get(ctx, types.ParamStoreKeyInstantiateAccess, &a)
return a
}
// GetParams returns the total set of wasm parameters.
func (k Keeper) GetParams(ctx sdk.Context) types.Params {
var params types.Params
k.paramSpace.GetParamSet(ctx, &params)
return params
}
func (k Keeper) setParams(ctx sdk.Context, ps types.Params) {
k.paramSpace.SetParamSet(ctx, &ps)
}
// Create uploads and compiles a WASM contract, returning a short identifier for the contract
func (k Keeper) Create(ctx sdk.Context, creator sdk.AccAddress, wasmCode []byte, source string, builder string) (codeID uint64, err error) {
func (k Keeper) Create(ctx sdk.Context, creator sdk.AccAddress, wasmCode []byte, source string, builder string, instantiateAccess *types.AccessConfig) (codeID uint64, err error) {
return k.create(ctx, creator, wasmCode, source, builder, instantiateAccess, k.authZPolicy)
}
func (k Keeper) create(ctx sdk.Context, creator sdk.AccAddress, wasmCode []byte, source string, builder string, instantiateAccess *types.AccessConfig, authZ AuthorizationPolicy) (codeID uint64, err error) {
if !authZ.CanCreateCode(k.getUploadAccessConfig(ctx), creator) {
return 0, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "can not create code")
}
wasmCode, err = uncompress(wasmCode)
if err != nil {
return 0, sdkerrors.Wrap(types.ErrCreateFailed, err.Error())
@ -93,7 +129,11 @@ func (k Keeper) Create(ctx sdk.Context, creator sdk.AccAddress, wasmCode []byte,
}
store := ctx.KVStore(k.storeKey)
codeID = k.autoIncrementID(ctx, types.KeyLastCodeID)
codeInfo := types.NewCodeInfo(codeHash, creator, source, builder)
if instantiateAccess == nil {
defaultAccessConfig := k.getInstantiateAccessConfig(ctx).With(creator)
instantiateAccess = &defaultAccessConfig
}
codeInfo := types.NewCodeInfo(codeHash, creator, source, builder, *instantiateAccess)
// 0x01 | codeID (uint64) -> ContractInfo
store.Set(types.GetCodeKey(codeID), k.cdc.MustMarshalBinaryBare(codeInfo))

View File

@ -44,7 +44,7 @@ func TestCreate(t *testing.T) {
wasmCode, err := ioutil.ReadFile("./testdata/contract.wasm")
require.NoError(t, err)
contractID, err := keeper.Create(ctx, creator, wasmCode, "https://github.com/CosmWasm/wasmd/blob/master/x/wasm/testdata/escrow.wasm", "cosmwasm-opt:0.5.2")
contractID, err := keeper.Create(ctx, creator, wasmCode, "https://github.com/CosmWasm/wasmd/blob/master/x/wasm/testdata/escrow.wasm", "cosmwasm-opt:0.5.2", nil)
require.NoError(t, err)
require.Equal(t, uint64(1), contractID)
// and verify content
@ -53,6 +53,56 @@ func TestCreate(t *testing.T) {
require.Equal(t, wasmCode, storedCode)
}
func TestCreateWithParamPermissions(t *testing.T) {
tempDir, err := ioutil.TempDir("", "wasm")
require.NoError(t, err)
defer os.RemoveAll(tempDir)
ctx, keepers := CreateTestInput(t, false, tempDir, SupportedFeatures, nil, nil)
accKeeper, keeper := keepers.AccountKeeper, keepers.WasmKeeper
deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000))
creator := createFakeFundedAccount(ctx, accKeeper, deposit)
otherAddr := createFakeFundedAccount(ctx, accKeeper, deposit)
wasmCode, err := ioutil.ReadFile("./testdata/contract.wasm")
require.NoError(t, err)
specs := map[string]struct {
srcPermission types.AccessConfig
expError *sdkerrors.Error
}{
"default": {
srcPermission: types.DefaultUploadAccess,
},
"everybody": {
srcPermission: types.AllowEverybody,
},
"nobody": {
srcPermission: types.AllowNobody,
expError: sdkerrors.ErrUnauthorized,
},
"onlyAddress with matching address": {
srcPermission: types.OnlyAddress.With(creator),
},
"onlyAddress with non matching address": {
srcPermission: types.OnlyAddress.With(otherAddr),
expError: sdkerrors.ErrUnauthorized,
},
}
for msg, spec := range specs {
t.Run(msg, func(t *testing.T) {
params := types.DefaultParams()
params.UploadAccess = spec.srcPermission
keeper.setParams(ctx, params)
_, err := keeper.Create(ctx, creator, wasmCode, "https://github.com/CosmWasm/wasmd/blob/master/x/wasm/testdata/escrow.wasm", "cosmwasm-opt:0.5.2", nil)
require.True(t, spec.expError.Is(err), err)
if spec.expError != nil {
return
}
})
}
}
func TestCreateDuplicate(t *testing.T) {
tempDir, err := ioutil.TempDir("", "wasm")
require.NoError(t, err)
@ -67,12 +117,12 @@ func TestCreateDuplicate(t *testing.T) {
require.NoError(t, err)
// create one copy
contractID, err := keeper.Create(ctx, creator, wasmCode, "https://github.com/CosmWasm/wasmd/blob/master/x/wasm/testdata/escrow.wasm", "cosmwasm-opt:0.5.2")
contractID, err := keeper.Create(ctx, creator, wasmCode, "https://github.com/CosmWasm/wasmd/blob/master/x/wasm/testdata/escrow.wasm", "cosmwasm-opt:0.5.2", nil)
require.NoError(t, err)
require.Equal(t, uint64(1), contractID)
// create second copy
duplicateID, err := keeper.Create(ctx, creator, wasmCode, "https://github.com/CosmWasm/wasmd/blob/master/x/wasm/testdata/escrow.wasm", "cosmwasm-opt:0.5.2")
duplicateID, err := keeper.Create(ctx, creator, wasmCode, "https://github.com/CosmWasm/wasmd/blob/master/x/wasm/testdata/escrow.wasm", "cosmwasm-opt:0.5.2", nil)
require.NoError(t, err)
require.Equal(t, uint64(2), duplicateID)
@ -102,14 +152,14 @@ func TestCreateWithSimulation(t *testing.T) {
require.NoError(t, err)
// create this once in simulation mode
contractID, err := keeper.Create(ctx, creator, wasmCode, "https://github.com/CosmWasm/wasmd/blob/master/x/wasm/testdata/escrow.wasm", "confio/cosmwasm-opt:0.57.2")
contractID, err := keeper.Create(ctx, creator, wasmCode, "https://github.com/CosmWasm/wasmd/blob/master/x/wasm/testdata/escrow.wasm", "confio/cosmwasm-opt:0.57.2", nil)
require.NoError(t, err)
require.Equal(t, uint64(1), contractID)
// then try to create it in non-simulation mode (should not fail)
ctx, keepers = CreateTestInput(t, false, tempDir, SupportedFeatures, nil, nil)
accKeeper, keeper = keepers.AccountKeeper, keepers.WasmKeeper
contractID, err = keeper.Create(ctx, creator, wasmCode, "https://github.com/CosmWasm/wasmd/blob/master/x/wasm/testdata/escrow.wasm", "confio/cosmwasm-opt:0.7.2")
contractID, err = keeper.Create(ctx, creator, wasmCode, "https://github.com/CosmWasm/wasmd/blob/master/x/wasm/testdata/escrow.wasm", "confio/cosmwasm-opt:0.7.2", nil)
require.NoError(t, err)
require.Equal(t, uint64(1), contractID)
@ -157,7 +207,7 @@ func TestCreateWithGzippedPayload(t *testing.T) {
wasmCode, err := ioutil.ReadFile("./testdata/contract.wasm.gzip")
require.NoError(t, err)
contractID, err := keeper.Create(ctx, creator, wasmCode, "https://github.com/CosmWasm/wasmd/blob/master/x/wasm/testdata/escrow.wasm", "")
contractID, err := keeper.Create(ctx, creator, wasmCode, "https://github.com/CosmWasm/wasmd/blob/master/x/wasm/testdata/escrow.wasm", "", nil)
require.NoError(t, err)
require.Equal(t, uint64(1), contractID)
// and verify content
@ -181,7 +231,7 @@ func TestInstantiate(t *testing.T) {
wasmCode, err := ioutil.ReadFile("./testdata/contract.wasm")
require.NoError(t, err)
contractID, err := keeper.Create(ctx, creator, wasmCode, "https://github.com/CosmWasm/wasmd/blob/master/x/wasm/testdata/escrow.wasm", "")
contractID, err := keeper.Create(ctx, creator, wasmCode, "https://github.com/CosmWasm/wasmd/blob/master/x/wasm/testdata/escrow.wasm", "", nil)
require.NoError(t, err)
_, _, bob := keyPubAddr()
@ -202,7 +252,7 @@ func TestInstantiate(t *testing.T) {
require.Equal(t, "cosmos18vd8fpwxzck93qlwghaj6arh4p7c5n89uzcee5", addr.String())
gasAfter := ctx.GasMeter().GasConsumed()
require.Equal(t, uint64(0x11536), gasAfter-gasBefore)
require.Equal(t, uint64(0x11542), gasAfter-gasBefore)
// ensure it is stored properly
info := keeper.GetContractInfo(ctx, addr)
@ -250,7 +300,7 @@ func TestExecute(t *testing.T) {
wasmCode, err := ioutil.ReadFile("./testdata/contract.wasm")
require.NoError(t, err)
contractID, err := keeper.Create(ctx, creator, wasmCode, "", "")
contractID, err := keeper.Create(ctx, creator, wasmCode, "", "", nil)
require.NoError(t, err)
_, _, bob := keyPubAddr()
@ -297,7 +347,7 @@ func TestExecute(t *testing.T) {
// make sure gas is properly deducted from ctx
gasAfter := ctx.GasMeter().GasConsumed()
require.Equal(t, uint64(0x11bef), gasAfter-gasBefore)
require.Equal(t, uint64(0x11bfb), gasAfter-gasBefore)
// ensure bob now exists and got both payments released
bobAcct = accKeeper.GetAccount(ctx, bob)
@ -344,7 +394,7 @@ func TestExecuteWithPanic(t *testing.T) {
wasmCode, err := ioutil.ReadFile("./testdata/contract.wasm")
require.NoError(t, err)
contractID, err := keeper.Create(ctx, creator, wasmCode, "", "")
contractID, err := keeper.Create(ctx, creator, wasmCode, "", "", nil)
require.NoError(t, err)
_, _, bob := keyPubAddr()
@ -378,7 +428,7 @@ func TestExecuteWithCpuLoop(t *testing.T) {
wasmCode, err := ioutil.ReadFile("./testdata/contract.wasm")
require.NoError(t, err)
contractID, err := keeper.Create(ctx, creator, wasmCode, "", "")
contractID, err := keeper.Create(ctx, creator, wasmCode, "", "", nil)
require.NoError(t, err)
_, _, bob := keyPubAddr()
@ -419,7 +469,7 @@ func TestExecuteWithStorageLoop(t *testing.T) {
wasmCode, err := ioutil.ReadFile("./testdata/contract.wasm")
require.NoError(t, err)
contractID, err := keeper.Create(ctx, creator, wasmCode, "", "")
contractID, err := keeper.Create(ctx, creator, wasmCode, "", "", nil)
require.NoError(t, err)
_, _, bob := keyPubAddr()
@ -466,9 +516,9 @@ func TestMigrate(t *testing.T) {
wasmCode, err := ioutil.ReadFile("./testdata/contract.wasm")
require.NoError(t, err)
originalContractID, err := keeper.Create(ctx, creator, wasmCode, "", "")
originalContractID, err := keeper.Create(ctx, creator, wasmCode, "", "", nil)
require.NoError(t, err)
newContractID, err := keeper.Create(ctx, creator, wasmCode, "", "")
newContractID, err := keeper.Create(ctx, creator, wasmCode, "", "", nil)
require.NoError(t, err)
require.NotEqual(t, originalContractID, newContractID)
@ -601,9 +651,9 @@ func TestMigrateWithDispatchedMessage(t *testing.T) {
burnerCode, err := ioutil.ReadFile("./testdata/burner.wasm")
require.NoError(t, err)
originalContractID, err := keeper.Create(ctx, creator, wasmCode, "", "")
originalContractID, err := keeper.Create(ctx, creator, wasmCode, "", "", nil)
require.NoError(t, err)
burnerContractID, err := keeper.Create(ctx, creator, burnerCode, "", "")
burnerContractID, err := keeper.Create(ctx, creator, burnerCode, "", "", nil)
require.NoError(t, err)
require.NotEqual(t, originalContractID, burnerContractID)
@ -712,7 +762,7 @@ func TestUpdateContractAdmin(t *testing.T) {
wasmCode, err := ioutil.ReadFile("./testdata/contract.wasm")
require.NoError(t, err)
originalContractID, err := keeper.Create(ctx, creator, wasmCode, "", "")
originalContractID, err := keeper.Create(ctx, creator, wasmCode, "", "", nil)
require.NoError(t, err)
_, _, anyAddr := keyPubAddr()
@ -787,7 +837,7 @@ func TestClearContractAdmin(t *testing.T) {
wasmCode, err := ioutil.ReadFile("./testdata/contract.wasm")
require.NoError(t, err)
originalContractID, err := keeper.Create(ctx, creator, wasmCode, "", "")
originalContractID, err := keeper.Create(ctx, creator, wasmCode, "", "", nil)
require.NoError(t, err)
_, _, anyAddr := keyPubAddr()

View File

@ -26,16 +26,16 @@ func NewWasmProposalHandler(k Keeper, enabledTypes map[string]struct{}) govtypes
return sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unsupported wasm proposal content type: %q", content.ProposalType())
}
switch c := content.(type) {
case *types.StoreCodeProposal:
return handleStoreCodeProposal(ctx, k, *c)
case *types.InstantiateContractProposal:
return handleInstantiateProposal(ctx, k, *c)
case *types.MigrateContractProposal:
return handleMigrateProposal(ctx, k, *c)
case *types.UpdateAdminProposal:
return handleUpdateAdminProposal(ctx, k, *c)
case *types.ClearAdminProposal:
return handleClearAdminProposal(ctx, k, *c)
case types.StoreCodeProposal:
return handleStoreCodeProposal(ctx, k, c)
case types.InstantiateContractProposal:
return handleInstantiateProposal(ctx, k, c)
case types.MigrateContractProposal:
return handleMigrateProposal(ctx, k, c)
case types.UpdateAdminProposal:
return handleUpdateAdminProposal(ctx, k, c)
case types.ClearAdminProposal:
return handleClearAdminProposal(ctx, k, c)
default:
return sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized wasm proposal content type: %T", c)
}
@ -47,7 +47,7 @@ func handleStoreCodeProposal(ctx sdk.Context, k Keeper, p types.StoreCodeProposa
return err
}
codeID, err := k.Create(ctx, p.Creator, p.WASMByteCode, p.Source, p.Builder)
codeID, err := k.create(ctx, p.Creator, p.WASMByteCode, p.Source, p.Builder, p.InstantiatePermission, GovAuthorizationPolicy{})
if err != nil {
return err
}

View File

@ -11,6 +11,7 @@ import (
"github.com/CosmWasm/wasmd/x/wasm/internal/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/gov"
"github.com/cosmos/cosmos-sdk/x/params"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@ -36,7 +37,7 @@ func TestStoreCodeProposal(t *testing.T) {
})
// when stored
storedProposal, err := govKeeper.SubmitProposal(ctx, &src)
storedProposal, err := govKeeper.SubmitProposal(ctx, src)
require.NoError(t, err)
// and proposal execute
@ -84,7 +85,7 @@ func TestInstantiateProposal(t *testing.T) {
})
// when stored
storedProposal, err := govKeeper.SubmitProposal(ctx, &src)
storedProposal, err := govKeeper.SubmitProposal(ctx, src)
require.NoError(t, err)
// and proposal execute
@ -153,7 +154,7 @@ func TestMigrateProposal(t *testing.T) {
}
// when stored
storedProposal, err := govKeeper.SubmitProposal(ctx, &src)
storedProposal, err := govKeeper.SubmitProposal(ctx, src)
require.NoError(t, err)
// and proposal execute
@ -187,7 +188,7 @@ func TestAdminProposals(t *testing.T) {
}{
"update with different admin": {
state: types.ContractInfoFixture(),
srcProposal: &types.UpdateAdminProposal{
srcProposal: types.UpdateAdminProposal{
WasmProposal: types.WasmProposal{
Title: "Foo",
Description: "Bar",
@ -202,7 +203,7 @@ func TestAdminProposals(t *testing.T) {
state: types.ContractInfoFixture(func(info *types.ContractInfo) {
info.Admin = nil
}),
srcProposal: &types.UpdateAdminProposal{
srcProposal: types.UpdateAdminProposal{
WasmProposal: types.WasmProposal{
Title: "Foo",
Description: "Bar",
@ -215,7 +216,7 @@ func TestAdminProposals(t *testing.T) {
},
"clear admin": {
state: types.ContractInfoFixture(),
srcProposal: &types.ClearAdminProposal{
srcProposal: types.ClearAdminProposal{
WasmProposal: types.WasmProposal{
Title: "Foo",
Description: "Bar",
@ -229,7 +230,7 @@ func TestAdminProposals(t *testing.T) {
state: types.ContractInfoFixture(func(info *types.ContractInfo) {
info.Admin = nil
}),
srcProposal: &types.ClearAdminProposal{
srcProposal: types.ClearAdminProposal{
WasmProposal: types.WasmProposal{
Title: "Foo",
Description: "Bar",
@ -268,3 +269,77 @@ func TestAdminProposals(t *testing.T) {
})
}
}
func TestUpdateParamsProposal(t *testing.T) {
tempDir, err := ioutil.TempDir("", "wasm")
require.NoError(t, err)
defer os.RemoveAll(tempDir)
ctx, keepers := CreateTestInput(t, false, tempDir, "staking", nil, nil)
govKeeper, wasmKeeper := keepers.GovKeeper, keepers.WasmKeeper
var (
cdc = keepers.WasmKeeper.cdc
myAddress sdk.AccAddress = make([]byte, sdk.AddrLen)
oneAddressAccessConfig = types.OnlyAddress.With(myAddress)
)
specs := map[string]struct {
src params.ParamChange
expUploadConfig types.AccessConfig
expInstantiateType types.AccessType
}{
"update upload permission param": {
src: params.ParamChange{
Subspace: types.DefaultParamspace,
Key: string(types.ParamStoreKeyUploadAccess),
Value: string(cdc.MustMarshalJSON(&types.AllowNobody)),
},
expUploadConfig: types.AllowNobody,
expInstantiateType: types.Everybody,
},
"update upload permission param with address": {
src: params.ParamChange{
Subspace: types.DefaultParamspace,
Key: string(types.ParamStoreKeyUploadAccess),
Value: string(cdc.MustMarshalJSON(&oneAddressAccessConfig)),
},
expUploadConfig: oneAddressAccessConfig,
expInstantiateType: types.Everybody,
},
"update instantiate param": {
src: params.ParamChange{
Subspace: types.DefaultParamspace,
Key: string(types.ParamStoreKeyInstantiateAccess),
Value: string(cdc.MustMarshalJSON(types.Nobody)),
},
expUploadConfig: types.AllowEverybody,
expInstantiateType: types.Nobody,
},
}
for msg, spec := range specs {
t.Run(msg, func(t *testing.T) {
wasmKeeper.setParams(ctx, types.DefaultParams())
proposal := params.ParameterChangeProposal{
Title: "Foo",
Description: "Bar",
Changes: []params.ParamChange{spec.src},
}
// when stored
storedProposal, err := govKeeper.SubmitProposal(ctx, proposal)
require.NoError(t, err)
// and proposal execute
handler := govKeeper.Router().GetRoute(storedProposal.ProposalRoute())
err = handler(ctx, storedProposal.Content)
require.NoError(t, err)
// then
assert.True(t, spec.expUploadConfig.Equals(wasmKeeper.getUploadAccessConfig(ctx)),
"got %#v not %#v", wasmKeeper.getUploadAccessConfig(ctx), spec.expUploadConfig)
assert.Equal(t, spec.expInstantiateType, wasmKeeper.getInstantiateAccessConfig(ctx))
})
}
}

View File

@ -30,7 +30,7 @@ func TestQueryContractState(t *testing.T) {
wasmCode, err := ioutil.ReadFile("./testdata/contract.wasm")
require.NoError(t, err)
contractID, err := keeper.Create(ctx, creator, wasmCode, "", "")
contractID, err := keeper.Create(ctx, creator, wasmCode, "", "", nil)
require.NoError(t, err)
_, _, bob := keyPubAddr()
@ -159,7 +159,7 @@ func TestListContractByCodeOrdering(t *testing.T) {
wasmCode, err := ioutil.ReadFile("./testdata/contract.wasm")
require.NoError(t, err)
codeID, err := keeper.Create(ctx, creator, wasmCode, "", "")
codeID, err := keeper.Create(ctx, creator, wasmCode, "", "", nil)
require.NoError(t, err)
_, _, bob := keyPubAddr()

View File

@ -65,14 +65,14 @@ func TestMaskReflectContractSend(t *testing.T) {
// upload mask code
maskCode, err := ioutil.ReadFile("./testdata/reflect.wasm")
require.NoError(t, err)
maskID, err := keeper.Create(ctx, creator, maskCode, "", "")
maskID, err := keeper.Create(ctx, creator, maskCode, "", "", nil)
require.NoError(t, err)
require.Equal(t, uint64(1), maskID)
// upload hackatom escrow code
escrowCode, err := ioutil.ReadFile("./testdata/contract.wasm")
require.NoError(t, err)
escrowID, err := keeper.Create(ctx, creator, escrowCode, "", "")
escrowID, err := keeper.Create(ctx, creator, escrowCode, "", "", nil)
require.NoError(t, err)
require.Equal(t, uint64(2), escrowID)
@ -150,7 +150,7 @@ func TestMaskReflectCustomMsg(t *testing.T) {
// upload code
maskCode, err := ioutil.ReadFile("./testdata/reflect.wasm")
require.NoError(t, err)
codeID, err := keeper.Create(ctx, creator, maskCode, "", "")
codeID, err := keeper.Create(ctx, creator, maskCode, "", "", nil)
require.NoError(t, err)
require.Equal(t, uint64(1), codeID)
@ -244,7 +244,7 @@ func TestMaskReflectCustomQuery(t *testing.T) {
// upload code
maskCode, err := ioutil.ReadFile("./testdata/reflect.wasm")
require.NoError(t, err)
codeID, err := keeper.Create(ctx, creator, maskCode, "", "")
codeID, err := keeper.Create(ctx, creator, maskCode, "", "", nil)
require.NoError(t, err)
require.Equal(t, uint64(1), codeID)

View File

@ -103,7 +103,7 @@ func TestInitializeStaking(t *testing.T) {
// upload staking derivates code
stakingCode, err := ioutil.ReadFile("./testdata/staking.wasm")
require.NoError(t, err)
stakingID, err := keeper.Create(ctx, creator, stakingCode, "", "")
stakingID, err := keeper.Create(ctx, creator, stakingCode, "", "", nil)
require.NoError(t, err)
require.Equal(t, uint64(1), stakingID)
@ -187,7 +187,7 @@ func initializeStaking(t *testing.T) initInfo {
// upload staking derivates code
stakingCode, err := ioutil.ReadFile("./testdata/staking.wasm")
require.NoError(t, err)
stakingID, err := keeper.Create(ctx, creator, stakingCode, "", "")
stakingID, err := keeper.Create(ctx, creator, stakingCode, "", "", nil)
require.NoError(t, err)
require.Equal(t, uint64(1), stakingID)

View File

@ -25,7 +25,7 @@ import (
"github.com/cosmos/cosmos-sdk/x/staking"
"github.com/cosmos/cosmos-sdk/x/supply"
wasmTypes "github.com/CosmWasm/wasmd/x/wasm/internal/types"
wasmtypes "github.com/CosmWasm/wasmd/x/wasm/internal/types"
)
const flagLRUCacheSize = "lru_size"
@ -43,10 +43,10 @@ func MakeTestCodec() *codec.Codec {
staking.AppModuleBasic{}.RegisterCodec(cdc)
distribution.AppModuleBasic{}.RegisterCodec(cdc)
gov.RegisterCodec(cdc)
wasmTypes.RegisterCodec(cdc)
wasmtypes.RegisterCodec(cdc)
sdk.RegisterCodec(cdc)
codec.RegisterCrypto(cdc)
params.RegisterCodec(cdc)
return cdc
}
@ -70,7 +70,7 @@ type TestKeepers struct {
// encoders can be nil to accept the defaults, or set it to override some of the message handlers (like default)
func CreateTestInput(t *testing.T, isCheckTx bool, tempDir string, supportedFeatures string, encoders *MessageEncoders, queriers *QueryPlugins) (sdk.Context, TestKeepers) {
keyContract := sdk.NewKVStoreKey(wasmTypes.StoreKey)
keyContract := sdk.NewKVStoreKey(wasmtypes.StoreKey)
keyAcc := sdk.NewKVStoreKey(auth.StoreKey)
keyStaking := sdk.NewKVStoreKey(staking.StoreKey)
keySupply := sdk.NewKVStoreKey(supply.StoreKey)
@ -98,18 +98,18 @@ func CreateTestInput(t *testing.T, isCheckTx bool, tempDir string, supportedFeat
}, isCheckTx, log.NewNopLogger())
cdc := MakeTestCodec()
pk := params.NewKeeper(cdc, keyParams, tkeyParams)
paramsKeeper := params.NewKeeper(cdc, keyParams, tkeyParams)
accountKeeper := auth.NewAccountKeeper(
cdc, // amino codec
keyAcc, // target store
pk.Subspace(auth.DefaultParamspace),
paramsKeeper.Subspace(auth.DefaultParamspace),
auth.ProtoBaseAccount, // prototype
)
bankKeeper := bank.NewBaseKeeper(
accountKeeper,
pk.Subspace(bank.DefaultParamspace),
paramsKeeper.Subspace(bank.DefaultParamspace),
nil,
)
bankKeeper.SetSendEnabled(ctx, true)
@ -125,10 +125,10 @@ func CreateTestInput(t *testing.T, isCheckTx bool, tempDir string, supportedFeat
}
supplyKeeper := supply.NewKeeper(cdc, keySupply, accountKeeper, bankKeeper, maccPerms)
stakingKeeper := staking.NewKeeper(cdc, keyStaking, supplyKeeper, pk.Subspace(staking.DefaultParamspace))
stakingKeeper := staking.NewKeeper(cdc, keyStaking, supplyKeeper, paramsKeeper.Subspace(staking.DefaultParamspace))
stakingKeeper.SetParams(ctx, TestingStakeParams)
distKeeper := distribution.NewKeeper(cdc, keyDistro, pk.Subspace(distribution.DefaultParamspace), stakingKeeper, supplyKeeper, auth.FeeCollectorName, nil)
distKeeper := distribution.NewKeeper(cdc, keyDistro, paramsKeeper.Subspace(distribution.DefaultParamspace), stakingKeeper, supplyKeeper, auth.FeeCollectorName, nil)
distKeeper.SetParams(ctx, distribution.DefaultParams())
stakingKeeper.SetHooks(distKeeper.Hooks())
@ -166,18 +166,22 @@ func CreateTestInput(t *testing.T, isCheckTx bool, tempDir string, supportedFeat
router.AddRoute(distribution.RouterKey, dh)
// Load default wasm config
wasmConfig := wasmTypes.DefaultWasmConfig()
keeper := NewKeeper(cdc, keyContract, accountKeeper, bankKeeper, stakingKeeper, router, tempDir, wasmConfig, supportedFeatures, encoders, queriers)
wasmConfig := wasmtypes.DefaultWasmConfig()
keeper := NewKeeper(cdc, keyContract, paramsKeeper.Subspace(wasmtypes.DefaultParamspace),
accountKeeper, bankKeeper, stakingKeeper, router, tempDir, wasmConfig,
supportedFeatures, encoders, queriers,
)
keeper.setParams(ctx, wasmtypes.DefaultParams())
// add wasm handler so we can loop-back (contracts calling contracts)
router.AddRoute(wasmTypes.RouterKey, TestHandler(keeper))
router.AddRoute(wasmtypes.RouterKey, TestHandler(keeper))
govRouter := gov.NewRouter().
AddRoute(params.RouterKey, params.NewParamChangeProposalHandler(paramsKeeper)).
AddRoute(govtypes.RouterKey, govtypes.ProposalHandler).
AddRoute(wasmTypes.RouterKey, NewWasmProposalHandler(keeper, wasmTypes.DefaultEnabledProposals))
AddRoute(wasmtypes.RouterKey, NewWasmProposalHandler(keeper, wasmtypes.DefaultEnabledProposals))
govKeeper := gov.NewKeeper(
cdc, keyGov, pk.Subspace(govtypes.DefaultParamspace).WithKeyTable(gov.ParamKeyTable()), supplyKeeper, stakingKeeper, govRouter,
cdc, keyGov, paramsKeeper.Subspace(govtypes.DefaultParamspace).WithKeyTable(gov.ParamKeyTable()), supplyKeeper, stakingKeeper, govRouter,
)
govKeeper.SetProposalID(ctx, govtypes.DefaultStartingProposalID)
@ -203,14 +207,14 @@ func TestHandler(k Keeper) sdk.Handler {
ctx = ctx.WithEventManager(sdk.NewEventManager())
switch msg := msg.(type) {
case wasmTypes.MsgInstantiateContract:
case wasmtypes.MsgInstantiateContract:
return handleInstantiate(ctx, k, &msg)
case *wasmTypes.MsgInstantiateContract:
case *wasmtypes.MsgInstantiateContract:
return handleInstantiate(ctx, k, msg)
case wasmTypes.MsgExecuteContract:
case wasmtypes.MsgExecuteContract:
return handleExecute(ctx, k, &msg)
case *wasmTypes.MsgExecuteContract:
case *wasmtypes.MsgExecuteContract:
return handleExecute(ctx, k, msg)
default:
@ -220,7 +224,7 @@ func TestHandler(k Keeper) sdk.Handler {
}
}
func handleInstantiate(ctx sdk.Context, k Keeper, msg *wasmTypes.MsgInstantiateContract) (*sdk.Result, error) {
func handleInstantiate(ctx sdk.Context, k Keeper, msg *wasmtypes.MsgInstantiateContract) (*sdk.Result, error) {
contractAddr, err := k.Instantiate(ctx, msg.Code, msg.Sender, msg.Admin, msg.InitMsg, msg.Label, msg.InitFunds)
if err != nil {
return nil, err
@ -232,7 +236,7 @@ func handleInstantiate(ctx sdk.Context, k Keeper, msg *wasmTypes.MsgInstantiateC
}, nil
}
func handleExecute(ctx sdk.Context, k Keeper, msg *wasmTypes.MsgExecuteContract) (*sdk.Result, error) {
func handleExecute(ctx sdk.Context, k Keeper, msg *wasmtypes.MsgExecuteContract) (*sdk.Result, error) {
res, err := k.Execute(ctx, msg.Contract, msg.Sender, msg.Msg, msg.SentFunds)
if err != nil {
return nil, err

View File

@ -11,6 +11,7 @@ func FuzzAddr(m *sdk.AccAddress, c fuzz.Continue) {
*m = make([]byte, 20)
c.Read(*m)
}
func FuzzAbsoluteTxPosition(m *types.AbsoluteTxPosition, c fuzz.Continue) {
m.BlockHeight = int64(c.RandUint64()) // can't be negative
m.TxIndex = c.RandUint64()
@ -28,7 +29,18 @@ func FuzzContractInfo(m *types.ContractInfo, c fuzz.Continue) {
c.Fuzz(&m.LastUpdated)
m.PreviousCodeID = c.RandUint64()
}
func FuzzStateModel(m *types.Model, c fuzz.Continue) {
m.Key = tmBytes.HexBytes(c.RandString())
c.Fuzz(&m.Value)
}
func FuzzAccessType(m *types.AccessType, c fuzz.Continue) {
*m = types.AllAccessTypes[c.Int()%len(types.AllAccessTypes)]
}
func FuzzAccessConfig(m *types.AccessConfig, c fuzz.Continue) {
FuzzAccessType(&m.Type, c)
var add sdk.AccAddress
FuzzAddr(&add, c)
*m = m.Type.With(add)
}

View File

@ -20,12 +20,16 @@ func (s Sequence) ValidateBasic() error {
// GenesisState is the struct representation of the export genesis
type GenesisState struct {
Params Params `json:"params"`
Codes []Code `json:"codes"`
Contracts []Contract `json:"contracts"`
Sequences []Sequence `json:"sequences"`
}
func (s GenesisState) ValidateBasic() error {
if err := s.Params.ValidateBasic(); err != nil {
return sdkerrors.Wrap(err, "params")
}
for i := range s.Codes {
if err := s.Codes[i].ValidateBasic(); err != nil {
return sdkerrors.Wrapf(err, "code: %d", i)

View File

@ -15,6 +15,12 @@ func TestValidateGenesisState(t *testing.T) {
"all good": {
srcMutator: func(s *GenesisState) {},
},
"params invalid": {
srcMutator: func(s *GenesisState) {
s.Params = Params{}
},
expError: true,
},
"codeinfo invalid": {
srcMutator: func(s *GenesisState) {
s.Codes[0].CodeInfo.CodeHash = nil

View File

@ -15,6 +15,8 @@ type MsgStoreCode struct {
Source string `json:"source" yaml:"source"`
// Builder is a valid docker image name with tag, optional
Builder string `json:"builder" yaml:"builder"`
// InstantiatePermission to apply on contract creation, optional
InstantiatePermission *AccessConfig `json:"instantiate_permission" yaml:"instantiate_permission"`
}
func (msg MsgStoreCode) Route() string {
@ -41,6 +43,11 @@ func (msg MsgStoreCode) ValidateBasic() error {
if err := validateBuilder(msg.Builder); err != nil {
return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "builder %s", err.Error())
}
if msg.InstantiatePermission != nil {
if err := msg.InstantiatePermission.ValidateBasic(); err != nil {
return sdkerrors.Wrap(err, "instantiate permission")
}
}
return nil
}

View File

@ -107,6 +107,14 @@ func TestStoreCodeValidation(t *testing.T) {
},
valid: false,
},
"invalid InstantiatePermission": {
msg: MsgStoreCode{
Sender: goodAddress,
WASMByteCode: []byte("foo"),
InstantiatePermission: &AccessConfig{Type: OnlyAddress, Address: badAddress},
},
valid: false,
},
}
for name, tc := range cases {
@ -119,7 +127,6 @@ func TestStoreCodeValidation(t *testing.T) {
}
})
}
}
func TestInstantiateContractValidation(t *testing.T) {

View File

@ -6,6 +6,8 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/x/params"
"github.com/pkg/errors"
"gopkg.in/yaml.v2"
)
const (
@ -25,6 +27,8 @@ const (
Everybody AccessType = 3
)
var AllAccessTypes = []AccessType{Nobody, OnlyAddress, Everybody}
func (a AccessType) With(addr sdk.AccAddress) AccessConfig {
switch a {
case Nobody:
@ -41,8 +45,12 @@ func (a AccessType) With(addr sdk.AccAddress) AccessConfig {
}
type AccessConfig struct {
Type AccessType `json:"type"`
Address sdk.AccAddress `json:"address"`
Type AccessType `json:"type" yaml:"type"`
Address sdk.AccAddress `json:"address" yaml:"address"`
}
func (a AccessConfig) Equals(o AccessConfig) bool {
return a.Type == o.Type && a.Address.Equals(o.Address)
}
var (
@ -51,12 +59,47 @@ var (
AllowNobody = AccessConfig{Type: Nobody}
)
// ParamKeyTable type declaration for parameters
// Params defines the set of wasm parameters.
type Params struct {
UploadAccess AccessConfig `json:"upload_access" yaml:"upload_access"`
InstantiateDefaultPermission AccessType `json:"instantiate_default_permission" yaml:"instantiate_default_permission"`
}
// ParamKeyTable returns the parameter key table.
func ParamKeyTable() params.KeyTable {
return params.NewKeyTable(
params.NewParamSetPair(ParamStoreKeyUploadAccess, AllowEverybody, validateAccessConfig),
params.NewParamSetPair(ParamStoreKeyInstantiateAccess, Everybody, validateAccessType),
)
return params.NewKeyTable().RegisterParamSet(&Params{})
}
// DefaultParams returns default wasm parameters
func DefaultParams() Params {
return Params{
UploadAccess: AllowEverybody,
InstantiateDefaultPermission: Everybody,
}
}
func (p Params) String() string {
out, _ := yaml.Marshal(p)
return string(out)
}
// ParamSetPairs returns the parameter set pairs.
func (p *Params) ParamSetPairs() params.ParamSetPairs {
return params.ParamSetPairs{
params.NewParamSetPair(ParamStoreKeyUploadAccess, &p.UploadAccess, validateAccessConfig),
params.NewParamSetPair(ParamStoreKeyInstantiateAccess, &p.InstantiateDefaultPermission, validateAccessType),
}
}
// ValidateBasic performs basic validation on wasm parameters
func (p Params) ValidateBasic() error {
if err := validateAccessType(p.InstantiateDefaultPermission); err != nil {
return errors.Wrap(err, "instantiate default permission")
}
if err := validateAccessConfig(p.UploadAccess); err != nil {
return errors.Wrap(err, "upload access")
}
return nil
}
func validateAccessConfig(i interface{}) error {
@ -75,8 +118,12 @@ func validateAccessType(i interface{}) error {
if v == Undefined {
return sdkerrors.Wrap(ErrEmpty, "type")
}
// TODO: should we prevent Nobody here?
return nil
for i := range AllAccessTypes {
if AllAccessTypes[i] == v {
return nil
}
}
return sdkerrors.Wrapf(ErrInvalid, "unknown type: %d", v)
}
func (v AccessConfig) ValidateBasic() error {
@ -87,10 +134,11 @@ func (v AccessConfig) ValidateBasic() error {
if len(v.Address) != 0 {
return sdkerrors.Wrap(ErrInvalid, "address not allowed for this type")
}
return nil
case OnlyAddress:
return sdk.VerifyAddressFormat(v.Address)
}
return sdkerrors.Wrap(ErrInvalid, "unknown type")
return sdkerrors.Wrapf(ErrInvalid, "unknown type: %d", v.Type)
}
func (v AccessConfig) Allowed(actor sdk.AccAddress) bool {

View File

@ -0,0 +1,88 @@
package types
import (
"testing"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/require"
)
func TestValidateParams(t *testing.T) {
var (
anyAddress = make([]byte, sdk.AddrLen)
invalidAddress = make([]byte, sdk.AddrLen-1)
)
specs := map[string]struct {
src Params
expErr bool
}{
"all good with defaults": {
src: DefaultParams(),
},
"all good with nobody": {
src: Params{
UploadAccess: AllowNobody,
InstantiateDefaultPermission: Nobody,
},
},
"all good with everybody": {
src: Params{
UploadAccess: AllowEverybody,
InstantiateDefaultPermission: Everybody,
},
},
"all good with only address": {
src: Params{
UploadAccess: OnlyAddress.With(anyAddress),
InstantiateDefaultPermission: OnlyAddress,
},
},
"reject empty type in instantiate permission": {
src: Params{
UploadAccess: AllowNobody,
InstantiateDefaultPermission: 0,
},
expErr: true,
},
"reject unknown type in instantiate": {
src: Params{
UploadAccess: AllowNobody,
InstantiateDefaultPermission: 4,
},
expErr: true,
},
"reject invalid address in only address": {
src: Params{
UploadAccess: AccessConfig{Type: OnlyAddress, Address: invalidAddress},
InstantiateDefaultPermission: OnlyAddress,
},
expErr: true,
},
"reject AccessConfig Everybody with obsolete address": {
src: Params{
UploadAccess: AccessConfig{Type: Everybody, Address: anyAddress},
InstantiateDefaultPermission: OnlyAddress,
},
expErr: true,
},
"reject AccessConfig Nobody with obsolete address": {
src: Params{
UploadAccess: AccessConfig{Type: Nobody, Address: anyAddress},
InstantiateDefaultPermission: OnlyAddress,
},
expErr: true,
},
}
for msg, spec := range specs {
t.Run(msg, func(t *testing.T) {
err := spec.src.ValidateBasic()
if spec.expErr {
require.Error(t, err)
} else {
require.NoError(t, err)
}
})
}
}

View File

@ -73,6 +73,8 @@ type StoreCodeProposal struct {
Source string `json:"source" yaml:"source"`
// Builder is a valid docker image name with tag, optional
Builder string `json:"builder" yaml:"builder"`
// InstantiatePermission to apply on contract creation, optional
InstantiatePermission *AccessConfig `json:"instantiate_permission" yaml:"instantiate_permission"`
}
// ProposalType returns the type
@ -98,7 +100,11 @@ func (p StoreCodeProposal) ValidateBasic() error {
if err := validateBuilder(p.Builder); err != nil {
return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "builder %s", err.Error())
}
if p.InstantiatePermission != nil {
if err := p.InstantiatePermission.ValidateBasic(); err != nil {
return sdkerrors.Wrap(err, "instantiate permission")
}
}
return nil
}

View File

@ -90,6 +90,7 @@ func TestValidateWasmProposal(t *testing.T) {
func TestValidateStoreCodeProposal(t *testing.T) {
var (
anyAddress sdk.AccAddress = bytes.Repeat([]byte{0x0}, sdk.AddrLen)
invalidAddress sdk.AccAddress = bytes.Repeat([]byte{0x1}, sdk.AddrLen-1)
)
@ -100,6 +101,13 @@ func TestValidateStoreCodeProposal(t *testing.T) {
"all good": {
src: StoreCodeProposalFixture(),
},
"with instantiate permission": {
src: StoreCodeProposalFixture(func(p *StoreCodeProposal) {
accessConfig := OnlyAddress.With(anyAddress)
p.InstantiatePermission = &accessConfig
}),
},
"without source": {
src: StoreCodeProposalFixture(func(p *StoreCodeProposal) {
p.Source = ""
@ -147,6 +155,12 @@ func TestValidateStoreCodeProposal(t *testing.T) {
}),
expErr: true,
},
"with invalid instantiate permission": {
src: StoreCodeProposalFixture(func(p *StoreCodeProposal) {
p.InstantiatePermission = &AccessConfig{}
}),
expErr: true,
},
}
for msg, spec := range specs {
t.Run(msg, func(t *testing.T) {

View File

@ -17,6 +17,7 @@ func GenesisFixture(mutators ...func(*GenesisState)) GenesisState {
)
fixture := GenesisState{
Params: DefaultParams(),
Codes: make([]Code, numCodes),
Contracts: make([]Contract, numContracts),
Sequences: make([]Sequence, numSequences),

View File

@ -30,10 +30,11 @@ func (m Model) ValidateBasic() error {
// CodeInfo is data for the uploaded contract WASM code
type CodeInfo struct {
CodeHash []byte `json:"code_hash"`
Creator sdk.AccAddress `json:"creator"`
Source string `json:"source"`
Builder string `json:"builder"`
CodeHash []byte `json:"code_hash"`
Creator sdk.AccAddress `json:"creator"`
Source string `json:"source"`
Builder string `json:"builder"`
InstantiateConfig AccessConfig `json:"instantiate_config"`
}
func (c CodeInfo) ValidateBasic() error {
@ -53,12 +54,13 @@ func (c CodeInfo) ValidateBasic() error {
}
// NewCodeInfo fills a new Contract struct
func NewCodeInfo(codeHash []byte, creator sdk.AccAddress, source string, builder string) CodeInfo {
func NewCodeInfo(codeHash []byte, creator sdk.AccAddress, source string, builder string, instantiatePermission AccessConfig) CodeInfo {
return CodeInfo{
CodeHash: codeHash,
Creator: creator,
Source: source,
Builder: builder,
CodeHash: codeHash,
Creator: creator,
Source: source,
Builder: builder,
InstantiateConfig: instantiatePermission,
}
}

View File

@ -37,7 +37,9 @@ func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) {
// DefaultGenesis returns default genesis state as raw bytes for the wasm
// module.
func (AppModuleBasic) DefaultGenesis() json.RawMessage {
return ModuleCdc.MustMarshalJSON(&GenesisState{})
return ModuleCdc.MustMarshalJSON(&GenesisState{
Params: DefaultParams(),
})
}
// ValidateGenesis performs genesis state validation for the wasm module.