-
Notifications
You must be signed in to change notification settings - Fork 49
/
vm.go
116 lines (103 loc) · 3.74 KB
/
vm.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
package wasm
import (
"errors"
"fmt"
"github.com/idena-network/idena-go/blockchain/attachments"
"github.com/idena-network/idena-go/blockchain/types"
"github.com/idena-network/idena-go/common/math"
"github.com/idena-network/idena-go/config"
"github.com/idena-network/idena-go/core/appstate"
"github.com/idena-network/idena-go/vm/costs"
"github.com/idena-network/idena-wasm-binding/lib"
)
type WasmVM struct {
appState *appstate.AppState
blockHeaderProvider BlockHeaderProvider
head *types.Header
cfg *config.Config
commitToState bool
}
func (vm *WasmVM) deploy(tx *types.Transaction, limit uint64) (env *WasmEnv, gasUsed uint64, actionResult []byte, err error) {
ctx := NewContractContext(tx)
env = NewWasmEnv(vm.appState, vm.blockHeaderProvider, ctx, vm.head, "deploy", vm.cfg.IsDebug, vm.commitToState, vm.cfg.Consensus.EnableUpgrade12)
attach := attachments.ParseDeployContractAttachment(tx)
actionResult = []byte{}
if attach == nil {
return env, limit, actionResult, errors.New("can't parse attachment")
}
if len(attach.Code) == 0 {
return env, limit, actionResult, errors.New("code is empty")
}
defer func() {
if r := recover(); r != nil {
err = errors.New(fmt.Sprint(r))
gasUsed = limit
}
}()
env.Deploy(attach.Code)
gasUsed, actionResult, err = lib.Deploy(lib.NewGoAPI(env, &lib.GasMeter{}), attach.Code, attach.Args, ctx.ContractAddr(), limit, vm.cfg.IsDebug)
return env, gasUsed, actionResult, err
}
func (vm *WasmVM) call(tx *types.Transaction, limit uint64) (env *WasmEnv, gasUsed uint64, actionResult []byte, method string, err error) {
attachment := attachments.ParseCallContractAttachment(tx)
method = ""
if attachment != nil {
method = attachment.Method
}
ctx := NewContractContext(tx)
env = NewWasmEnv(vm.appState, vm.blockHeaderProvider, ctx, vm.head, method, vm.cfg.IsDebug, vm.commitToState, vm.cfg.Consensus.EnableUpgrade12)
contract := *tx.To
code := vm.appState.State.GetContractCode(contract)
actionResult = []byte{}
if len(code) == 0 {
return env, limit, actionResult, method, errors.New("code is empty")
}
if attachment == nil {
return env, limit, actionResult, method, errors.New("can't parse attachment")
}
defer func() {
if r := recover(); r != nil {
err = errors.New(fmt.Sprint(r))
gasUsed = limit
}
}()
gasUsed, actionResult, err = lib.Execute(lib.NewGoAPI(env, &lib.GasMeter{}), code, attachment.Method, attachment.Args, contract, limit, vm.cfg.IsDebug)
return env, gasUsed, actionResult, attachment.Method, err
}
func (vm *WasmVM) Run(tx *types.Transaction, wasmGasLimit uint64) *types.TxReceipt {
var usedGas uint64
var err error
var env *WasmEnv
var actionResult []byte
var method string
sender, _ := types.Sender(tx)
switch tx.Type {
case types.DeployContractTx:
method = "deploy"
env, usedGas, actionResult, err = vm.deploy(tx, wasmGasLimit)
case types.CallContractTx:
env, usedGas, actionResult, method, err = vm.call(tx, wasmGasLimit)
}
var events []*types.TxEvent
if err == nil && vm.commitToState {
events = env.InternalCommit()
}
if wasmGasLimit >= 0 {
usedGas = math.Min(usedGas, wasmGasLimit)
}
usedGas = costs.WasmGasToGas(usedGas)
return &types.TxReceipt{
GasUsed: usedGas,
TxHash: tx.Hash(),
Error: err,
Success: err == nil,
From: sender,
ContractAddress: env.ContractAddress(nil),
Events: events,
Method: method,
ActionResult: actionResult,
}
}
func NewWasmVM(appState *appstate.AppState, blockHeaderProvider BlockHeaderProvider, head *types.Header, cfg *config.Config, commitToState bool) *WasmVM {
return &WasmVM{appState: appState, blockHeaderProvider: blockHeaderProvider, head: head, cfg: cfg, commitToState: commitToState}
}