Add configurable control for code instantiation

This commit is contained in:
Alex Peters 2020-07-08 16:58:35 +02:00
parent 85a404691a
commit 522ba535cb
No known key found for this signature in database
GPG Key ID: BD28388D49EE708D
8 changed files with 148 additions and 16 deletions

View File

@ -37,6 +37,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
## [Unreleased]
### Features
* (wasmd) [\#163](https://github.com/CosmWasm/wasmd/issues/163) Control who can instantiate code
* (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

View File

@ -165,6 +165,9 @@ func (k Keeper) importCode(ctx sdk.Context, codeID uint64, codeInfo types.CodeIn
// Instantiate creates an instance of a WASM contract
func (k Keeper) Instantiate(ctx sdk.Context, codeID uint64, creator, admin sdk.AccAddress, initMsg []byte, label string, deposit sdk.Coins) (sdk.AccAddress, error) {
return k.instantiate(ctx, codeID, creator, admin, initMsg, label, deposit, k.authZPolicy)
}
func (k Keeper) instantiate(ctx sdk.Context, codeID uint64, creator, admin sdk.AccAddress, initMsg []byte, label string, deposit sdk.Coins, authZ AuthorizationPolicy) (sdk.AccAddress, error) {
ctx.GasMeter().ConsumeGas(InstanceCost, "Loading CosmWasm module: init")
// create contract address
@ -196,6 +199,10 @@ func (k Keeper) Instantiate(ctx sdk.Context, codeID uint64, creator, admin sdk.A
var codeInfo types.CodeInfo
k.cdc.MustUnmarshalBinaryBare(bz, &codeInfo)
if !authZ.CanInstantiateContract(codeInfo.InstantiateConfig, creator) {
return nil, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "can not instantiate")
}
// prepare params for contract instantiate call
params := types.NewEnv(ctx, creator, deposit, contractAddress)

View File

@ -53,6 +53,59 @@ func TestCreate(t *testing.T) {
require.Equal(t, wasmCode, storedCode)
}
func TestCreateStoresInstantiatePermission(t *testing.T) {
wasmCode, err := ioutil.ReadFile("./testdata/contract.wasm")
require.NoError(t, err)
var (
deposit = sdk.NewCoins(sdk.NewInt64Coin("denom", 100000))
myAddr = bytes.Repeat([]byte{1}, sdk.AddrLen)
)
specs := map[string]struct {
srcPermission types.AccessType
expInstConf types.AccessConfig
}{
"default": {
srcPermission: types.DefaultParams().DefaultInstantiatePermission,
expInstConf: types.AllowEverybody,
},
"everybody": {
srcPermission: types.Everybody,
expInstConf: types.AllowEverybody,
},
"nobody": {
srcPermission: types.Nobody,
expInstConf: types.AllowNobody,
},
"onlyAddress with matching address": {
srcPermission: types.OnlyAddress,
expInstConf: types.AccessConfig{Type: types.OnlyAddress, Address: myAddr},
},
}
for msg, spec := range specs {
t.Run(msg, func(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
keeper.setParams(ctx, types.Params{
UploadAccess: types.AllowEverybody,
DefaultInstantiatePermission: spec.srcPermission,
})
fundAccounts(ctx, accKeeper, myAddr, deposit)
codeID, err := keeper.Create(ctx, myAddr, wasmCode, "https://github.com/CosmWasm/wasmd/blob/master/x/wasm/testdata/escrow.wasm", "cosmwasm-opt:0.5.2", nil)
require.NoError(t, err)
codeInfo := keeper.GetCodeInfo(ctx, codeID)
require.NotNil(t, codeInfo)
assert.True(t, spec.expInstConf.Equals(codeInfo.InstantiateConfig), "got %#v", codeInfo.InstantiateConfig)
})
}
}
func TestCreateWithParamPermissions(t *testing.T) {
tempDir, err := ioutil.TempDir("", "wasm")
require.NoError(t, err)
@ -263,6 +316,70 @@ func TestInstantiate(t *testing.T) {
assert.Equal(t, info.Label, "demo contract 1")
}
func TestInstantiateWithPermissions(t *testing.T) {
wasmCode, err := ioutil.ReadFile("./testdata/contract.wasm")
require.NoError(t, err)
var (
deposit = sdk.NewCoins(sdk.NewInt64Coin("denom", 100000))
myAddr = bytes.Repeat([]byte{1}, sdk.AddrLen)
otherAddr = bytes.Repeat([]byte{2}, sdk.AddrLen)
anyAddr = bytes.Repeat([]byte{3}, sdk.AddrLen)
)
initMsg := InitMsg{
Verifier: anyAddr,
Beneficiary: anyAddr,
}
initMsgBz, err := json.Marshal(initMsg)
require.NoError(t, err)
specs := map[string]struct {
srcPermission types.AccessConfig
srcActor sdk.AccAddress
expError *sdkerrors.Error
}{
"default": {
srcPermission: types.DefaultUploadAccess,
srcActor: anyAddr,
},
"everybody": {
srcPermission: types.AllowEverybody,
srcActor: anyAddr,
},
"nobody": {
srcPermission: types.AllowNobody,
srcActor: myAddr,
expError: sdkerrors.ErrUnauthorized,
},
"onlyAddress with matching address": {
srcPermission: types.OnlyAddress.With(myAddr),
srcActor: myAddr,
},
"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) {
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
fundAccounts(ctx, accKeeper, spec.srcActor, deposit)
contractID, err := keeper.Create(ctx, myAddr, wasmCode, "https://github.com/CosmWasm/wasmd/blob/master/x/wasm/testdata/escrow.wasm", "", &spec.srcPermission)
require.NoError(t, err)
_, err = keeper.Instantiate(ctx, contractID, spec.srcActor, nil, initMsgBz, "demo contract 1", nil)
assert.True(t, spec.expError.Is(err), "got %+v", err)
})
}
}
func TestInstantiateWithNonExistingCodeID(t *testing.T) {
tempDir, err := ioutil.TempDir("", "wasm")
require.NoError(t, err)
@ -898,11 +1015,14 @@ type InitMsg struct {
func createFakeFundedAccount(ctx sdk.Context, am auth.AccountKeeper, coins sdk.Coins) sdk.AccAddress {
_, _, addr := keyPubAddr()
fundAccounts(ctx, am, addr, coins)
return addr
}
func fundAccounts(ctx sdk.Context, am auth.AccountKeeper, addr sdk.AccAddress, coins sdk.Coins) {
baseAcct := auth.NewBaseAccountWithAddress(addr)
_ = baseAcct.SetCoins(coins)
am.SetAccount(ctx, &baseAcct)
return addr
}
var keyCounter uint64 = 0

View File

@ -67,7 +67,7 @@ func handleInstantiateProposal(ctx sdk.Context, k Keeper, p types.InstantiateCon
return err
}
contractAddr, err := k.Instantiate(ctx, p.Code, p.Creator, p.Admin, p.InitMsg, p.Label, p.InitFunds)
contractAddr, err := k.instantiate(ctx, p.Code, p.Creator, p.Admin, p.InitMsg, p.Label, p.InitFunds, GovAuthorizationPolicy{})
if err != nil {
return err
}

View File

@ -23,7 +23,7 @@ func TestStoreCodeProposal(t *testing.T) {
ctx, keepers := CreateTestInput(t, false, tempDir, "staking", nil, nil)
govKeeper, wasmKeeper := keepers.GovKeeper, keepers.WasmKeeper
wasmKeeper.setParams(ctx, types.Params{UploadAccess: types.AllowNobody, DefaultInstantiatePermission: types.Nobody})
wasmCode, err := ioutil.ReadFile("./testdata/contract.wasm")
require.NoError(t, err)
@ -64,6 +64,7 @@ func TestInstantiateProposal(t *testing.T) {
ctx, keepers := CreateTestInput(t, false, tempDir, "staking", nil, nil)
govKeeper, wasmKeeper := keepers.GovKeeper, keepers.WasmKeeper
wasmKeeper.setParams(ctx, types.Params{UploadAccess: types.AllowNobody, DefaultInstantiatePermission: types.Nobody})
wasmCode, err := ioutil.ReadFile("./testdata/contract.wasm")
require.NoError(t, err)
@ -113,6 +114,7 @@ func TestMigrateProposal(t *testing.T) {
ctx, keepers := CreateTestInput(t, false, tempDir, "staking", nil, nil)
govKeeper, wasmKeeper := keepers.GovKeeper, keepers.WasmKeeper
wasmKeeper.setParams(ctx, types.Params{UploadAccess: types.AllowNobody, DefaultInstantiatePermission: types.Nobody})
wasmCode, err := ioutil.ReadFile("./testdata/contract.wasm")
require.NoError(t, err)
@ -248,6 +250,7 @@ func TestAdminProposals(t *testing.T) {
defer os.RemoveAll(tempDir)
ctx, keepers := CreateTestInput(t, false, tempDir, "staking", nil, nil)
govKeeper, wasmKeeper := keepers.GovKeeper, keepers.WasmKeeper
wasmKeeper.setParams(ctx, types.Params{UploadAccess: types.AllowNobody, DefaultInstantiatePermission: types.Nobody})
codeInfoFixture := types.CodeInfoFixture(types.WithSHA256CodeHash(wasmCode))
require.NoError(t, wasmKeeper.importCode(ctx, 1, codeInfoFixture, wasmCode))

View File

@ -62,7 +62,7 @@ var (
// 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"`
DefaultInstantiatePermission AccessType `json:"instantiate_default_permission" yaml:"instantiate_default_permission"`
}
// ParamKeyTable returns the parameter key table.
@ -74,7 +74,7 @@ func ParamKeyTable() params.KeyTable {
func DefaultParams() Params {
return Params{
UploadAccess: AllowEverybody,
InstantiateDefaultPermission: Everybody,
DefaultInstantiatePermission: Everybody,
}
}
@ -87,13 +87,13 @@ func (p Params) String() string {
func (p *Params) ParamSetPairs() params.ParamSetPairs {
return params.ParamSetPairs{
params.NewParamSetPair(ParamStoreKeyUploadAccess, &p.UploadAccess, validateAccessConfig),
params.NewParamSetPair(ParamStoreKeyInstantiateAccess, &p.InstantiateDefaultPermission, validateAccessType),
params.NewParamSetPair(ParamStoreKeyInstantiateAccess, &p.DefaultInstantiatePermission, validateAccessType),
}
}
// ValidateBasic performs basic validation on wasm parameters
func (p Params) ValidateBasic() error {
if err := validateAccessType(p.InstantiateDefaultPermission); err != nil {
if err := validateAccessType(p.DefaultInstantiatePermission); err != nil {
return errors.Wrap(err, "instantiate default permission")
}
if err := validateAccessConfig(p.UploadAccess); err != nil {

View File

@ -23,53 +23,53 @@ func TestValidateParams(t *testing.T) {
"all good with nobody": {
src: Params{
UploadAccess: AllowNobody,
InstantiateDefaultPermission: Nobody,
DefaultInstantiatePermission: Nobody,
},
},
"all good with everybody": {
src: Params{
UploadAccess: AllowEverybody,
InstantiateDefaultPermission: Everybody,
DefaultInstantiatePermission: Everybody,
},
},
"all good with only address": {
src: Params{
UploadAccess: OnlyAddress.With(anyAddress),
InstantiateDefaultPermission: OnlyAddress,
DefaultInstantiatePermission: OnlyAddress,
},
},
"reject empty type in instantiate permission": {
src: Params{
UploadAccess: AllowNobody,
InstantiateDefaultPermission: 0,
DefaultInstantiatePermission: 0,
},
expErr: true,
},
"reject unknown type in instantiate": {
src: Params{
UploadAccess: AllowNobody,
InstantiateDefaultPermission: 4,
DefaultInstantiatePermission: 4,
},
expErr: true,
},
"reject invalid address in only address": {
src: Params{
UploadAccess: AccessConfig{Type: OnlyAddress, Address: invalidAddress},
InstantiateDefaultPermission: OnlyAddress,
DefaultInstantiatePermission: OnlyAddress,
},
expErr: true,
},
"reject AccessConfig Everybody with obsolete address": {
src: Params{
UploadAccess: AccessConfig{Type: Everybody, Address: anyAddress},
InstantiateDefaultPermission: OnlyAddress,
DefaultInstantiatePermission: OnlyAddress,
},
expErr: true,
},
"reject AccessConfig Nobody with obsolete address": {
src: Params{
UploadAccess: AccessConfig{Type: Nobody, Address: anyAddress},
InstantiateDefaultPermission: OnlyAddress,
DefaultInstantiatePermission: OnlyAddress,
},
expErr: true,
},

View File

@ -394,6 +394,7 @@ func TestValidateUpdateAdminProposal(t *testing.T) {
})
}
}
func TestValidateClearAdminProposal(t *testing.T) {
var (
invalidAddress sdk.AccAddress = bytes.Repeat([]byte{0x1}, sdk.AddrLen-1)