Support self calling contract on instantiation (#300)

* Support self calling contract on instantiation

* Review feedback

* Review feedback
This commit is contained in:
Alexander Peters
2020-11-09 09:16:41 +01:00
committed by GitHub
parent fbd7168cc1
commit 4fb3a50fa7
15 changed files with 307 additions and 97 deletions

View File

@@ -422,7 +422,7 @@ func NewWasmApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest b
distr.NewAppModule(appCodec, app.distrKeeper, app.accountKeeper, app.bankKeeper, app.stakingKeeper),
staking.NewAppModule(appCodec, app.stakingKeeper, app.accountKeeper, app.bankKeeper),
upgrade.NewAppModule(app.upgradeKeeper),
wasm.NewAppModule(app.wasmKeeper),
wasm.NewAppModule(&app.wasmKeeper),
evidence.NewAppModule(app.evidenceKeeper),
ibc.NewAppModule(app.ibcKeeper),
params.NewAppModule(app.paramsKeeper),
@@ -472,7 +472,7 @@ func NewWasmApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest b
distr.NewAppModule(appCodec, app.distrKeeper, app.accountKeeper, app.bankKeeper, app.stakingKeeper),
slashing.NewAppModule(appCodec, app.slashingKeeper, app.accountKeeper, app.bankKeeper, app.stakingKeeper),
params.NewAppModule(app.paramsKeeper),
wasm.NewAppModule(app.wasmKeeper),
wasm.NewAppModule(&app.wasmKeeper),
evidence.NewAppModule(app.evidenceKeeper),
ibc.NewAppModule(app.ibcKeeper),
transferModule,

View File

@@ -118,14 +118,14 @@ func TestInitGenesis(t *testing.T) {
})
// export into genstate
genState := ExportGenesis(data.ctx, data.keeper)
genState := ExportGenesis(data.ctx, &data.keeper)
// create new app to import genstate into
newData := setupTest(t)
q2 := newData.module.LegacyQuerierHandler(nil)
// initialize new app with genstate
InitGenesis(newData.ctx, newData.keeper, *genState)
InitGenesis(newData.ctx, &newData.keeper, *genState)
// run same checks again on newdata, to make sure it was reinitialized correctly
assertCodeList(t, q2, newData.ctx, 1)

View File

