diff --git a/CHANGELOG.md b/CHANGELOG.md index 8423d1fce..1891da599 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,8 @@ ## [unreleased] ### Improvements -- [\#593](https://github.com/terra-money/core/pull/588) revert jemalloc integration from wasmvm & add troubleshoot section to docs +- [\#601](https://github.com/terra-money/core/pull/601) revert readvm-pool feature +- [\#593](https://github.com/terra-money/core/pull/593) revert jemalloc integration from wasmvm & add troubleshoot section to docs ## [v0.5.9] diff --git a/go.mod b/go.mod index 2bae06c25..9a411d264 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,6 @@ require ( github.com/stretchr/testify v1.7.0 github.com/tendermint/tendermint v0.34.13 github.com/tendermint/tm-db v0.6.4 - golang.org/x/sync v0.0.0-20210220032951-036812b2e83c google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c google.golang.org/grpc v1.40.0 gopkg.in/yaml.v2 v2.4.0 diff --git a/go.sum b/go.sum index b0b975a01..c28cb040a 100644 --- a/go.sum +++ b/go.sum @@ -1240,7 +1240,6 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= diff --git a/x/wasm/config/config.go b/x/wasm/config/config.go index 36f6132a8..4dae6095c 100644 --- a/x/wasm/config/config.go +++ b/x/wasm/config/config.go @@ -8,11 +8,9 @@ import ( // config default values const ( - DefaultContractQueryGasLimit = uint64(3000000) - DefaultContractDebugMode = false - DefaultWriteVMMemoryCacheSize = uint32(500) - DefaultReadVMMemoryCacheSize = uint32(300) - DefaultNumReadVM = uint32(1) + DefaultContractQueryGasLimit = uint64(3000000) + DefaultContractDebugMode = false + DefaultContractMemoryCacheSize = uint32(100) ) // DBDir used to store wasm data to @@ -28,35 +26,25 @@ type Config struct { // The flag to specify whether print contract logs or not ContractDebugMode bool `mapstructure:"contract-debug-mode"` - // The write WASM VM memory cache size in MiB not bytes - WriteVMMemoryCacheSize uint32 `mapstructure:"write-vm-memory-cache-size"` - - // The read WASM VM memory cache size in MiB not bytes - ReadVMMemoryCacheSize uint32 `mapstructure:"read-vm-memory-cache-size"` - - // The number of read WASM VMs - NumReadVMs uint32 `mapstructure:"num-read-vms"` + // The WASM VM memory cache size in MiB not bytes + ContractMemoryCacheSize uint32 `mapstructure:"contract-memory-cache-size"` } // DefaultConfig returns the default settings for WasmConfig func DefaultConfig() *Config { return &Config{ - ContractQueryGasLimit: DefaultContractQueryGasLimit, - ContractDebugMode: DefaultContractDebugMode, - WriteVMMemoryCacheSize: DefaultWriteVMMemoryCacheSize, - ReadVMMemoryCacheSize: DefaultReadVMMemoryCacheSize, - NumReadVMs: DefaultNumReadVM, + ContractQueryGasLimit: DefaultContractQueryGasLimit, + ContractDebugMode: DefaultContractDebugMode, + ContractMemoryCacheSize: DefaultContractMemoryCacheSize, } } // GetConfig load config values from the app options func GetConfig(appOpts servertypes.AppOptions) *Config { return &Config{ - ContractQueryGasLimit: cast.ToUint64(appOpts.Get("wasm.contract-query-gas-limit")), - ContractDebugMode: cast.ToBool(appOpts.Get("wasm.contract-debug-mode")), - WriteVMMemoryCacheSize: cast.ToUint32(appOpts.Get("wasm.write-vm-memory-cache-size")), - ReadVMMemoryCacheSize: cast.ToUint32(appOpts.Get("wasm.read-vm-memory-cache-size")), - NumReadVMs: cast.ToUint32(appOpts.Get("wasm.num-read-vms")), + ContractQueryGasLimit: cast.ToUint64(appOpts.Get("wasm.contract-query-gas-limit")), + ContractDebugMode: cast.ToBool(appOpts.Get("wasm.contract-debug-mode")), + ContractMemoryCacheSize: cast.ToUint32(appOpts.Get("wasm.contract-memory-cache-size")), } } @@ -71,12 +59,6 @@ contract-query-gas-limit = "{{ .WASMConfig.ContractQueryGasLimit }}" # The flag to specify whether print contract logs or not contract-debug-mode = "{{ .WASMConfig.ContractDebugMode }}" -# The write WASM VM memory cache size in MiB not bytes -write-vm-memory-cache-size = "{{ .WASMConfig.WriteVMMemoryCacheSize }}" - -# The read WASM VM memory cache size in MiB not bytes -read-vm-memory-cache-size = "{{ .WASMConfig.ReadVMMemoryCacheSize }}" - -# The number of read WASM VMs -num-read-vms = "{{ .WASMConfig.NumReadVMs }}" +# The WASM VM memory cache size in MiB not bytes +contract-memory-cache-size = "{{ .WASMConfig.ContractMemoryCacheSize }}" ` diff --git a/x/wasm/keeper/contract.go b/x/wasm/keeper/contract.go index e6656833e..443d5c1fa 100644 --- a/x/wasm/keeper/contract.go +++ b/x/wasm/keeper/contract.go @@ -1,6 +1,7 @@ package keeper import ( + "context" "time" wasmvmtypes "github.com/CosmWasm/wasmvm/types" @@ -426,21 +427,13 @@ func (k Keeper) queryToContract(ctx sdk.Context, contractAddress sdk.AccAddress, env := types.NewEnv(ctx, contractAddress) - // when the vm is given, use that given vm - var wasmVM types.WasmerEngine - if vm := ctx.Context().Value(types.QueryWasmVMContextKey); vm != nil { - wasmVM = vm.(types.WasmerEngine) - } else { - wasmVM = k.wasmVM - } - - // assert max depth to prevent stack overflow - if err := wasmVM.IncreaseQueryDepth(); err != nil { + // assert and increase query depth + ctx, err = assertAndIncreaseQueryDepth(ctx) + if err != nil { return nil, err } - defer wasmVM.DecreaseQueryDepth() - queryResult, gasUsed, err := wasmVM.Query( + queryResult, gasUsed, err := k.wasmVM.Query( codeInfo.CodeHash, env, queryMsg, @@ -461,6 +454,24 @@ func (k Keeper) queryToContract(ctx sdk.Context, contractAddress sdk.AccAddress, return queryResult, err } +func assertAndIncreaseQueryDepth(ctx sdk.Context) (sdk.Context, error) { + var queryDepth uint8 + if depth := ctx.Context().Value(types.WasmVMQueryDepthContextKey); depth != nil { + queryDepth = depth.(uint8) + } else { + queryDepth = 1 + } + + if queryDepth > types.ContractMaxQueryDepth { + return ctx, types.ErrExceedMaxQueryDepth + } + + // set next query depth + ctx = ctx.WithContext(context.WithValue(ctx.Context(), types.WasmVMQueryDepthContextKey, queryDepth+1)) + + return ctx, nil +} + func (k Keeper) getContractDetails(ctx sdk.Context, contractAddress sdk.AccAddress) (codeInfo types.CodeInfo, contractStorePrefix prefix.Store, err error) { store := ctx.KVStore(k.storeKey) diff --git a/x/wasm/keeper/keeper.go b/x/wasm/keeper/keeper.go index d0d404d1d..0d59ffdc1 100644 --- a/x/wasm/keeper/keeper.go +++ b/x/wasm/keeper/keeper.go @@ -4,10 +4,8 @@ import ( "encoding/binary" "fmt" "path/filepath" - "sync" "github.com/tendermint/tendermint/libs/log" - "golang.org/x/sync/semaphore" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store/prefix" @@ -34,10 +32,7 @@ type Keeper struct { serviceRouter types.MsgServiceRouter queryRouter types.GRPCQueryRouter - wasmVM types.WasmerEngine - wasmReadVMPool *[]types.WasmerEngine - wasmReadVMSemaphore *semaphore.Weighted - wasmReadVMMutex *sync.Mutex + wasmVM types.WasmerEngine querier types.Querier msgParser types.MsgParser @@ -66,65 +61,35 @@ func NewKeeper( } // prevent zero write vm cache - if wasmConfig.WriteVMMemoryCacheSize == 0 { - wasmConfig.WriteVMMemoryCacheSize = config.DefaultWriteVMMemoryCacheSize + if wasmConfig.ContractMemoryCacheSize == 0 { + wasmConfig.ContractMemoryCacheSize = config.DefaultContractMemoryCacheSize } - var writeWasmVM types.WasmerEngine - if vm, err := wasmvm.NewVM( + vm, err := wasmvm.NewVM( filepath.Join(homePath, config.DBDir), supportedFeatures, types.ContractMemoryLimit, wasmConfig.ContractDebugMode, - wasmConfig.WriteVMMemoryCacheSize, - ); err != nil { - panic(err) - } else { - writeWasmVM = types.NewWasmerEngineWithQueryDepth(vm) - } + wasmConfig.ContractMemoryCacheSize, + ) - // prevent zero read vm - if wasmConfig.NumReadVMs == 0 { - wasmConfig.NumReadVMs = config.DefaultNumReadVM - } - - // prevent zero read vm cache - if wasmConfig.ReadVMMemoryCacheSize == 0 { - wasmConfig.ReadVMMemoryCacheSize = config.DefaultReadVMMemoryCacheSize - } - - numReadVms := wasmConfig.NumReadVMs - wasmReadVMPool := make([]types.WasmerEngine, numReadVms) - for i := uint32(0); i < numReadVms; i++ { - if vm, err := wasmvm.NewVM( - filepath.Join(homePath, config.DBDir), - supportedFeatures, - types.ContractMemoryLimit, - wasmConfig.ContractDebugMode, - wasmConfig.ReadVMMemoryCacheSize, - ); err != nil { - panic(err) - } else { - wasmReadVMPool[i] = types.NewWasmerEngineWithQueryDepth(vm) - } + if err != nil { + panic(err) } return Keeper{ - storeKey: storeKey, - cdc: cdc, - paramSpace: paramspace, - wasmVM: writeWasmVM, - wasmReadVMPool: &wasmReadVMPool, - wasmReadVMSemaphore: semaphore.NewWeighted(int64(numReadVms)), - wasmReadVMMutex: &sync.Mutex{}, - accountKeeper: accountKeeper, - bankKeeper: bankKeeper, - treasuryKeeper: treasuryKeeper, - serviceRouter: serviceRouter, - queryRouter: queryRouter, - wasmConfig: wasmConfig, - msgParser: types.NewWasmMsgParser(), - querier: types.NewWasmQuerier(), + storeKey: storeKey, + cdc: cdc, + paramSpace: paramspace, + wasmVM: vm, + accountKeeper: accountKeeper, + bankKeeper: bankKeeper, + treasuryKeeper: treasuryKeeper, + serviceRouter: serviceRouter, + queryRouter: queryRouter, + wasmConfig: wasmConfig, + msgParser: types.NewWasmMsgParser(), + querier: types.NewWasmQuerier(), } } diff --git a/x/wasm/keeper/legacy_querier.go b/x/wasm/keeper/legacy_querier.go index 405bab09d..3b348733a 100644 --- a/x/wasm/keeper/legacy_querier.go +++ b/x/wasm/keeper/legacy_querier.go @@ -1,7 +1,6 @@ package keeper import ( - "context" "fmt" "runtime/debug" @@ -117,15 +116,8 @@ func queryContractStore(ctx sdk.Context, req abci.RequestQuery, k Keeper, legacy return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) } - wasmVM, err := k.acquireWasmVM(sdk.WrapSDKContext(ctx)) - if err != nil { - return nil, sdkerrors.Wrap(types.ErrContractQueryFailed, err.Error()) - } - // recover from out-of-gas panic defer func() { - k.releaseWasmVM(wasmVM) - if r := recover(); r != nil { switch rType := r.(type) { case sdk.ErrorOutOfGas: @@ -148,8 +140,6 @@ func queryContractStore(ctx sdk.Context, req abci.RequestQuery, k Keeper, legacy } }() - // store query wasmvm in the context - ctx = ctx.WithContext(context.WithValue(ctx.Context(), types.QueryWasmVMContextKey, wasmVM)) bz, err = k.queryToContract(ctx, params.ContractAddress, params.Msg) return diff --git a/x/wasm/keeper/pool.go b/x/wasm/keeper/pool.go deleted file mode 100644 index 0d7bf5610..000000000 --- a/x/wasm/keeper/pool.go +++ /dev/null @@ -1,29 +0,0 @@ -package keeper - -import ( - "context" - - "github.com/terra-money/core/x/wasm/types" -) - -func (k Keeper) acquireWasmVM(ctx context.Context) (types.WasmerEngine, error) { - err := k.wasmReadVMSemaphore.Acquire(ctx, 1) - if err != nil { - return nil, err - } - - k.wasmReadVMMutex.Lock() - wasmVM := (*k.wasmReadVMPool)[0] - *k.wasmReadVMPool = (*k.wasmReadVMPool)[1:] - k.wasmReadVMMutex.Unlock() - - return wasmVM, nil -} - -func (k Keeper) releaseWasmVM(wasmVM types.WasmerEngine) { - k.wasmReadVMMutex.Lock() - *k.wasmReadVMPool = append(*k.wasmReadVMPool, wasmVM) - k.wasmReadVMMutex.Unlock() - - k.wasmReadVMSemaphore.Release(1) -} diff --git a/x/wasm/keeper/querier.go b/x/wasm/keeper/querier.go index 6f03bc6f0..80f7b50bb 100644 --- a/x/wasm/keeper/querier.go +++ b/x/wasm/keeper/querier.go @@ -87,14 +87,8 @@ func (q querier) ContractStore(c context.Context, req *types.QueryContractStoreR return nil, status.Error(codes.InvalidArgument, err.Error()) } - wasmVM, err := q.acquireWasmVM(c) - if err != nil { - return nil, status.Error(codes.Internal, err.Error()) - } - // recover from out-of-gas panic defer func() { - q.releaseWasmVM(wasmVM) if r := recover(); r != nil { switch rType := r.(type) { @@ -120,8 +114,6 @@ func (q querier) ContractStore(c context.Context, req *types.QueryContractStoreR } }() - // store query wasmvm in the context - ctx = ctx.WithContext(context.WithValue(ctx.Context(), types.QueryWasmVMContextKey, wasmVM)) bz, err := q.queryToContract(ctx, contractAddr, req.QueryMsg) if err != nil { return nil, status.Error(codes.Internal, err.Error()) diff --git a/x/wasm/keeper/recursive_test.go b/x/wasm/keeper/recursive_test.go index 88eb5fb11..fa3103413 100644 --- a/x/wasm/keeper/recursive_test.go +++ b/x/wasm/keeper/recursive_test.go @@ -1,7 +1,6 @@ package keeper import ( - "context" "encoding/json" "io/ioutil" "testing" @@ -346,19 +345,13 @@ func TestLimitRecursiveQueryGas(t *testing.T) { func TestLimitRecursiveQueryDepth(t *testing.T) { contractAddr, _, ctx, keeper, _ := initRecurseContract(t) - // pick query wasmvm - wasmvm, err := keeper.acquireWasmVM(ctx.Context()) - require.NoError(t, err) - defer keeper.releaseWasmVM(wasmvm) - - ctx = ctx.WithContext(context.WithValue(ctx.Context(), types.QueryWasmVMContextKey, wasmvm)) // exceed max query depth msg := buildQuery(t, Recurse{ Depth: types.ContractMaxQueryDepth, }) - _, err = keeper.queryToContract(ctx, contractAddr, msg) + _, err := keeper.queryToContract(ctx, contractAddr, msg) require.Error(t, err) msg = buildQuery(t, Recurse{ diff --git a/x/wasm/types/keys.go b/x/wasm/types/keys.go index 9371fe0bb..5cb9f9c09 100644 --- a/x/wasm/types/keys.go +++ b/x/wasm/types/keys.go @@ -21,8 +21,8 @@ const ( // RouterKey is the msg router key for the wasm module RouterKey = ModuleName - // QueryWasmVMContextKey is the context key to store wasmvm in query context - QueryWasmVMContextKey = "wasmvm" + // WasmVMQueryDepthContextKey context key to keep query depth + WasmVMQueryDepthContextKey = "wasmvm-query-depth" ) // Keys for wasm store diff --git a/x/wasm/types/wasmer_engine.go b/x/wasm/types/wasmer_engine.go index 393462bb8..183626fb2 100644 --- a/x/wasm/types/wasmer_engine.go +++ b/x/wasm/types/wasmer_engine.go @@ -8,37 +8,6 @@ import ( // ContractMaxQueryDepth maximum recursive query depth allowed const ContractMaxQueryDepth = 20 -var _ WasmerEngine = &WasmerEngineWithQueryDepth{} - -// WasmerEngineWithQueryDepth VM wrapper with depth counter to prevent -// stack overflow -type WasmerEngineWithQueryDepth struct { - *wasmvm.VM - QueryDepth uint8 -} - -// NewWasmerEngineWithQueryDepth wrap wasmer engine with query depth checker -func NewWasmerEngineWithQueryDepth(wasmVM *wasmvm.VM) *WasmerEngineWithQueryDepth { - wasmerEngine := WasmerEngineWithQueryDepth{VM: wasmVM} - return &wasmerEngine -} - -// IncreaseQueryDepth increase execution depth by 1 and check whether -// the depth exceeds max one or not -func (wasmer *WasmerEngineWithQueryDepth) IncreaseQueryDepth() error { - if wasmer.QueryDepth >= ContractMaxQueryDepth { - return ErrExceedMaxQueryDepth - } - - wasmer.QueryDepth++ - return nil -} - -// DecreaseQueryDepth decrease execution depth by 1 -func (wasmer *WasmerEngineWithQueryDepth) DecreaseQueryDepth() { - wasmer.QueryDepth-- -} - // WasmerEngine defines the WASM contract runtime engine. type WasmerEngine interface { @@ -153,11 +122,4 @@ type WasmerEngine interface { // Cleanup should be called when no longer using this to free resources on the rust-side Cleanup() - - // IncreaseQueryDepth will increase query depth by 1 and assert the current depth - // reached out the maximum - IncreaseQueryDepth() error - - // DecreaseQueryDepth will decrease query depth by 1 - DecreaseQueryDepth() }