Skip to content

Commit

Permalink
movevm loaderv2 integration (#284)
Browse files Browse the repository at this point in the history
* connect movevm with loaderv2 and introduce module & script cache

* use cache context before query

* disable unstable

* update movevm to latest

* add toml description

---------

Co-authored-by: beer-1 <[email protected]>
  • Loading branch information
sh-cha and beer-1 committed Oct 31, 2024
1 parent c7fdaa1 commit c58bbb5
Show file tree
Hide file tree
Showing 13 changed files with 79 additions and 349 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ ARG TARGETARCH
ARG GOARCH

# See https://github.com/initia-labs/movevm/releases
ENV LIBMOVEVM_VERSION=v0.5.1
ENV LIBMOVEVM_VERSION=v0.6.0

# Install necessary packages
RUN set -eux; apk add --no-cache ca-certificates build-base git cmake
Expand Down
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ require (
github.com/hashicorp/go-metrics v0.5.3
github.com/initia-labs/OPinit v0.5.5
// we also need to update `LIBMOVEVM_VERSION` of Dockerfile#9
github.com/initia-labs/movevm v0.5.1
github.com/initia-labs/movevm v0.6.0
github.com/noble-assets/forwarding/v2 v2.0.0-20240521090705-86712c4c9e43
github.com/pelletier/go-toml v1.9.5
github.com/pkg/errors v0.9.1
Expand Down Expand Up @@ -232,6 +232,7 @@ replace (
// Fix upstream GHSA-h395-qcrw-5vmq vulnerability.
// TODO Remove it: https://github.com/cosmos/cosmos-sdk/issues/10409
github.com/gin-gonic/gin => github.com/gin-gonic/gin v1.8.1

// Downgraded to avoid bugs in following commits which caused simulations to fail.
github.com/syndtr/goleveldb => github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7
)
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -732,8 +732,8 @@ github.com/initia-labs/OPinit/api v0.5.1 h1:zwyJf7HtKJCKvLJ1R9PjVfJO1L+d/jKoeFyT
github.com/initia-labs/OPinit/api v0.5.1/go.mod h1:gHK6DEWb3/DqQD5LjKirUx9jilAh2UioXanoQdgqVfU=
github.com/initia-labs/cometbft v0.0.0-20240923045653-ba99eb347236 h1:+HmPQ1uptOe4r5oQHuHMG5zF1F3maNoEba5uiTUMnlk=
github.com/initia-labs/cometbft v0.0.0-20240923045653-ba99eb347236/go.mod h1:GPHp3/pehPqgX1930HmK1BpBLZPxB75v/dZg8Viwy+o=
github.com/initia-labs/movevm v0.5.1 h1:Nl5SizJIfRLM6clz/zV8dOFUXcnlMP2wOQsZB/mmU2w=
github.com/initia-labs/movevm v0.5.1/go.mod h1:aUWdvFZPdULjJ2McQTE+mLnfnG3CLAz0TWJRFzFFUwg=
github.com/initia-labs/movevm v0.6.0 h1:46JjDfOId6hfeoqYqKWTzPeb5ESQzzm8AhhGWYnFOmE=
github.com/initia-labs/movevm v0.6.0/go.mod h1:aUWdvFZPdULjJ2McQTE+mLnfnG3CLAz0TWJRFzFFUwg=
github.com/jhump/protoreflect v1.15.3 h1:6SFRuqU45u9hIZPJAoZ8c28T3nK64BNdp9w6jFonzls=
github.com/jhump/protoreflect v1.15.3/go.mod h1:4ORHmSBmlCW8fh3xHmJMGyul1zNqZK4Elxc8qKP+p1k=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
Expand Down
16 changes: 16 additions & 0 deletions x/move/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,33 +9,45 @@ import (

// DefaultContractSimulationGasLimit - default max simulation gas
const DefaultContractSimulationGasLimit = uint64(3_000_000)
const DefaultScriptCacheCapacity = uint64(100)
const DefaultModuleCacheCapacity = uint64(500)

const (
flagContractSimulationGasLimit = "move.contract-simulation-gas-limit"
flagScriptCacheCapacity = "move.script-cache-capacity"
flagModuleCacheCapacity = "move.module-cache-capacity"
)

// MoveConfig is the extra config required for move
type MoveConfig struct {
ContractSimulationGasLimit uint64 `mapstructure:"contract-simulation-gas-limit"`
ScriptCacheCapacity uint64 `mapstructure:"script-cache-capacity"`
ModuleCacheCapacity uint64 `mapstructure:"module-cache-capacity"`
}

// DefaultMoveConfig returns the default settings for MoveConfig
func DefaultMoveConfig() MoveConfig {
return MoveConfig{
ContractSimulationGasLimit: DefaultContractSimulationGasLimit,
ScriptCacheCapacity: DefaultScriptCacheCapacity,
ModuleCacheCapacity: DefaultModuleCacheCapacity,
}
}

// GetConfig load config values from the app options
func GetConfig(appOpts servertypes.AppOptions) MoveConfig {
return MoveConfig{
ContractSimulationGasLimit: cast.ToUint64(appOpts.Get(flagContractSimulationGasLimit)),
ScriptCacheCapacity: cast.ToUint64(appOpts.Get(flagScriptCacheCapacity)),
ModuleCacheCapacity: cast.ToUint64(appOpts.Get(flagModuleCacheCapacity)),
}
}

// AddConfigFlags implements servertypes.MoveConfigFlags interface.
func AddConfigFlags(startCmd *cobra.Command) {
startCmd.Flags().Uint64(flagContractSimulationGasLimit, DefaultContractSimulationGasLimit, "Set the max simulation gas for move contract execution")
startCmd.Flags().Uint64(flagScriptCacheCapacity, DefaultScriptCacheCapacity, "Set the script cache capacity")
startCmd.Flags().Uint64(flagModuleCacheCapacity, DefaultModuleCacheCapacity, "Set the module cache capacity")
}

// DefaultConfigTemplate default config template for move module
Expand All @@ -47,4 +59,8 @@ const DefaultConfigTemplate = `
[move]
# The maximum gas amount can be used in a tx simulation call.
contract-simulation-gas-limit = "{{ .MoveConfig.ContractSimulationGasLimit }}"
# The capacity of the script cache in (MiB).
script-cache-capacity = "{{ .MoveConfig.ScriptCacheCapacity }}"
# The capacity of the module cache in (MiB).
module-cache-capacity = "{{ .MoveConfig.ModuleCacheCapacity }}"
`
7 changes: 7 additions & 0 deletions x/move/keeper/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ import (
govtypesv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1"
slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types"

ibcfee "github.com/cosmos/ibc-go/v8/modules/apps/29-fee"
ibctransfer "github.com/cosmos/ibc-go/v8/modules/apps/transfer"
ibcnfttransfer "github.com/initia-labs/initia/x/ibc/nft-transfer"

initiaapp "github.com/initia-labs/initia/app"
initiaappparams "github.com/initia-labs/initia/app/params"
"github.com/initia-labs/initia/x/bank"
Expand Down Expand Up @@ -90,6 +94,9 @@ var ModuleBasics = module.NewBasicManager(
slashing.AppModuleBasic{},
move.AppModuleBasic{},
oracle.AppModuleBasic{},
ibctransfer.AppModuleBasic{},
ibcnfttransfer.AppModuleBasic{},
ibcfee.AppModuleBasic{},
)

// Bond denom should be set for staking test
Expand Down
4 changes: 1 addition & 3 deletions x/move/keeper/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,7 @@ func (k Keeper) Initialize(
}

vmStore := types.NewVMStore(ctx, k.VMStore)
execRes, err := execVM(ctx, k, func(vm types.VMEngine) (vmtypes.ExecutionResult, error) {
return vm.Initialize(vmStore, api, env, vmtypes.NewModuleBundle(modules...), _allowedPublishers)
})
execRes, err := k.initiaMoveVM.Initialize(vmStore, api, env, vmtypes.NewModuleBundle(modules...), _allowedPublishers)
if err != nil {
return err
}
Expand Down
110 changes: 34 additions & 76 deletions x/move/keeper/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,16 +170,14 @@ func (k Keeper) executeEntryFunction(
sdkCtx = sdkCtx.WithGasMeter(storetypes.NewInfiniteGasMeter())

gasBalance := gasForRuntime
execRes, err := execVM(ctx, k, func(vm types.VMEngine) (vmtypes.ExecutionResult, error) {
return vm.ExecuteEntryFunction(
&gasBalance,
types.NewVMStore(sdkCtx, k.VMStore),
NewApi(k, sdkCtx),
types.NewEnv(sdkCtx, ac, ec),
senders,
payload,
)
})
execRes, err := k.initiaMoveVM.ExecuteEntryFunction(
&gasBalance,
types.NewVMStore(sdkCtx, k.VMStore),
NewApi(k, sdkCtx),
types.NewEnv(sdkCtx, ac, ec),
senders,
payload,
)

// consume gas first and check error
gasUsed := gasForRuntime - gasBalance
Expand Down Expand Up @@ -282,16 +280,14 @@ func (k Keeper) executeScript(
sdkCtx = sdkCtx.WithGasMeter(storetypes.NewInfiniteGasMeter())

gasBalance := gasForRuntime
execRes, err := execVM(ctx, k, func(vm types.VMEngine) (vmtypes.ExecutionResult, error) {
return vm.ExecuteScript(
&gasBalance,
types.NewVMStore(sdkCtx, k.VMStore),
NewApi(k, sdkCtx),
types.NewEnv(sdkCtx, ac, ec),
senders,
payload,
)
})
execRes, err := k.initiaMoveVM.ExecuteScript(
&gasBalance,
types.NewVMStore(sdkCtx, k.VMStore),
NewApi(k, sdkCtx),
types.NewEnv(sdkCtx, ac, ec),
senders,
payload,
)

// consume gas first and check error
gasUsed := gasForRuntime - gasBalance
Expand Down Expand Up @@ -392,10 +388,6 @@ func (k Keeper) DispatchMessages(ctx context.Context, messages []vmtypes.CosmosM
}

func (k Keeper) dispatchMessage(parentCtx sdk.Context, message vmtypes.CosmosMessage) (err error) {
var allowFailure bool
var callback *vmtypes.StargateCallback
var callbackSender vmtypes.AccountAddress

ctx, commit := parentCtx.CacheContext()
defer func() {
if r := recover(); r != nil {
Expand All @@ -412,7 +404,7 @@ func (k Keeper) dispatchMessage(parentCtx sdk.Context, message vmtypes.CosmosMes

if !success {
// return error if failed and not allowed to fail
if !allowFailure {
if !message.AllowFailure {
return
}

Expand All @@ -430,48 +422,27 @@ func (k Keeper) dispatchMessage(parentCtx sdk.Context, message vmtypes.CosmosMes
parentCtx.EventManager().EmitEvent(event)

// if callback exists, execute it with parent context becuase it's already committed
if callback != nil {
if message.Callback != nil {
err = k.ExecuteEntryFunctionJSON(
parentCtx,
callbackSender,
callback.ModuleAddress,
callback.ModuleName,
callback.FunctionName,
message.Sender,
message.Callback.ModuleAddress,
message.Callback.ModuleName,
message.Callback.FunctionName,
[]vmtypes.TypeTag{},
[]string{
fmt.Sprintf("\"%d\"", callback.Id),
fmt.Sprintf("\"%d\"", message.Callback.Id),
fmt.Sprintf("%v", success),
},
)
}
}()

// validate basic & signer check is done in HandleVMStargateMsg
var msg proto.Message
if stargateMsg, ok := message.(*vmtypes.CosmosMessage__Stargate); ok {
// callback only exists in stargate message
allowFailure = stargateMsg.Value.AllowFailure
callback = stargateMsg.Value.Callback
callbackSender = stargateMsg.Value.Sender

// validate basic & signer check is done in HandleVMStargateMsg
msg, err = k.HandleVMStargateMsg(ctx, &stargateMsg.Value)
if err != nil {
return
}
} else {
// signer check had been done in moveVM
msg, err = types.ConvertToSDKMessage(ctx, k.MoveBankKeeper(), NewNftKeeper(&k), message, k.ac, k.vc)
if err != nil {
return
}

// conduct validate basic
if msg, ok := msg.(sdk.HasValidateBasic); ok {
err = msg.ValidateBasic()
if err != nil {
return
}
}
msg, err = k.HandleVMStargateMsg(ctx, &message)
if err != nil {
return
}

// find the handler
Expand Down Expand Up @@ -626,15 +597,13 @@ func (k Keeper) executeViewFunction(
gasForRuntime := k.computeGasForRuntime(ctx, gasMeter)

gasBalance := gasForRuntime
viewRes, err := execVM(ctx, k, func(vm types.VMEngine) (vmtypes.ViewOutput, error) {
return vm.ExecuteViewFunction(
&gasBalance,
types.NewVMStore(ctx, k.VMStore),
api,
env,
payload,
)
})
viewRes, err := k.initiaMoveVM.ExecuteViewFunction(
&gasBalance,
types.NewVMStore(ctx, k.VMStore),
api,
env,
payload,
)

// consume gas first and check error
gasUsed := gasForRuntime - gasBalance
Expand All @@ -646,17 +615,6 @@ func (k Keeper) executeViewFunction(
return viewRes, gasUsed, nil
}

// execVM runs vm in separate function statement to release right after execution
// to avoid deadlock even if the function panics
//
// TODO - remove this after loader v2 is installed
func execVM[T any](ctx context.Context, k Keeper, f func(types.VMEngine) (T, error)) (T, error) {
vm := k.acquireVM(ctx)
defer k.releaseVM()

return f(vm)
}

func (k Keeper) computeGasForRuntime(ctx context.Context, gasMeter storetypes.GasMeter) uint64 {
gasForRuntime := gasMeter.Limit() - gasMeter.GasConsumedToLimit()
if isSimulation(ctx) {
Expand Down
33 changes: 9 additions & 24 deletions x/move/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import (
"context"
"errors"

"golang.org/x/sync/semaphore"

"cosmossdk.io/collections"
"cosmossdk.io/core/address"
corestoretypes "cosmossdk.io/core/store"
Expand Down Expand Up @@ -49,10 +47,7 @@ type Keeper struct {

config moveconfig.MoveConfig

// TODO - remove after loader v2
moveVMs []types.VMEngine
moveVMIdx *uint64
moveVMSemaphore *semaphore.Weighted
initiaMoveVM types.VMEngine

feeCollector string
authority string
Expand Down Expand Up @@ -95,21 +90,13 @@ func NewKeeper(
moveConfig.ContractSimulationGasLimit = moveconfig.DefaultContractSimulationGasLimit
}

// use only one VM for now because we need to clear the cache on every module upgrade.
// but if we have multiple VMs, only the VM that executed the module upgrade will have the cache cleared.
vmCount := 1
moveVMIdx := uint64(0)
vms := make([]types.VMEngine, vmCount)
for i := 0; i < vmCount; i++ {
moveVM, err := vm.NewVM(vmtypes.InitiaVMConfig{
// TODO: check this before mainnet
AllowUnstable: true,
})
if err != nil {
panic(err)
}

vms[i] = &moveVM
moveVM, err := vm.NewVM(vmtypes.InitiaVMConfig{
AllowUnstable: false,
ScriptCacheCapacity: moveConfig.ScriptCacheCapacity,
ModuleCacheCapacity: moveConfig.ModuleCacheCapacity,
})
if err != nil {
panic(err)
}

sb := collections.NewSchemaBuilder(storeService)
Expand All @@ -122,9 +109,7 @@ func NewKeeper(
msgRouter: msgRouter,
grpcRouter: grpcRouter,
config: moveConfig,
moveVMs: vms,
moveVMIdx: &moveVMIdx,
moveVMSemaphore: semaphore.NewWeighted(int64(vmCount)),
initiaMoveVM: &moveVM,
distrKeeper: distrKeeper,
StakingKeeper: stakingKeeper,
RewardKeeper: rewardKeeper,
Expand Down
2 changes: 1 addition & 1 deletion x/move/keeper/vm_msg_stargate.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (
vmtypes "github.com/initia-labs/movevm/types"
)

func (k Keeper) HandleVMStargateMsg(ctx context.Context, req *vmtypes.StargateMessage) (proto.Message, error) {
func (k Keeper) HandleVMStargateMsg(ctx context.Context, req *vmtypes.CosmosMessage) (proto.Message, error) {
var sdkMsg sdk.Msg
err := k.cdc.UnmarshalInterfaceJSON(req.Data, &sdkMsg)
if err != nil {
Expand Down
4 changes: 2 additions & 2 deletions x/move/keeper/vm_msg_stargate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,14 @@ func Test_HandleVMStargateMsg(t *testing.T) {
}`, sdk.MsgTypeURL(&govtypes.MsgVote{}), addrs[1]))

// unauthorized test
_, err = input.MoveKeeper.HandleVMStargateMsg(ctx, &vmtypes.StargateMessage{
_, err = input.MoveKeeper.HandleVMStargateMsg(ctx, &vmtypes.CosmosMessage{
Sender: addr0,
Data: jsonData,
})
require.ErrorIs(t, err, sdkerrors.ErrUnauthorized)

// valid test
res, err := input.MoveKeeper.HandleVMStargateMsg(ctx, &vmtypes.StargateMessage{
res, err := input.MoveKeeper.HandleVMStargateMsg(ctx, &vmtypes.CosmosMessage{
Sender: addr1,
Data: jsonData,
})
Expand Down
4 changes: 4 additions & 0 deletions x/move/keeper/vm_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ func (k Keeper) queryCustom(ctx sdk.Context, req *vmtypes.CustomQuery) ([]byte,
return nil, types.ErrNotSupportedCustomQuery
}

// create cache context for query
ctx, _ = ctx.CacheContext()
return customQuery(ctx, req.Data)
}

Expand All @@ -44,6 +46,8 @@ func (k Keeper) queryStargate(ctx sdk.Context, req *vmtypes.StargateQuery) ([]by
return nil, err
}

// create cache context for query
ctx, _ = ctx.CacheContext()
res, err := route(ctx, &abci.RequestQuery{
Data: reqData,
Path: req.Path,
Expand Down
Loading

0 comments on commit c58bbb5

Please sign in to comment.