760 lines
26 KiB
Go
760 lines
26 KiB
Go
package keeper
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/cosmos/cosmos-sdk/baseapp"
|
|
"github.com/cosmos/cosmos-sdk/codec"
|
|
"github.com/cosmos/cosmos-sdk/std"
|
|
"github.com/cosmos/cosmos-sdk/store"
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
"github.com/cosmos/cosmos-sdk/types/address"
|
|
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
|
"github.com/cosmos/cosmos-sdk/types/module"
|
|
"github.com/cosmos/cosmos-sdk/x/auth"
|
|
authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper"
|
|
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
|
"github.com/cosmos/cosmos-sdk/x/auth/vesting"
|
|
authzkeeper "github.com/cosmos/cosmos-sdk/x/authz/keeper"
|
|
"github.com/cosmos/cosmos-sdk/x/bank"
|
|
bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper"
|
|
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
|
|
"github.com/cosmos/cosmos-sdk/x/capability"
|
|
capabilitykeeper "github.com/cosmos/cosmos-sdk/x/capability/keeper"
|
|
capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types"
|
|
"github.com/cosmos/cosmos-sdk/x/crisis"
|
|
crisistypes "github.com/cosmos/cosmos-sdk/x/crisis/types"
|
|
"github.com/cosmos/cosmos-sdk/x/distribution"
|
|
distrclient "github.com/cosmos/cosmos-sdk/x/distribution/client"
|
|
distributionkeeper "github.com/cosmos/cosmos-sdk/x/distribution/keeper"
|
|
distributiontypes "github.com/cosmos/cosmos-sdk/x/distribution/types"
|
|
"github.com/cosmos/cosmos-sdk/x/evidence"
|
|
evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types"
|
|
"github.com/cosmos/cosmos-sdk/x/feegrant"
|
|
"github.com/cosmos/cosmos-sdk/x/gov"
|
|
govkeeper "github.com/cosmos/cosmos-sdk/x/gov/keeper"
|
|
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
|
|
"github.com/cosmos/cosmos-sdk/x/mint"
|
|
minttypes "github.com/cosmos/cosmos-sdk/x/mint/types"
|
|
"github.com/cosmos/cosmos-sdk/x/params"
|
|
paramsclient "github.com/cosmos/cosmos-sdk/x/params/client"
|
|
paramskeeper "github.com/cosmos/cosmos-sdk/x/params/keeper"
|
|
paramstypes "github.com/cosmos/cosmos-sdk/x/params/types"
|
|
paramproposal "github.com/cosmos/cosmos-sdk/x/params/types/proposal"
|
|
"github.com/cosmos/cosmos-sdk/x/slashing"
|
|
slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types"
|
|
"github.com/cosmos/cosmos-sdk/x/staking"
|
|
stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper"
|
|
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
|
|
"github.com/cosmos/cosmos-sdk/x/upgrade"
|
|
upgradeclient "github.com/cosmos/cosmos-sdk/x/upgrade/client"
|
|
upgradekeeper "github.com/cosmos/cosmos-sdk/x/upgrade/keeper"
|
|
upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types"
|
|
"github.com/cosmos/ibc-go/v4/modules/apps/transfer"
|
|
ibctransfertypes "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types"
|
|
ibc "github.com/cosmos/ibc-go/v4/modules/core"
|
|
ibchost "github.com/cosmos/ibc-go/v4/modules/core/24-host"
|
|
ibckeeper "github.com/cosmos/ibc-go/v4/modules/core/keeper"
|
|
"github.com/stretchr/testify/require"
|
|
"github.com/tendermint/tendermint/crypto"
|
|
"github.com/tendermint/tendermint/crypto/ed25519"
|
|
"github.com/tendermint/tendermint/libs/log"
|
|
"github.com/tendermint/tendermint/libs/rand"
|
|
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
|
dbm "github.com/tendermint/tm-db"
|
|
|
|
wasmappparams "github.com/cerc-io/laconicd/app/params"
|
|
"github.com/cerc-io/laconicd/x/wasm/keeper/wasmtesting"
|
|
"github.com/cerc-io/laconicd/x/wasm/types"
|
|
)
|
|
|
|
var moduleBasics = module.NewBasicManager(
|
|
auth.AppModuleBasic{},
|
|
bank.AppModuleBasic{},
|
|
capability.AppModuleBasic{},
|
|
staking.AppModuleBasic{},
|
|
mint.AppModuleBasic{},
|
|
distribution.AppModuleBasic{},
|
|
gov.NewAppModuleBasic(
|
|
paramsclient.ProposalHandler, distrclient.ProposalHandler, upgradeclient.ProposalHandler,
|
|
),
|
|
params.AppModuleBasic{},
|
|
crisis.AppModuleBasic{},
|
|
slashing.AppModuleBasic{},
|
|
ibc.AppModuleBasic{},
|
|
upgrade.AppModuleBasic{},
|
|
evidence.AppModuleBasic{},
|
|
transfer.AppModuleBasic{},
|
|
vesting.AppModuleBasic{},
|
|
)
|
|
|
|
func MakeTestCodec(t testing.TB) codec.Codec {
|
|
return MakeEncodingConfig(t).Marshaler
|
|
}
|
|
|
|
func MakeEncodingConfig(_ testing.TB) wasmappparams.EncodingConfig {
|
|
encodingConfig := wasmappparams.MakeEncodingConfig()
|
|
amino := encodingConfig.Amino
|
|
interfaceRegistry := encodingConfig.InterfaceRegistry
|
|
|
|
std.RegisterInterfaces(interfaceRegistry)
|
|
std.RegisterLegacyAminoCodec(amino)
|
|
|
|
moduleBasics.RegisterLegacyAminoCodec(amino)
|
|
moduleBasics.RegisterInterfaces(interfaceRegistry)
|
|
// add wasmd types
|
|
types.RegisterInterfaces(interfaceRegistry)
|
|
types.RegisterLegacyAminoCodec(amino)
|
|
|
|
return encodingConfig
|
|
}
|
|
|
|
var TestingStakeParams = stakingtypes.Params{
|
|
UnbondingTime: 100,
|
|
MaxValidators: 10,
|
|
MaxEntries: 10,
|
|
HistoricalEntries: 10,
|
|
BondDenom: "stake",
|
|
}
|
|
|
|
type TestFaucet struct {
|
|
t testing.TB
|
|
bankKeeper bankkeeper.Keeper
|
|
sender sdk.AccAddress
|
|
balance sdk.Coins
|
|
minterModuleName string
|
|
}
|
|
|
|
func NewTestFaucet(t testing.TB, ctx sdk.Context, bankKeeper bankkeeper.Keeper, minterModuleName string, initialAmount ...sdk.Coin) *TestFaucet {
|
|
require.NotEmpty(t, initialAmount)
|
|
r := &TestFaucet{t: t, bankKeeper: bankKeeper, minterModuleName: minterModuleName}
|
|
_, _, addr := keyPubAddr()
|
|
r.sender = addr
|
|
r.Mint(ctx, addr, initialAmount...)
|
|
r.balance = initialAmount
|
|
return r
|
|
}
|
|
|
|
func (f *TestFaucet) Mint(parentCtx sdk.Context, addr sdk.AccAddress, amounts ...sdk.Coin) {
|
|
require.NotEmpty(f.t, amounts)
|
|
ctx := parentCtx.WithEventManager(sdk.NewEventManager()) // discard all faucet related events
|
|
err := f.bankKeeper.MintCoins(ctx, f.minterModuleName, amounts)
|
|
require.NoError(f.t, err)
|
|
err = f.bankKeeper.SendCoinsFromModuleToAccount(ctx, f.minterModuleName, addr, amounts)
|
|
require.NoError(f.t, err)
|
|
f.balance = f.balance.Add(amounts...)
|
|
}
|
|
|
|
func (f *TestFaucet) Fund(parentCtx sdk.Context, receiver sdk.AccAddress, amounts ...sdk.Coin) {
|
|
require.NotEmpty(f.t, amounts)
|
|
// ensure faucet is always filled
|
|
if !f.balance.IsAllGTE(amounts) {
|
|
f.Mint(parentCtx, f.sender, amounts...)
|
|
}
|
|
ctx := parentCtx.WithEventManager(sdk.NewEventManager()) // discard all faucet related events
|
|
err := f.bankKeeper.SendCoins(ctx, f.sender, receiver, amounts)
|
|
require.NoError(f.t, err)
|
|
f.balance = f.balance.Sub(amounts)
|
|
}
|
|
|
|
func (f *TestFaucet) NewFundedRandomAccount(ctx sdk.Context, amounts ...sdk.Coin) sdk.AccAddress {
|
|
_, _, addr := keyPubAddr()
|
|
f.Fund(ctx, addr, amounts...)
|
|
return addr
|
|
}
|
|
|
|
type TestKeepers struct {
|
|
AccountKeeper authkeeper.AccountKeeper
|
|
StakingKeeper stakingkeeper.Keeper
|
|
DistKeeper distributionkeeper.Keeper
|
|
BankKeeper bankkeeper.Keeper
|
|
GovKeeper govkeeper.Keeper
|
|
ContractKeeper types.ContractOpsKeeper
|
|
WasmKeeper *Keeper
|
|
IBCKeeper *ibckeeper.Keeper
|
|
Router *baseapp.Router
|
|
EncodingConfig wasmappparams.EncodingConfig
|
|
Faucet *TestFaucet
|
|
MultiStore sdk.CommitMultiStore
|
|
ScopedWasmKeeper capabilitykeeper.ScopedKeeper
|
|
}
|
|
|
|
// CreateDefaultTestInput common settings for CreateTestInput
|
|
func CreateDefaultTestInput(t testing.TB) (sdk.Context, TestKeepers) {
|
|
return CreateTestInput(t, false, "staking")
|
|
}
|
|
|
|
// CreateTestInput encoders can be nil to accept the defaults, or set it to override some of the message handlers (like default)
|
|
func CreateTestInput(t testing.TB, isCheckTx bool, availableCapabilities string, opts ...Option) (sdk.Context, TestKeepers) {
|
|
// Load default wasm config
|
|
return createTestInput(t, isCheckTx, availableCapabilities, types.DefaultWasmConfig(), dbm.NewMemDB(), opts...)
|
|
}
|
|
|
|
// encoders can be nil to accept the defaults, or set it to override some of the message handlers (like default)
|
|
func createTestInput(
|
|
t testing.TB,
|
|
isCheckTx bool,
|
|
availableCapabilities string,
|
|
wasmConfig types.WasmConfig,
|
|
db dbm.DB,
|
|
opts ...Option,
|
|
) (sdk.Context, TestKeepers) {
|
|
tempDir := t.TempDir()
|
|
|
|
keys := sdk.NewKVStoreKeys(
|
|
authtypes.StoreKey, banktypes.StoreKey, stakingtypes.StoreKey,
|
|
minttypes.StoreKey, distributiontypes.StoreKey, slashingtypes.StoreKey,
|
|
govtypes.StoreKey, paramstypes.StoreKey, ibchost.StoreKey, upgradetypes.StoreKey,
|
|
evidencetypes.StoreKey, ibctransfertypes.StoreKey,
|
|
capabilitytypes.StoreKey, feegrant.StoreKey, authzkeeper.StoreKey,
|
|
types.StoreKey,
|
|
)
|
|
ms := store.NewCommitMultiStore(db)
|
|
for _, v := range keys {
|
|
ms.MountStoreWithDB(v, sdk.StoreTypeIAVL, db)
|
|
}
|
|
tkeys := sdk.NewTransientStoreKeys(paramstypes.TStoreKey)
|
|
for _, v := range tkeys {
|
|
ms.MountStoreWithDB(v, sdk.StoreTypeTransient, db)
|
|
}
|
|
|
|
memKeys := sdk.NewMemoryStoreKeys(capabilitytypes.MemStoreKey)
|
|
for _, v := range memKeys {
|
|
ms.MountStoreWithDB(v, sdk.StoreTypeMemory, db)
|
|
}
|
|
|
|
require.NoError(t, ms.LoadLatestVersion())
|
|
|
|
ctx := sdk.NewContext(ms, tmproto.Header{
|
|
Height: 1234567,
|
|
Time: time.Date(2020, time.April, 22, 12, 0, 0, 0, time.UTC),
|
|
}, isCheckTx, log.NewNopLogger())
|
|
ctx = types.WithTXCounter(ctx, 0)
|
|
|
|
encodingConfig := MakeEncodingConfig(t)
|
|
appCodec, legacyAmino := encodingConfig.Marshaler, encodingConfig.Amino
|
|
|
|
paramsKeeper := paramskeeper.NewKeeper(
|
|
appCodec,
|
|
legacyAmino,
|
|
keys[paramstypes.StoreKey],
|
|
tkeys[paramstypes.TStoreKey],
|
|
)
|
|
for _, m := range []string{
|
|
authtypes.ModuleName,
|
|
banktypes.ModuleName,
|
|
stakingtypes.ModuleName,
|
|
minttypes.ModuleName,
|
|
distributiontypes.ModuleName,
|
|
slashingtypes.ModuleName,
|
|
crisistypes.ModuleName,
|
|
ibctransfertypes.ModuleName,
|
|
capabilitytypes.ModuleName,
|
|
ibchost.ModuleName,
|
|
govtypes.ModuleName,
|
|
types.ModuleName,
|
|
} {
|
|
paramsKeeper.Subspace(m)
|
|
}
|
|
subspace := func(m string) paramstypes.Subspace {
|
|
r, ok := paramsKeeper.GetSubspace(m)
|
|
require.True(t, ok)
|
|
return r
|
|
}
|
|
maccPerms := map[string][]string{ // module account permissions
|
|
authtypes.FeeCollectorName: nil,
|
|
distributiontypes.ModuleName: nil,
|
|
minttypes.ModuleName: {authtypes.Minter},
|
|
stakingtypes.BondedPoolName: {authtypes.Burner, authtypes.Staking},
|
|
stakingtypes.NotBondedPoolName: {authtypes.Burner, authtypes.Staking},
|
|
govtypes.ModuleName: {authtypes.Burner},
|
|
ibctransfertypes.ModuleName: {authtypes.Minter, authtypes.Burner},
|
|
types.ModuleName: {authtypes.Burner},
|
|
}
|
|
accountKeeper := authkeeper.NewAccountKeeper(
|
|
appCodec,
|
|
keys[authtypes.StoreKey], // target store
|
|
subspace(authtypes.ModuleName),
|
|
authtypes.ProtoBaseAccount, // prototype
|
|
maccPerms,
|
|
)
|
|
blockedAddrs := make(map[string]bool)
|
|
for acc := range maccPerms {
|
|
blockedAddrs[authtypes.NewModuleAddress(acc).String()] = true
|
|
}
|
|
|
|
bankKeeper := bankkeeper.NewBaseKeeper(
|
|
appCodec,
|
|
keys[banktypes.StoreKey],
|
|
accountKeeper,
|
|
subspace(banktypes.ModuleName),
|
|
blockedAddrs,
|
|
)
|
|
bankKeeper.SetParams(ctx, banktypes.DefaultParams())
|
|
|
|
stakingKeeper := stakingkeeper.NewKeeper(
|
|
appCodec,
|
|
keys[stakingtypes.StoreKey],
|
|
accountKeeper,
|
|
bankKeeper,
|
|
subspace(stakingtypes.ModuleName),
|
|
)
|
|
stakingKeeper.SetParams(ctx, TestingStakeParams)
|
|
|
|
distKeeper := distributionkeeper.NewKeeper(
|
|
appCodec,
|
|
keys[distributiontypes.StoreKey],
|
|
subspace(distributiontypes.ModuleName),
|
|
accountKeeper,
|
|
bankKeeper,
|
|
stakingKeeper,
|
|
authtypes.FeeCollectorName,
|
|
nil,
|
|
)
|
|
distKeeper.SetParams(ctx, distributiontypes.DefaultParams())
|
|
stakingKeeper.SetHooks(distKeeper.Hooks())
|
|
|
|
// set genesis items required for distribution
|
|
distKeeper.SetFeePool(ctx, distributiontypes.InitialFeePool())
|
|
|
|
upgradeKeeper := upgradekeeper.NewKeeper(
|
|
map[int64]bool{},
|
|
keys[upgradetypes.StoreKey],
|
|
appCodec,
|
|
tempDir,
|
|
nil,
|
|
)
|
|
|
|
faucet := NewTestFaucet(t, ctx, bankKeeper, minttypes.ModuleName, sdk.NewCoin("stake", sdk.NewInt(100_000_000_000)))
|
|
|
|
// set some funds ot pay out validatores, based on code from:
|
|
// https://github.com/cosmos/cosmos-sdk/blob/fea231556aee4d549d7551a6190389c4328194eb/x/distribution/keeper/keeper_test.go#L50-L57
|
|
distrAcc := distKeeper.GetDistributionAccount(ctx)
|
|
faucet.Fund(ctx, distrAcc.GetAddress(), sdk.NewCoin("stake", sdk.NewInt(2000000)))
|
|
accountKeeper.SetModuleAccount(ctx, distrAcc)
|
|
|
|
capabilityKeeper := capabilitykeeper.NewKeeper(
|
|
appCodec,
|
|
keys[capabilitytypes.StoreKey],
|
|
memKeys[capabilitytypes.MemStoreKey],
|
|
)
|
|
scopedIBCKeeper := capabilityKeeper.ScopeToModule(ibchost.ModuleName)
|
|
scopedWasmKeeper := capabilityKeeper.ScopeToModule(types.ModuleName)
|
|
|
|
ibcKeeper := ibckeeper.NewKeeper(
|
|
appCodec,
|
|
keys[ibchost.StoreKey],
|
|
subspace(ibchost.ModuleName),
|
|
stakingKeeper,
|
|
upgradeKeeper,
|
|
scopedIBCKeeper,
|
|
)
|
|
|
|
router := baseapp.NewRouter()
|
|
bh := bank.NewHandler(bankKeeper)
|
|
router.AddRoute(sdk.NewRoute(banktypes.RouterKey, bh))
|
|
sh := staking.NewHandler(stakingKeeper)
|
|
router.AddRoute(sdk.NewRoute(stakingtypes.RouterKey, sh))
|
|
dh := distribution.NewHandler(distKeeper)
|
|
router.AddRoute(sdk.NewRoute(distributiontypes.RouterKey, dh))
|
|
|
|
querier := baseapp.NewGRPCQueryRouter()
|
|
querier.SetInterfaceRegistry(encodingConfig.InterfaceRegistry)
|
|
msgRouter := baseapp.NewMsgServiceRouter()
|
|
msgRouter.SetInterfaceRegistry(encodingConfig.InterfaceRegistry)
|
|
|
|
cfg := sdk.GetConfig()
|
|
cfg.SetAddressVerifier(types.VerifyAddressLen())
|
|
|
|
keeper := NewKeeper(
|
|
appCodec,
|
|
keys[types.StoreKey],
|
|
subspace(types.ModuleName),
|
|
accountKeeper,
|
|
bankKeeper,
|
|
stakingKeeper,
|
|
distKeeper,
|
|
ibcKeeper.ChannelKeeper,
|
|
&ibcKeeper.PortKeeper,
|
|
scopedWasmKeeper,
|
|
wasmtesting.MockIBCTransferKeeper{},
|
|
msgRouter,
|
|
querier,
|
|
tempDir,
|
|
wasmConfig,
|
|
availableCapabilities,
|
|
opts...,
|
|
)
|
|
keeper.SetParams(ctx, types.DefaultParams())
|
|
// add wasm handler so we can loop-back (contracts calling contracts)
|
|
contractKeeper := NewDefaultPermissionKeeper(&keeper)
|
|
router.AddRoute(sdk.NewRoute(types.RouterKey, TestHandler(contractKeeper)))
|
|
|
|
am := module.NewManager( // minimal module set that we use for message/ query tests
|
|
bank.NewAppModule(appCodec, bankKeeper, accountKeeper),
|
|
staking.NewAppModule(appCodec, stakingKeeper, accountKeeper, bankKeeper),
|
|
distribution.NewAppModule(appCodec, distKeeper, accountKeeper, bankKeeper, stakingKeeper),
|
|
)
|
|
am.RegisterServices(module.NewConfigurator(appCodec, msgRouter, querier))
|
|
types.RegisterMsgServer(msgRouter, NewMsgServerImpl(NewDefaultPermissionKeeper(keeper)))
|
|
types.RegisterQueryServer(querier, NewGrpcQuerier(appCodec, keys[types.ModuleName], keeper, keeper.queryGasLimit))
|
|
|
|
govRouter := govtypes.NewRouter().
|
|
AddRoute(govtypes.RouterKey, govtypes.ProposalHandler).
|
|
AddRoute(paramproposal.RouterKey, params.NewParamChangeProposalHandler(paramsKeeper)).
|
|
AddRoute(distributiontypes.RouterKey, distribution.NewCommunityPoolSpendProposalHandler(distKeeper)).
|
|
AddRoute(types.RouterKey, NewWasmProposalHandler(&keeper, types.EnableAllProposals))
|
|
|
|
govKeeper := govkeeper.NewKeeper(
|
|
appCodec,
|
|
keys[govtypes.StoreKey],
|
|
subspace(govtypes.ModuleName).WithKeyTable(govtypes.ParamKeyTable()),
|
|
accountKeeper,
|
|
bankKeeper,
|
|
stakingKeeper,
|
|
govRouter,
|
|
)
|
|
|
|
govKeeper.SetProposalID(ctx, govtypes.DefaultStartingProposalID)
|
|
govKeeper.SetDepositParams(ctx, govtypes.DefaultDepositParams())
|
|
govKeeper.SetVotingParams(ctx, govtypes.DefaultVotingParams())
|
|
govKeeper.SetTallyParams(ctx, govtypes.DefaultTallyParams())
|
|
|
|
keepers := TestKeepers{
|
|
AccountKeeper: accountKeeper,
|
|
StakingKeeper: stakingKeeper,
|
|
DistKeeper: distKeeper,
|
|
ContractKeeper: contractKeeper,
|
|
WasmKeeper: &keeper,
|
|
BankKeeper: bankKeeper,
|
|
GovKeeper: govKeeper,
|
|
IBCKeeper: ibcKeeper,
|
|
Router: router,
|
|
EncodingConfig: encodingConfig,
|
|
Faucet: faucet,
|
|
MultiStore: ms,
|
|
ScopedWasmKeeper: scopedWasmKeeper,
|
|
}
|
|
return ctx, keepers
|
|
}
|
|
|
|
// TestHandler returns a wasm handler for tests (to avoid circular imports)
|
|
func TestHandler(k types.ContractOpsKeeper) sdk.Handler {
|
|
return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) {
|
|
ctx = ctx.WithEventManager(sdk.NewEventManager())
|
|
switch msg := msg.(type) {
|
|
case *types.MsgStoreCode:
|
|
return handleStoreCode(ctx, k, msg)
|
|
case *types.MsgInstantiateContract:
|
|
return handleInstantiate(ctx, k, msg)
|
|
case *types.MsgExecuteContract:
|
|
return handleExecute(ctx, k, msg)
|
|
default:
|
|
errMsg := fmt.Sprintf("unrecognized wasm message type: %T", msg)
|
|
return nil, sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, errMsg)
|
|
}
|
|
}
|
|
}
|
|
|
|
func handleStoreCode(ctx sdk.Context, k types.ContractOpsKeeper, msg *types.MsgStoreCode) (*sdk.Result, error) {
|
|
senderAddr, err := sdk.AccAddressFromBech32(msg.Sender)
|
|
if err != nil {
|
|
return nil, sdkerrors.Wrap(err, "sender")
|
|
}
|
|
codeID, _, err := k.Create(ctx, senderAddr, msg.WASMByteCode, msg.InstantiatePermission)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &sdk.Result{
|
|
Data: []byte(fmt.Sprintf("%d", codeID)),
|
|
Events: ctx.EventManager().ABCIEvents(),
|
|
}, nil
|
|
}
|
|
|
|
func handleInstantiate(ctx sdk.Context, k types.ContractOpsKeeper, msg *types.MsgInstantiateContract) (*sdk.Result, error) {
|
|
senderAddr, err := sdk.AccAddressFromBech32(msg.Sender)
|
|
if err != nil {
|
|
return nil, sdkerrors.Wrap(err, "sender")
|
|
}
|
|
var adminAddr sdk.AccAddress
|
|
if msg.Admin != "" {
|
|
if adminAddr, err = sdk.AccAddressFromBech32(msg.Admin); err != nil {
|
|
return nil, sdkerrors.Wrap(err, "admin")
|
|
}
|
|
}
|
|
|
|
contractAddr, _, err := k.Instantiate(ctx, msg.CodeID, senderAddr, adminAddr, msg.Msg, msg.Label, msg.Funds)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &sdk.Result{
|
|
Data: contractAddr,
|
|
Events: ctx.EventManager().Events().ToABCIEvents(),
|
|
}, nil
|
|
}
|
|
|
|
func handleExecute(ctx sdk.Context, k types.ContractOpsKeeper, msg *types.MsgExecuteContract) (*sdk.Result, error) {
|
|
senderAddr, err := sdk.AccAddressFromBech32(msg.Sender)
|
|
if err != nil {
|
|
return nil, sdkerrors.Wrap(err, "sender")
|
|
}
|
|
contractAddr, err := sdk.AccAddressFromBech32(msg.Contract)
|
|
if err != nil {
|
|
return nil, sdkerrors.Wrap(err, "admin")
|
|
}
|
|
data, err := k.Execute(ctx, contractAddr, senderAddr, msg.Msg, msg.Funds)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &sdk.Result{
|
|
Data: data,
|
|
Events: ctx.EventManager().Events().ToABCIEvents(),
|
|
}, nil
|
|
}
|
|
|
|
func RandomAccountAddress(_ testing.TB) sdk.AccAddress {
|
|
_, _, addr := keyPubAddr()
|
|
return addr
|
|
}
|
|
|
|
// DeterministicAccountAddress creates a test address with v repeated to valid address size
|
|
func DeterministicAccountAddress(_ testing.TB, v byte) sdk.AccAddress {
|
|
return bytes.Repeat([]byte{v}, address.Len)
|
|
}
|
|
|
|
func RandomBech32AccountAddress(t testing.TB) string {
|
|
return RandomAccountAddress(t).String()
|
|
}
|
|
|
|
type ExampleContract struct {
|
|
InitialAmount sdk.Coins
|
|
Creator crypto.PrivKey
|
|
CreatorAddr sdk.AccAddress
|
|
CodeID uint64
|
|
Checksum []byte
|
|
}
|
|
|
|
func StoreHackatomExampleContract(t testing.TB, ctx sdk.Context, keepers TestKeepers) ExampleContract {
|
|
return StoreExampleContract(t, ctx, keepers, "./testdata/hackatom.wasm")
|
|
}
|
|
|
|
func StoreBurnerExampleContract(t testing.TB, ctx sdk.Context, keepers TestKeepers) ExampleContract {
|
|
return StoreExampleContract(t, ctx, keepers, "./testdata/burner.wasm")
|
|
}
|
|
|
|
func StoreIBCReflectContract(t testing.TB, ctx sdk.Context, keepers TestKeepers) ExampleContract {
|
|
return StoreExampleContract(t, ctx, keepers, "./testdata/ibc_reflect.wasm")
|
|
}
|
|
|
|
func StoreReflectContract(t testing.TB, ctx sdk.Context, keepers TestKeepers) ExampleContract {
|
|
return StoreExampleContract(t, ctx, keepers, "./testdata/reflect.wasm")
|
|
}
|
|
|
|
func StoreExampleContract(t testing.TB, ctx sdk.Context, keepers TestKeepers, wasmFile string) ExampleContract {
|
|
anyAmount := sdk.NewCoins(sdk.NewInt64Coin("denom", 1000))
|
|
creator, _, creatorAddr := keyPubAddr()
|
|
fundAccounts(t, ctx, keepers.AccountKeeper, keepers.BankKeeper, creatorAddr, anyAmount)
|
|
|
|
wasmCode, err := os.ReadFile(wasmFile)
|
|
require.NoError(t, err)
|
|
|
|
codeID, _, err := keepers.ContractKeeper.Create(ctx, creatorAddr, wasmCode, nil)
|
|
require.NoError(t, err)
|
|
hash := keepers.WasmKeeper.GetCodeInfo(ctx, codeID).CodeHash
|
|
return ExampleContract{anyAmount, creator, creatorAddr, codeID, hash}
|
|
}
|
|
|
|
var wasmIdent = []byte("\x00\x61\x73\x6D")
|
|
|
|
type ExampleContractInstance struct {
|
|
ExampleContract
|
|
Contract sdk.AccAddress
|
|
}
|
|
|
|
// SeedNewContractInstance sets the mock wasmerEngine in keeper and calls store + instantiate to init the contract's metadata
|
|
func SeedNewContractInstance(t testing.TB, ctx sdk.Context, keepers TestKeepers, mock types.WasmerEngine) ExampleContractInstance {
|
|
t.Helper()
|
|
exampleContract := StoreRandomContract(t, ctx, keepers, mock)
|
|
contractAddr, _, err := keepers.ContractKeeper.Instantiate(ctx, exampleContract.CodeID, exampleContract.CreatorAddr, exampleContract.CreatorAddr, []byte(`{}`), "", nil)
|
|
require.NoError(t, err)
|
|
return ExampleContractInstance{
|
|
ExampleContract: exampleContract,
|
|
Contract: contractAddr,
|
|
}
|
|
}
|
|
|
|
// StoreRandomContract sets the mock wasmerEngine in keeper and calls store
|
|
func StoreRandomContract(t testing.TB, ctx sdk.Context, keepers TestKeepers, mock types.WasmerEngine) ExampleContract {
|
|
return StoreRandomContractWithAccessConfig(t, ctx, keepers, mock, nil)
|
|
}
|
|
|
|
func StoreRandomContractWithAccessConfig(
|
|
t testing.TB, ctx sdk.Context,
|
|
keepers TestKeepers,
|
|
mock types.WasmerEngine,
|
|
cfg *types.AccessConfig,
|
|
) ExampleContract {
|
|
t.Helper()
|
|
anyAmount := sdk.NewCoins(sdk.NewInt64Coin("denom", 1000))
|
|
creator, _, creatorAddr := keyPubAddr()
|
|
fundAccounts(t, ctx, keepers.AccountKeeper, keepers.BankKeeper, creatorAddr, anyAmount)
|
|
keepers.WasmKeeper.wasmVM = mock
|
|
wasmCode := append(wasmIdent, rand.Bytes(10)...) //nolint:gocritic
|
|
codeID, checksum, err := keepers.ContractKeeper.Create(ctx, creatorAddr, wasmCode, cfg)
|
|
require.NoError(t, err)
|
|
exampleContract := ExampleContract{InitialAmount: anyAmount, Creator: creator, CreatorAddr: creatorAddr, CodeID: codeID, Checksum: checksum}
|
|
return exampleContract
|
|
}
|
|
|
|
type HackatomExampleInstance struct {
|
|
ExampleContract
|
|
Contract sdk.AccAddress
|
|
Verifier crypto.PrivKey
|
|
VerifierAddr sdk.AccAddress
|
|
Beneficiary crypto.PrivKey
|
|
BeneficiaryAddr sdk.AccAddress
|
|
Label string
|
|
Deposit sdk.Coins
|
|
}
|
|
|
|
// InstantiateHackatomExampleContract load and instantiate the "./testdata/hackatom.wasm" contract
|
|
func InstantiateHackatomExampleContract(t testing.TB, 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))
|
|
|
|
adminAddr := contract.CreatorAddr
|
|
label := "demo contract to query"
|
|
contractAddr, _, err := keepers.ContractKeeper.Instantiate(ctx, contract.CodeID, contract.CreatorAddr, adminAddr, initMsgBz, label, initialAmount)
|
|
require.NoError(t, err)
|
|
return HackatomExampleInstance{
|
|
ExampleContract: contract,
|
|
Contract: contractAddr,
|
|
Verifier: verifier,
|
|
VerifierAddr: verifierAddr,
|
|
Beneficiary: beneficiary,
|
|
BeneficiaryAddr: beneficiaryAddr,
|
|
Label: label,
|
|
Deposit: initialAmount,
|
|
}
|
|
}
|
|
|
|
type ExampleInstance struct {
|
|
ExampleContract
|
|
Contract sdk.AccAddress
|
|
Label string
|
|
Deposit sdk.Coins
|
|
}
|
|
|
|
// InstantiateReflectExampleContract load and instantiate the "./testdata/reflect.wasm" contract
|
|
func InstantiateReflectExampleContract(t testing.TB, ctx sdk.Context, keepers TestKeepers) ExampleInstance {
|
|
example := StoreReflectContract(t, ctx, keepers)
|
|
initialAmount := sdk.NewCoins(sdk.NewInt64Coin("denom", 100))
|
|
label := "demo contract to query"
|
|
contractAddr, _, err := keepers.ContractKeeper.Instantiate(ctx, example.CodeID, example.CreatorAddr, example.CreatorAddr, []byte("{}"), label, initialAmount)
|
|
|
|
require.NoError(t, err)
|
|
return ExampleInstance{
|
|
ExampleContract: example,
|
|
Contract: contractAddr,
|
|
Label: label,
|
|
Deposit: initialAmount,
|
|
}
|
|
}
|
|
|
|
type HackatomExampleInitMsg struct {
|
|
Verifier sdk.AccAddress `json:"verifier"`
|
|
Beneficiary sdk.AccAddress `json:"beneficiary"`
|
|
}
|
|
|
|
func (m HackatomExampleInitMsg) GetBytes(t testing.TB) []byte {
|
|
initMsgBz, err := json.Marshal(m)
|
|
require.NoError(t, err)
|
|
return initMsgBz
|
|
}
|
|
|
|
type IBCReflectExampleInstance struct {
|
|
Contract sdk.AccAddress
|
|
Admin sdk.AccAddress
|
|
CodeID uint64
|
|
ReflectCodeID uint64
|
|
}
|
|
|
|
// InstantiateIBCReflectContract load and instantiate the "./testdata/ibc_reflect.wasm" contract
|
|
func InstantiateIBCReflectContract(t testing.TB, ctx sdk.Context, keepers TestKeepers) IBCReflectExampleInstance {
|
|
reflectID := StoreReflectContract(t, ctx, keepers).CodeID
|
|
ibcReflectID := StoreIBCReflectContract(t, ctx, keepers).CodeID
|
|
|
|
initMsgBz := IBCReflectInitMsg{
|
|
ReflectCodeID: reflectID,
|
|
}.GetBytes(t)
|
|
adminAddr := RandomAccountAddress(t)
|
|
|
|
contractAddr, _, err := keepers.ContractKeeper.Instantiate(ctx, ibcReflectID, adminAddr, adminAddr, initMsgBz, "ibc-reflect-factory", nil)
|
|
require.NoError(t, err)
|
|
return IBCReflectExampleInstance{
|
|
Admin: adminAddr,
|
|
Contract: contractAddr,
|
|
CodeID: ibcReflectID,
|
|
ReflectCodeID: reflectID,
|
|
}
|
|
}
|
|
|
|
type IBCReflectInitMsg struct {
|
|
ReflectCodeID uint64 `json:"reflect_code_id"`
|
|
}
|
|
|
|
func (m IBCReflectInitMsg) GetBytes(t testing.TB) []byte {
|
|
initMsgBz, err := json.Marshal(m)
|
|
require.NoError(t, err)
|
|
return initMsgBz
|
|
}
|
|
|
|
type BurnerExampleInitMsg struct {
|
|
Payout sdk.AccAddress `json:"payout"`
|
|
}
|
|
|
|
func (m BurnerExampleInitMsg) GetBytes(t testing.TB) []byte {
|
|
initMsgBz, err := json.Marshal(m)
|
|
require.NoError(t, err)
|
|
return initMsgBz
|
|
}
|
|
|
|
func fundAccounts(t testing.TB, ctx sdk.Context, am authkeeper.AccountKeeper, bank bankkeeper.Keeper, addr sdk.AccAddress, coins sdk.Coins) {
|
|
acc := am.NewAccountWithAddress(ctx, addr)
|
|
am.SetAccount(ctx, acc)
|
|
NewTestFaucet(t, ctx, bank, minttypes.ModuleName, coins...).Fund(ctx, addr, coins...)
|
|
}
|
|
|
|
var keyCounter uint64
|
|
|
|
// we need to make this deterministic (same every test run), as encoded address size and thus gas cost,
|
|
// depends on the actual bytes (due to ugly CanonicalAddress encoding)
|
|
func keyPubAddr() (crypto.PrivKey, crypto.PubKey, sdk.AccAddress) {
|
|
keyCounter++
|
|
seed := make([]byte, 8)
|
|
binary.BigEndian.PutUint64(seed, keyCounter)
|
|
|
|
key := ed25519.GenPrivKeyFromSecret(seed)
|
|
pub := key.PubKey()
|
|
addr := sdk.AccAddress(pub.Address())
|
|
return key, pub, addr
|
|
}
|