@@ -10,7 +10,7 @@ import (
)
// NewHandler returns a handler for "bank" type messages.
func NewHandler(k Keeper) sdk.Handler {
func NewHandler(k *Keeper) sdk.Handler {
return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) {
ctx = ctx.WithEventManager(sdk.NewEventManager())
@@ -47,7 +47,7 @@ func filteredMessageEvents(manager *sdk.EventManager) []abci.Event {
return res
}
func handleStoreCode(ctx sdk.Context, k Keeper, msg *MsgStoreCode) (*sdk.Result, error) {
func handleStoreCode(ctx sdk.Context, k *Keeper, msg *MsgStoreCode) (*sdk.Result, error) {
err := msg.ValidateBasic()
if err != nil {
return nil, err
@@ -73,7 +73,7 @@ func handleStoreCode(ctx sdk.Context, k Keeper, msg *MsgStoreCode) (*sdk.Result,
}, nil
}
func handleInstantiate(ctx sdk.Context, k Keeper, msg *MsgInstantiateContract) (*sdk.Result, error) {
func handleInstantiate(ctx sdk.Context, k *Keeper, msg *MsgInstantiateContract) (*sdk.Result, error) {
contractAddr, err := k.Instantiate(ctx, msg.CodeID, msg.Sender, msg.Admin, msg.InitMsg, msg.Label, msg.InitFunds)
if err != nil {
return nil, err
@@ -95,7 +95,7 @@ func handleInstantiate(ctx sdk.Context, k Keeper, msg *MsgInstantiateContract) (
}, nil
}
func handleExecute(ctx sdk.Context, k Keeper, msg *MsgExecuteContract) (*sdk.Result, error) {
func handleExecute(ctx sdk.Context, k *Keeper, msg *MsgExecuteContract) (*sdk.Result, error) {
res, err := k.Execute(ctx, msg.Contract, msg.Sender, msg.Msg, msg.SentFunds)
if err != nil {
return nil, err
@@ -115,7 +115,7 @@ func handleExecute(ctx sdk.Context, k Keeper, msg *MsgExecuteContract) (*sdk.Res
return res, nil
}
func handleMigration(ctx sdk.Context, k Keeper, msg *MsgMigrateContract) (*sdk.Result, error) {
func handleMigration(ctx sdk.Context, k *Keeper, msg *MsgMigrateContract) (*sdk.Result, error) {
res, err := k.Migrate(ctx, msg.Contract, msg.Sender, msg.CodeID, msg.MigrateMsg)
if err != nil {
return nil, err
@@ -133,7 +133,7 @@ func handleMigration(ctx sdk.Context, k Keeper, msg *MsgMigrateContract) (*sdk.R
return res, nil
}
func handleUpdateContractAdmin(ctx sdk.Context, k Keeper, msg *MsgUpdateAdmin) (*sdk.Result, error) {
func handleUpdateContractAdmin(ctx sdk.Context, k *Keeper, msg *MsgUpdateAdmin) (*sdk.Result, error) {
if err := k.UpdateContractAdmin(ctx, msg.Contract, msg.Sender, msg.NewAdmin); err != nil {
return nil, err
}
@@ -149,7 +149,7 @@ func handleUpdateContractAdmin(ctx sdk.Context, k Keeper, msg *MsgUpdateAdmin) (
}, nil
}
func handleClearContractAdmin(ctx sdk.Context, k Keeper, msg *MsgClearAdmin) (*sdk.Result, error) {
func handleClearContractAdmin(ctx sdk.Context, k *Keeper, msg *MsgClearAdmin) (*sdk.Result, error) {
if err := k.ClearContractAdmin(ctx, msg.Contract, msg.Sender); err != nil {
return nil, err
}

View File

@@ -11,7 +11,7 @@ import (
// InitGenesis sets supply information for genesis.
//
// CONTRACT: all types of accounts must have been already initialized/created
func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) error {
func InitGenesis(ctx sdk.Context, keeper *Keeper, data types.GenesisState) error {
var maxCodeID uint64
for i, code := range data.Codes {
err := keeper.importCode(ctx, code.CodeID, code.CodeInfo, code.CodeBytes)
@@ -52,7 +52,7 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) error
}
// ExportGenesis returns a GenesisState for a given context and keeper.
func ExportGenesis(ctx sdk.Context, keeper Keeper) *types.GenesisState {
func ExportGenesis(ctx sdk.Context, keeper *Keeper) *types.GenesisState {
var genState types.GenesisState
genState.Params = keeper.GetParams(ctx)

View File

@@ -473,7 +473,7 @@ func TestImportContractWithCodeHistoryReset(t *testing.T) {
assert.Equal(t, expHistory, keeper.GetContractHistory(ctx, contractAddr).CodeHistoryEntries)
}
func setupKeeper(t *testing.T) (Keeper, sdk.Context, []sdk.StoreKey, func()) {
func setupKeeper(t *testing.T) (*Keeper, sdk.Context, []sdk.StoreKey, func()) {
t.Helper()
tempDir, err := ioutil.TempDir("", "wasm")
require.NoError(t, err)
@@ -504,5 +504,5 @@ func setupKeeper(t *testing.T) (Keeper, sdk.Context, []sdk.StoreKey, func()) {
srcKeeper := NewKeeper(encodingConfig.Marshaler, keyWasm, pk.Subspace(wasmTypes.DefaultParamspace), authkeeper.AccountKeeper{}, nil, stakingkeeper.Keeper{}, distributionkeeper.Keeper{}, nil, tempDir, wasmConfig, "", nil, nil)
srcKeeper.setParams(ctx, wasmTypes.DefaultParams())
return srcKeeper, ctx, []sdk.StoreKey{keyWasm, keyParams}, cleanup
return &srcKeeper, ctx, []sdk.StoreKey{keyWasm, keyParams}, cleanup
}

View File

@@ -47,7 +47,7 @@ type Keeper struct {
accountKeeper authkeeper.AccountKeeper
bankKeeper bankkeeper.Keeper
wasmer wasm.Wasmer
wasmer types.WasmerEngine
queryPlugins QueryPlugins
messenger MessageHandler
// queryGasLimit is the max wasm gas that can be spent on executing a query with a contract
@@ -86,7 +86,7 @@ func NewKeeper(
keeper := Keeper{
storeKey: storeKey,
cdc: cdc,
wasmer: *wasmer,
wasmer: wasmer,
accountKeeper: accountKeeper,
bankKeeper: bankKeeper,
messenger: NewMessageHandler(router, customEncoders),
@@ -254,16 +254,18 @@ func (k Keeper) instantiate(ctx sdk.Context, codeID uint64, creator, admin sdk.A
events := types.ParseEvents(res.Attributes, contractAddress)
ctx.EventManager().EmitEvents(events)
// persist instance first
createdAt := types.NewAbsoluteTxPosition(ctx)
instance := types.NewContractInfo(codeID, creator, admin, label, createdAt)
store.Set(types.GetContractAddressKey(contractAddress), k.cdc.MustMarshalBinaryBare(&instance))
k.appendToContractHistory(ctx, contractAddress, instance.InitialHistory(initMsg))
// then dispatch so that contract could be called back
err = k.dispatchMessages(ctx, contractAddress, res.Messages)
if err != nil {
return nil, err
}
// persist instance
createdAt := types.NewAbsoluteTxPosition(ctx)
instance := types.NewContractInfo(codeID, creator, admin, label, createdAt)
store.Set(types.GetContractAddressKey(contractAddress), k.cdc.MustMarshalBinaryBare(&instance))
k.appendToContractHistory(ctx, contractAddress, instance.InitialHistory(initMsg))
return contractAddress, nil
}

View File

@@ -432,6 +432,21 @@ func TestInstantiateWithNonExistingCodeID(t *testing.T) {
require.Nil(t, addr)
}
func TestInstantiateWithCallbackToContract(t *testing.T) {
ctx, keepers := CreateTestInput(t, false, SupportedFeatures, nil, nil)
var (
executeCalled bool
err error
)
wasmerMock := selfCallingInstMockWasmer(&executeCalled)
keepers.WasmKeeper.wasmer = wasmerMock
example := StoreHackatomExampleContract(t, ctx, keepers)
_, err = keepers.WasmKeeper.Instantiate(ctx, example.CodeID, example.CreatorAddr, nil, nil, "test", nil)
require.NoError(t, err)
assert.True(t, executeCalled)
}
func TestExecute(t *testing.T) {
ctx, keepers := CreateTestInput(t, false, SupportedFeatures, nil, nil)
accKeeper, keeper, bankKeeper := keepers.AccountKeeper, keepers.WasmKeeper, keepers.BankKeeper

View File

@@ -27,7 +27,7 @@ const (
)
// NewLegacyQuerier creates a new querier
func NewLegacyQuerier(keeper Keeper) sdk.Querier {
func NewLegacyQuerier(keeper *Keeper) sdk.Querier {
return func(ctx sdk.Context, path []string, req abci.RequestQuery) ([]byte, error) {
var (
rsp interface{}
@@ -39,13 +39,13 @@ func NewLegacyQuerier(keeper Keeper) sdk.Querier {
if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, err.Error())
}
rsp, err = queryContractInfo(ctx, addr, keeper)
rsp, err = queryContractInfo(ctx, addr, *keeper)
case QueryListContractByCode:
codeID, err := strconv.ParseUint(path[1], 10, 64)
if err != nil {
return nil, sdkerrors.Wrapf(types.ErrInvalid, "code id: %s", err.Error())
}
rsp, err = queryContractListByCode(ctx, codeID, keeper)
rsp, err = queryContractListByCode(ctx, codeID, *keeper)
case QueryGetContractState:
if len(path) < 3 {
return nil, sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "unknown data query endpoint")
@@ -58,13 +58,13 @@ func NewLegacyQuerier(keeper Keeper) sdk.Querier {
}
rsp, err = queryCode(ctx, codeID, keeper)
case QueryListCode:
rsp, err = queryCodeList(ctx, keeper)
rsp, err = queryCodeList(ctx, *keeper)
case QueryContractHistory:
contractAddr, err := sdk.AccAddressFromBech32(path[1])
if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, err.Error())
}
rsp, err = queryContractHistory(ctx, contractAddr, keeper)
rsp, err = queryContractHistory(ctx, contractAddr, *keeper)
default:
return nil, sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "unknown data query endpoint")
}
@@ -82,7 +82,7 @@ func NewLegacyQuerier(keeper Keeper) sdk.Querier {
}
}
func queryContractState(ctx sdk.Context, bech, queryMethod string, data []byte, keeper Keeper) (json.RawMessage, error) {
func queryContractState(ctx sdk.Context, bech, queryMethod string, data []byte, keeper *Keeper) (json.RawMessage, error) {
contractAddr, err := sdk.AccAddressFromBech32(bech)
if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, bech)

View File

@@ -11,11 +11,11 @@ import (
)
type grpcQuerier struct {
keeper Keeper
keeper *Keeper
}
// todo: this needs proper tests and doc
func NewQuerier(keeper Keeper) grpcQuerier {
func NewQuerier(keeper *Keeper) grpcQuerier {
return grpcQuerier{keeper: keeper}
}
@@ -23,7 +23,7 @@ func (q grpcQuerier) ContractInfo(c context.Context, req *types.QueryContractInf
if err := sdk.VerifyAddressFormat(req.Address); err != nil {
return nil, err
}
rsp, err := queryContractInfo(sdk.UnwrapSDKContext(c), req.Address, q.keeper)
rsp, err := queryContractInfo(sdk.UnwrapSDKContext(c), req.Address, *q.keeper)
switch {
case err != nil:
return nil, err
@@ -40,7 +40,7 @@ func (q grpcQuerier) ContractHistory(c context.Context, req *types.QueryContract
if err := sdk.VerifyAddressFormat(req.Address); err != nil {
return nil, err
}
rsp, err := queryContractHistory(sdk.UnwrapSDKContext(c), req.Address, q.keeper)
rsp, err := queryContractHistory(sdk.UnwrapSDKContext(c), req.Address, *q.keeper)
switch {
case err != nil:
return nil, err
@@ -56,7 +56,7 @@ func (q grpcQuerier) ContractsByCode(c context.Context, req *types.QueryContract
if req.CodeId == 0 {
return nil, sdkerrors.Wrap(types.ErrInvalid, "code id")
}
rsp, err := queryContractListByCode(sdk.UnwrapSDKContext(c), req.CodeId, q.keeper)
rsp, err := queryContractListByCode(sdk.UnwrapSDKContext(c), req.CodeId, *q.keeper)
switch {
case err != nil:
return nil, err
@@ -134,7 +134,7 @@ func (q grpcQuerier) Code(c context.Context, req *types.QueryCodeRequest) (*type
}
func (q grpcQuerier) Codes(c context.Context, _ *empty.Empty) (*types.QueryCodesResponse, error) {
rsp, err := queryCodeList(sdk.UnwrapSDKContext(c), q.keeper)
rsp, err := queryCodeList(sdk.UnwrapSDKContext(c), *q.keeper)
switch {
case err != nil:
return nil, err
@@ -182,7 +182,7 @@ func queryContractListByCode(ctx sdk.Context, codeID uint64, keeper Keeper) ([]t
return contracts, nil
}
func queryCode(ctx sdk.Context, codeID uint64, keeper Keeper) (*types.QueryCodeResponse, error) {
func queryCode(ctx sdk.Context, codeID uint64, keeper *Keeper) (*types.QueryCodeResponse, error) {
if codeID == 0 {
return nil, nil
}

View File

@@ -36,7 +36,7 @@ type recurseResponse struct {
// number os wasm queries called from a contract
var totalWasmQueryCounter int
func initRecurseContract(t *testing.T) (contract sdk.AccAddress, creator sdk.AccAddress, ctx sdk.Context, keeper Keeper) {
func initRecurseContract(t *testing.T) (contract sdk.AccAddress, creator sdk.AccAddress, ctx sdk.Context, keeper *Keeper) {
// we do one basic setup before all test cases (which are read-only and don't change state)
var realWasmQuerier func(ctx sdk.Context, request *wasmTypes.WasmQuery) ([]byte, error)
countingQuerier := &QueryPlugins{
@@ -48,7 +48,7 @@ func initRecurseContract(t *testing.T) (contract sdk.AccAddress, creator sdk.Acc
ctx, keepers := CreateTestInput(t, false, SupportedFeatures, nil, countingQuerier)
keeper = keepers.WasmKeeper
realWasmQuerier = WasmQuerier(&keeper)
realWasmQuerier = WasmQuerier(keeper)
exampleContract := InstantiateHackatomExampleContract(t, ctx, keepers)
return exampleContract.Contract, exampleContract.CreatorAddr, ctx, keeper

View File

@@ -213,7 +213,7 @@ func initializeStaking(t *testing.T) initInfo {
ctx: ctx,
accKeeper: accKeeper,
stakingKeeper: stakingKeeper,
wasmKeeper: keeper,
wasmKeeper: *keeper,
distKeeper: k.DistKeeper,
bankKeeper: bankKeeper,
}

View File

@@ -1,6 +1,7 @@
package keeper
import (
"bytes"
"encoding/binary"
"encoding/json"
"fmt"
@@ -9,11 +10,12 @@ import (
"testing"
"time"
wasmTypes "github.com/CosmWasm/wasmd/x/wasm/internal/types"
wasmtypes "github.com/CosmWasm/wasmd/x/wasm/internal/types"
"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/baseapp"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/codec/types"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
params2 "github.com/cosmos/cosmos-sdk/simapp/params"
"github.com/cosmos/cosmos-sdk/std"
"github.com/cosmos/cosmos-sdk/store"
@@ -87,7 +89,7 @@ func MakeTestCodec() codec.Marshaler {
}
func MakeEncodingConfig() params2.EncodingConfig {
amino := codec.NewLegacyAmino()
interfaceRegistry := types.NewInterfaceRegistry()
interfaceRegistry := codectypes.NewInterfaceRegistry()
marshaler := codec.NewProtoCodec(interfaceRegistry)
txCfg := tx.NewTxConfig(marshaler, tx.DefaultSignModes)
@@ -115,10 +117,10 @@ var TestingStakeParams = stakingtypes.Params{
type TestKeepers struct {
AccountKeeper authkeeper.AccountKeeper
StakingKeeper stakingkeeper.Keeper
WasmKeeper Keeper
DistKeeper distributionkeeper.Keeper
BankKeeper bankkeeper.Keeper
GovKeeper govkeeper.Keeper
WasmKeeper *Keeper
}
// encoders can be nil to accept the defaults, or set it to override some of the message handlers (like default)
@@ -127,7 +129,7 @@ func CreateTestInput(t *testing.T, isCheckTx bool, supportedFeatures string, enc
require.NoError(t, err)
t.Cleanup(func() { os.RemoveAll(tempDir) })
keyWasm := sdk.NewKVStoreKey(wasmTypes.StoreKey)
keyWasm := sdk.NewKVStoreKey(types.StoreKey)
keyAcc := sdk.NewKVStoreKey(authtypes.StoreKey)
keyBank := sdk.NewKVStoreKey(banktypes.StoreKey)
keyStaking := sdk.NewKVStoreKey(stakingtypes.StoreKey)
@@ -229,12 +231,12 @@ func CreateTestInput(t *testing.T, isCheckTx bool, supportedFeatures string, enc
router.AddRoute(sdk.NewRoute(distributiontypes.RouterKey, dh))
// Load default wasm config
wasmConfig := wasmTypes.DefaultWasmConfig()
wasmConfig := types.DefaultWasmConfig()
keeper := NewKeeper(
appCodec,
keyWasm,
paramsKeeper.Subspace(wasmtypes.DefaultParamspace),
paramsKeeper.Subspace(types.DefaultParamspace),
authKeeper,
bankKeeper,
stakingKeeper,
@@ -246,15 +248,15 @@ func CreateTestInput(t *testing.T, isCheckTx bool, supportedFeatures string, enc
encoders,
queriers,
)
keeper.setParams(ctx, wasmtypes.DefaultParams())
keeper.setParams(ctx, types.DefaultParams())
// add wasm handler so we can loop-back (contracts calling contracts)
router.AddRoute(sdk.NewRoute(wasmTypes.RouterKey, TestHandler(keeper)))
router.AddRoute(sdk.NewRoute(types.RouterKey, TestHandler(&keeper)))
govRouter := govtypes.NewRouter().
AddRoute(govtypes.RouterKey, govtypes.ProposalHandler).
AddRoute(paramproposal.RouterKey, params.NewParamChangeProposalHandler(paramsKeeper)).
AddRoute(distributiontypes.RouterKey, distribution.NewCommunityPoolSpendProposalHandler(distKeeper)).
AddRoute(wasmtypes.RouterKey, NewWasmProposalHandler(keeper, wasmtypes.EnableAllProposals))
AddRoute(types.RouterKey, NewWasmProposalHandler(keeper, types.EnableAllProposals))
govKeeper := govkeeper.NewKeeper(
appCodec, keyGov, paramsKeeper.Subspace(govtypes.ModuleName).WithKeyTable(govtypes.ParamKeyTable()), authKeeper, bankKeeper, stakingKeeper, govRouter,
@@ -269,7 +271,7 @@ func CreateTestInput(t *testing.T, isCheckTx bool, supportedFeatures string, enc
AccountKeeper: authKeeper,
StakingKeeper: stakingKeeper,
DistKeeper: distKeeper,
WasmKeeper: keeper,
WasmKeeper: &keeper,
BankKeeper: bankKeeper,
GovKeeper: govKeeper,
}
@@ -277,15 +279,15 @@ func CreateTestInput(t *testing.T, isCheckTx bool, supportedFeatures string, enc
}
// TestHandler returns a wasm handler for tests (to avoid circular imports)
func TestHandler(k Keeper) sdk.Handler {
func TestHandler(k *Keeper) sdk.Handler {
return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) {
ctx = ctx.WithEventManager(sdk.NewEventManager())
switch msg := msg.(type) {
case *wasmtypes.MsgInstantiateContract:
case *types.MsgInstantiateContract:
return handleInstantiate(ctx, k, msg)
case *wasmtypes.MsgExecuteContract:
case *types.MsgExecuteContract:
return handleExecute(ctx, k, msg)
default:
@@ -295,7 +297,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 *types.MsgInstantiateContract) (*sdk.Result, error) {
contractAddr, err := k.Instantiate(ctx, msg.CodeID, msg.Sender, msg.Admin, msg.InitMsg, msg.Label, msg.InitFunds)
if err != nil {
return nil, err
@@ -307,7 +309,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 *types.MsgExecuteContract) (*sdk.Result, error) {
res, err := k.Execute(ctx, msg.Contract, msg.Sender, msg.Msg, msg.SentFunds)
if err != nil {
return nil, err
@@ -323,46 +325,13 @@ func AnyAccAddress(_ *testing.T) sdk.AccAddress {
}
type HackatomExampleContract struct {
InitialAmount sdk.Coins
Contract sdk.AccAddress
Creator crypto.PrivKey
CreatorAddr sdk.AccAddress
Verifier crypto.PrivKey
VerifierAddr sdk.AccAddress
Beneficiary crypto.PrivKey
BeneficiaryAddr sdk.AccAddress
CodeID uint64
InitialAmount sdk.Coins
Creator crypto.PrivKey
CreatorAddr sdk.AccAddress
CodeID uint64
}
// InstantiateHackatomExampleContract load and instantiate the "./testdata/hackatom.wasm" contract
func InstantiateHackatomExampleContract(t *testing.T, ctx sdk.Context, keepers TestKeepers) HackatomExampleContract {
anyAmount, creator, creatorAddr, codeID := StoreHackatomExampleContract(t, ctx, keepers)
verifier, _, verifierAddr := keyPubAddr()
fundAccounts(t, ctx, keepers.AccountKeeper, keepers.BankKeeper, verifierAddr, anyAmount)
beneficiary, _, beneficiaryAddr := keyPubAddr()
initMsgBz := HackatomExampleInitMsg{
Verifier: verifierAddr,
Beneficiary: beneficiaryAddr,
}.GetBytes(t)
initialAmount := sdk.NewCoins(sdk.NewInt64Coin("denom", 100))
contractAddr, err := keepers.WasmKeeper.Instantiate(ctx, codeID, creatorAddr, nil, initMsgBz, "demo contract to query", initialAmount)
require.NoError(t, err)
return HackatomExampleContract{
InitialAmount: initialAmount,
Contract: contractAddr,
Creator: creator,
CreatorAddr: creatorAddr,
Verifier: verifier,
VerifierAddr: verifierAddr,
Beneficiary: beneficiary,
BeneficiaryAddr: beneficiaryAddr,
CodeID: codeID,
}
}
func StoreHackatomExampleContract(t *testing.T, ctx sdk.Context, keepers TestKeepers) (sdk.Coins, crypto.PrivKey, sdk.AccAddress, uint64) {
func StoreHackatomExampleContract(t *testing.T, ctx sdk.Context, keepers TestKeepers) HackatomExampleContract {
anyAmount := sdk.NewCoins(sdk.NewInt64Coin("denom", 1000))
creator, _, creatorAddr := keyPubAddr()
fundAccounts(t, ctx, keepers.AccountKeeper, keepers.BankKeeper, creatorAddr, anyAmount)
@@ -372,7 +341,41 @@ func StoreHackatomExampleContract(t *testing.T, ctx sdk.Context, keepers TestKee
codeID, err := keepers.WasmKeeper.Create(ctx, creatorAddr, wasmCode, "", "", nil)
require.NoError(t, err)
return anyAmount, creator, creatorAddr, codeID
return HackatomExampleContract{anyAmount, creator, creatorAddr, codeID}
}
type HackatomExampleInstance struct {
HackatomExampleContract
Contract sdk.AccAddress
Verifier crypto.PrivKey
VerifierAddr sdk.AccAddress
Beneficiary crypto.PrivKey
BeneficiaryAddr sdk.AccAddress
}
// InstantiateHackatomExampleContract load and instantiate the "./testdata/hackatom.wasm" contract
func InstantiateHackatomExampleContract(t *testing.T, ctx sdk.Context, keepers TestKeepers) HackatomExampleInstance {
contract := StoreHackatomExampleContract(t, ctx, keepers)
verifier, _, verifierAddr := keyPubAddr()
fundAccounts(t, ctx, keepers.AccountKeeper, keepers.BankKeeper, verifierAddr, contract.InitialAmount)
beneficiary, _, beneficiaryAddr := keyPubAddr()
initMsgBz := HackatomExampleInitMsg{
Verifier: verifierAddr,
Beneficiary: beneficiaryAddr,
}.GetBytes(t)
initialAmount := sdk.NewCoins(sdk.NewInt64Coin("denom", 100))
contractAddr, err := keepers.WasmKeeper.Instantiate(ctx, contract.CodeID, contract.CreatorAddr, nil, initMsgBz, "demo contract to query", initialAmount)
require.NoError(t, err)
return HackatomExampleInstance{
HackatomExampleContract: contract,
Contract: contractAddr,
Verifier: verifier,
VerifierAddr: verifierAddr,
Beneficiary: beneficiary,
BeneficiaryAddr: beneficiaryAddr,
}
}
type HackatomExampleInitMsg struct {
@@ -412,3 +415,91 @@ func keyPubAddr() (crypto.PrivKey, crypto.PubKey, sdk.AccAddress) {
addr := sdk.AccAddress(pub.Address())
return key, pub, addr
}
var _ types.WasmerEngine = &MockWasmer{}
// MockWasmer implements types.WasmerEngine for testing purpose. One or multiple messages can be stubbed.
// Without a stub function a panic is thrown.
type MockWasmer struct {
CreateFn func(code cosmwasm.WasmCode) (cosmwasm.CodeID, error)
InstantiateFn func(code cosmwasm.CodeID, env wasmTypes.Env, info wasmTypes.MessageInfo, initMsg []byte, store cosmwasm.KVStore, goapi cosmwasm.GoAPI, querier cosmwasm.Querier, gasMeter cosmwasm.GasMeter, gasLimit uint64) (*wasmTypes.InitResponse, uint64, error)
ExecuteFn func(code cosmwasm.CodeID, env wasmTypes.Env, info wasmTypes.MessageInfo, executeMsg []byte, store cosmwasm.KVStore, goapi cosmwasm.GoAPI, querier cosmwasm.Querier, gasMeter cosmwasm.GasMeter, gasLimit uint64) (*wasmTypes.HandleResponse, uint64, error)
QueryFn func(code cosmwasm.CodeID, env wasmTypes.Env, queryMsg []byte, store cosmwasm.KVStore, goapi cosmwasm.GoAPI, querier cosmwasm.Querier, gasMeter cosmwasm.GasMeter, gasLimit uint64) ([]byte, uint64, error)
MigrateFn func(code cosmwasm.CodeID, env wasmTypes.Env, info wasmTypes.MessageInfo, migrateMsg []byte, store cosmwasm.KVStore, goapi cosmwasm.GoAPI, querier cosmwasm.Querier, gasMeter cosmwasm.GasMeter, gasLimit uint64) (*wasmTypes.MigrateResponse, uint64, error)
GetCodeFn func(code cosmwasm.CodeID) (cosmwasm.WasmCode, error)
CleanupFn func()
}
func (m *MockWasmer) Create(code cosmwasm.WasmCode) (cosmwasm.CodeID, error) {
if m.CreateFn == nil {
panic("not supposed to be called!")
}
return m.CreateFn(code)
}
func (m *MockWasmer) Instantiate(code cosmwasm.CodeID, env wasmTypes.Env, info wasmTypes.MessageInfo, initMsg []byte, store cosmwasm.KVStore, goapi cosmwasm.GoAPI, querier cosmwasm.Querier, gasMeter cosmwasm.GasMeter, gasLimit uint64) (*wasmTypes.InitResponse, uint64, error) {
if m.InstantiateFn == nil {
panic("not supposed to be called!")
}
return m.InstantiateFn(code, env, info, initMsg, store, goapi, querier, gasMeter, gasLimit)
}
func (m *MockWasmer) Execute(code cosmwasm.CodeID, env wasmTypes.Env, info wasmTypes.MessageInfo, executeMsg []byte, store cosmwasm.KVStore, goapi cosmwasm.GoAPI, querier cosmwasm.Querier, gasMeter cosmwasm.GasMeter, gasLimit uint64) (*wasmTypes.HandleResponse, uint64, error) {
if m.ExecuteFn == nil {
panic("not supposed to be called!")
}
return m.ExecuteFn(code, env, info, executeMsg, store, goapi, querier, gasMeter, gasLimit)
}
func (m *MockWasmer) Query(code cosmwasm.CodeID, env wasmTypes.Env, queryMsg []byte, store cosmwasm.KVStore, goapi cosmwasm.GoAPI, querier cosmwasm.Querier, gasMeter cosmwasm.GasMeter, gasLimit uint64) ([]byte, uint64, error) {
if m.QueryFn == nil {
panic("not supposed to be called!")
}
return m.QueryFn(code, env, queryMsg, store, goapi, querier, gasMeter, gasLimit)
}
func (m *MockWasmer) Migrate(code cosmwasm.CodeID, env wasmTypes.Env, info wasmTypes.MessageInfo, migrateMsg []byte, store cosmwasm.KVStore, goapi cosmwasm.GoAPI, querier cosmwasm.Querier, gasMeter cosmwasm.GasMeter, gasLimit uint64) (*wasmTypes.MigrateResponse, uint64, error) {
if m.MigrateFn == nil {
panic("not supposed to be called!")
}
return m.MigrateFn(code, env, info, migrateMsg, store, goapi, querier, gasMeter, gasLimit)
}
func (m *MockWasmer) GetCode(code cosmwasm.CodeID) (cosmwasm.WasmCode, error) {
if m.GetCodeFn == nil {
panic("not supposed to be called!")
}
return m.GetCodeFn(code)
}
func (m *MockWasmer) Cleanup() {
if m.CleanupFn == nil {
panic("not supposed to be called!")
}
m.CleanupFn()
}
var alwaysPanicMockWasmer = &MockWasmer{}
// selfCallingInstMockWasmer prepares a Wasmer mock that calls itself on instantiation.
func selfCallingInstMockWasmer(executeCalled *bool) *MockWasmer {
return &MockWasmer{
CreateFn: func(code cosmwasm.WasmCode) (cosmwasm.CodeID, error) {
anyCodeID := bytes.Repeat([]byte{0x1}, 32)
return anyCodeID, nil
},
InstantiateFn: func(code cosmwasm.CodeID, env wasmTypes.Env, info wasmTypes.MessageInfo, initMsg []byte, store cosmwasm.KVStore, goapi cosmwasm.GoAPI, querier cosmwasm.Querier, gasMeter cosmwasm.GasMeter, gasLimit uint64) (*wasmTypes.InitResponse, uint64, error) {
return &wasmTypes.InitResponse{
Messages: []wasmTypes.CosmosMsg{
{Wasm: &wasmTypes.WasmMsg{Execute: &wasmTypes.ExecuteMsg{ContractAddr: env.Contract.Address, Msg: []byte(`{}`)}}},
},
}, 1, nil
},
ExecuteFn: func(code cosmwasm.CodeID, env wasmTypes.Env, info wasmTypes.MessageInfo, executeMsg []byte, store cosmwasm.KVStore, goapi cosmwasm.GoAPI, querier cosmwasm.Querier, gasMeter cosmwasm.GasMeter, gasLimit uint64) (*wasmTypes.HandleResponse, uint64, error) {
*executeCalled = true
return &wasmTypes.HandleResponse{}, 1, nil
},
}
}

View File

@@ -0,0 +1,102 @@
package types
import (
"github.com/CosmWasm/go-cosmwasm"
"github.com/CosmWasm/go-cosmwasm/types"
)
// WasmerEngine defines the WASM contract runtime engine.
type WasmerEngine interface {
// Create will compile the wasm code, and store the resulting pre-compile
// as well as the original code. Both can be referenced later via CodeID
// This must be done one time for given code, after which it can be
// instatitated many times, and each instance called many times.
//
// For example, the code for all ERC-20 contracts should be the same.
// This function stores the code for that contract only once, but it can
// be instantiated with custom inputs in the future.
Create(code cosmwasm.WasmCode) (cosmwasm.CodeID, error)
// Instantiate will create a new contract based on the given codeID.
// We can set the initMsg (contract "genesis") here, and it then receives
// an account and address and can be invoked (Execute) many times.
//
// Storage should be set with a PrefixedKVStore that this code can safely access.
//
// Under the hood, we may recompile the wasm, use a cached native compile, or even use a cached instance
// for performance.
Instantiate(
code cosmwasm.CodeID,
env types.Env,
info types.MessageInfo,
initMsg []byte,
store cosmwasm.KVStore,
goapi cosmwasm.GoAPI,
querier cosmwasm.Querier,
gasMeter cosmwasm.GasMeter,
gasLimit uint64,
) (*types.InitResponse, uint64, error)
// Execute calls a given contract. Since the only difference between contracts with the same CodeID is the
// data in their local storage, and their address in the outside world, we need no ContractID here.
// (That is a detail for the external, sdk-facing, side).
//
// The caller is responsible for passing the correct `store` (which must have been initialized exactly once),
// and setting the env with relevent info on this instance (address, balance, etc)
Execute(
code cosmwasm.CodeID,
env types.Env,
info types.MessageInfo,
executeMsg []byte,
store cosmwasm.KVStore,
goapi cosmwasm.GoAPI,
querier cosmwasm.Querier,
gasMeter cosmwasm.GasMeter,
gasLimit uint64,
) (*types.HandleResponse, uint64, error)
// Query allows a client to execute a contract-specific query. If the result is not empty, it should be
// valid json-encoded data to return to the client.
// The meaning of path and data can be determined by the code. Path is the suffix of the abci.QueryRequest.Path
Query(
code cosmwasm.CodeID,
env types.Env,
queryMsg []byte,
store cosmwasm.KVStore,
goapi cosmwasm.GoAPI,
querier cosmwasm.Querier,
gasMeter cosmwasm.GasMeter,
gasLimit uint64,
) ([]byte, uint64, error)
// Migrate will migrate an existing contract to a new code binary.
// This takes storage of the data from the original contract and the CodeID of the new contract that should
// replace it. This allows it to run a migration step if needed, or return an error if unable to migrate
// the given data.
//
// MigrateMsg has some data on how to perform the migration.
Migrate(
code cosmwasm.CodeID,
env types.Env,
info types.MessageInfo,
migrateMsg []byte,
store cosmwasm.KVStore,
goapi cosmwasm.GoAPI,
querier cosmwasm.Querier,
gasMeter cosmwasm.GasMeter,
gasLimit uint64,
) (*types.MigrateResponse, uint64, error)
// GetCode will load the original wasm code for the given code id.
// This will only succeed if that code id was previously returned from
// a call to Create.
//
// This can be used so that the (short) code id (hash) is stored in the iavl tree
// and the larger binary blobs (wasm and pre-compiles) are all managed by the
// rust library
GetCode(code cosmwasm.CodeID) (cosmwasm.WasmCode, error)
// Cleanup should be called when no longer using this to free resources on the rust-side
Cleanup()
}

View File

@@ -85,11 +85,11 @@ func (b AppModuleBasic) RegisterInterfaces(registry cdctypes.InterfaceRegistry)
// AppModule implements an application module for the wasm module.
type AppModule struct {
AppModuleBasic
keeper Keeper
keeper *Keeper
}
// NewAppModule creates a new AppModule object
func NewAppModule(keeper Keeper) AppModule {
func NewAppModule(keeper *Keeper) AppModule {
return AppModule{
AppModuleBasic: AppModuleBasic{},
keeper: keeper,

View File

@@ -36,7 +36,7 @@ func setupTest(t *testing.T) testData {
module: NewAppModule(keeper),
ctx: ctx,
acctKeeper: acctKeeper,
keeper: keeper,
keeper: *keeper,
bankKeeper: bankKeeper,
}
return data