mirror of https://github.com/certusone/wasmd.git
Add configurable control for code instantiation
This commit is contained in:
parent
85a404691a
commit
522ba535cb
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue