package clock_test
import (
"crypto/sha256"
"encoding/json"
"testing"
"time"
wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types"
"github.com/stretchr/testify/suite"
_ "embed"
tmproto "github.com/cometbft/cometbft/proto/tendermint/types"
"github.com/cosmos/cosmos-sdk/testutil/testdata"
sdk "github.com/cosmos/cosmos-sdk/types"
minttypes "github.com/cosmos/cosmos-sdk/x/mint/types"
"github.com/cybercongress/go-cyber/v7/app"
clock "github.com/cybercongress/go-cyber/v7/x/clock"
"github.com/cybercongress/go-cyber/v7/x/clock/types"
)
type EndBlockerTestSuite struct {
suite.Suite
ctx sdk.Context
app *app.App
}
func TestEndBlockerTestSuite(t *testing.T) {
suite.Run(t, new(EndBlockerTestSuite))
}
func (s *EndBlockerTestSuite) SetupTest() {
app := app.Setup(s.T())
ctx := app.BaseApp.NewContext(false, tmproto.Header{
ChainID: "testing",
Height: 10,
Time: time.Now(),
})
s.app = app
s.ctx = ctx
}
var clockContract []byte
var burnContract []byte
func (s *EndBlockerTestSuite) StoreCode(wasmContract []byte) {
_, _, sender := testdata.KeyTestPubAddr()
msg := wasmtypes.MsgStoreCodeFixture(func(m *wasmtypes.MsgStoreCode) {
m.WASMByteCode = wasmContract
m.Sender = sender.String()
})
rsp, err := s.app.MsgServiceRouter().Handler(msg)(s.ctx, msg)
s.Require().NoError(err)
var result wasmtypes.MsgStoreCodeResponse
s.Require().NoError(s.app.AppCodec().Unmarshal(rsp.Data, &result))
s.Require().Equal(uint64(1), result.CodeID)
expHash := sha256.Sum256(wasmContract)
s.Require().Equal(expHash[:], result.Checksum)
info := s.app.AppKeepers.WasmKeeper.GetCodeInfo(s.ctx, 1)
s.Require().NotNil(info)
s.Require().Equal(expHash[:], info.CodeHash)
s.Require().Equal(sender.String(), info.Creator)
s.Require().Equal(wasmtypes.DefaultParams().InstantiateDefaultPermission.With(sender), info.InstantiateConfig)
}
func (s *EndBlockerTestSuite) InstantiateContract(sender string, admin string) string {
msgStoreCode := wasmtypes.MsgStoreCodeFixture(func(m *wasmtypes.MsgStoreCode) {
m.WASMByteCode = clockContract
m.Sender = sender
})
_, err := s.app.MsgServiceRouter().Handler(msgStoreCode)(s.ctx, msgStoreCode)
s.Require().NoError(err)
msgInstantiate := wasmtypes.MsgInstantiateContractFixture(func(m *wasmtypes.MsgInstantiateContract) {
m.Sender = sender
m.Admin = admin
m.Msg = []byte(`{}`)
})
resp, err := s.app.MsgServiceRouter().Handler(msgInstantiate)(s.ctx, msgInstantiate)
s.Require().NoError(err)
var result wasmtypes.MsgInstantiateContractResponse
s.Require().NoError(s.app.AppCodec().Unmarshal(resp.Data, &result))
contractInfo := s.app.AppKeepers.WasmKeeper.GetContractInfo(s.ctx, sdk.MustAccAddressFromBech32(result.Address))
s.Require().Equal(contractInfo.CodeID, uint64(1))
s.Require().Equal(contractInfo.Admin, admin)
s.Require().Equal(contractInfo.Creator, sender)
return result.Address
}
func (s *EndBlockerTestSuite) FundAccount(ctx sdk.Context, addr sdk.AccAddress, amounts sdk.Coins) error {
if err := s.app.AppKeepers.BankKeeper.MintCoins(ctx, minttypes.ModuleName, amounts); err != nil {
return err
}
return s.app.AppKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, minttypes.ModuleName, addr, amounts)
}
func (s *EndBlockerTestSuite) registerContract() string {
_, _, sender := testdata.KeyTestPubAddr()
_, _, admin := testdata.KeyTestPubAddr()
_ = s.FundAccount(s.ctx, sender, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(1_000_000))))
_ = s.FundAccount(s.ctx, admin, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(1_000_000))))
contractAddress := s.InstantiateContract(sender.String(), admin.String())
clockKeeper := s.app.AppKeepers.ClockKeeper
err := clockKeeper.RegisterContract(s.ctx, admin.String(), contractAddress)
s.Require().NoError(err)
contract, err := clockKeeper.GetClockContract(s.ctx, contractAddress)
s.Require().NoError(err)
s.Require().Equal(contractAddress, contract.ContractAddress)
s.ctx = s.ctx.WithBlockHeight(11)
return contract.ContractAddress
}
func (s *EndBlockerTestSuite) TestEndBlocker() {
clockKeeper := s.app.AppKeepers.ClockKeeper
s.StoreCode(clockContract)
contractAddress := s.registerContract()
val := s.queryContract(contractAddress)
s.Require().Equal(int64(0), val)
s.callEndBlocker()
val = s.queryContract(contractAddress)
s.Require().Equal(int64(1), val)
s.updateGasLimit(65_000)
s.callEndBlocker()
contract, err := clockKeeper.GetClockContract(s.ctx, contractAddress)
s.Require().NoError(err)
s.Require().True(contract.IsJailed)
s.updateGasLimit(types.DefaultParams().ContractGasLimit)
s.callEndBlocker()
err = clockKeeper.SetJailStatus(s.ctx, contractAddress, false)
s.Require().NoError(err)
contract, err = clockKeeper.GetClockContract(s.ctx, contractAddress)
s.Require().NoError(err)
s.Require().False(contract.IsJailed)
s.callEndBlocker()
val = s.queryContract(contractAddress)
s.Require().Equal(int64(2), val)
}
func (s *EndBlockerTestSuite) TestInvalidContract() {
clockKeeper := s.app.AppKeepers.ClockKeeper
s.StoreCode(burnContract)
contractAddress := s.registerContract()
s.callEndBlocker()
contract, err := clockKeeper.GetClockContract(s.ctx, contractAddress)
s.Require().NoError(err)
s.Require().True(contract.IsJailed)
}
func (s *EndBlockerTestSuite) TestPerformance() {
s.StoreCode(burnContract)
numContracts := 1000
for x := 0; x < numContracts; x++ {
_ = s.registerContract()
}
clockKeeper := s.app.AppKeepers.ClockKeeper
contracts, err := clockKeeper.GetAllContracts(s.ctx)
s.Require().NoError(err)
s.Require().Len(contracts, numContracts)
s.callEndBlocker()
contracts, err = clockKeeper.GetAllContracts(s.ctx)
s.Require().NoError(err)
for _, contract := range contracts {
s.Require().True(contract.IsJailed)
}
}
func (s *EndBlockerTestSuite) updateGasLimit(gasLimit uint64) {
params := types.DefaultParams()
params.ContractGasLimit = gasLimit
k := s.app.AppKeepers.ClockKeeper
store := s.ctx.KVStore(k.GetStore())
bz := k.GetCdc().MustMarshal(¶ms)
store.Set(types.ParamsKey, bz)
s.ctx = s.ctx.WithBlockHeight(s.ctx.BlockHeight() + 1)
}
func (s *EndBlockerTestSuite) callEndBlocker() {
clock.EndBlocker(s.ctx, s.app.AppKeepers.ClockKeeper)
s.ctx = s.ctx.WithBlockHeight(s.ctx.BlockHeight() + 1)
}
func (s *EndBlockerTestSuite) queryContract(contractAddress string) int64 {
query := `{"get_config":{}}`
output, err := s.app.AppKeepers.WasmKeeper.QuerySmart(s.ctx, sdk.MustAccAddressFromBech32(contractAddress), []byte(query))
s.Require().NoError(err)
var val struct {
Val int64 `json:"val"`
}
err = json.Unmarshal(output, &val)
s.Require().NoError(err)
return val.Val
}