/
interpreter.go
125 lines (110 loc) · 2.9 KB
/
interpreter.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
117
118
119
120
121
122
123
124
125
package vm
import (
"encoding/hex"
"github.com/koinotice/vite/common/fork"
"github.com/koinotice/vite/common/helper"
"github.com/koinotice/vite/vm/util"
"sync/atomic"
)
type interpreter struct {
instructionSet [256]operation
}
var (
simpleInterpreter = &interpreter{simpleInstructionSet}
offchainSimpleInterpreter = &interpreter{offchainSimpleInstructionSet}
randInterpreter = &interpreter{randInstructionSet}
offchainRandInterpreter = &interpreter{offchainRandInstructionSet}
)
func newInterpreter(blockHeight uint64, offChain bool) *interpreter {
if !fork.IsSeedFork(blockHeight) {
if offChain {
return offchainSimpleInterpreter
}
return simpleInterpreter
}
if offChain {
return offchainRandInterpreter
}
return randInterpreter
}
func (i *interpreter) runLoop(vm *VM, c *contract) (ret []byte, err error) {
c.returnData = nil
var (
op opCode
mem = newMemory()
st = newStack()
pc = uint64(0)
cost uint64
flag bool
)
for atomic.LoadInt32(&vm.abort) == 0 {
currentPc := pc
op = c.getOp(pc)
operation := i.instructionSet[op]
if !operation.valid {
nodeConfig.log.Error("invalid opcode", "op", int(op))
return nil, util.ErrInvalidOpCode
}
if err := operation.validateStack(st); err != nil {
return nil, err
}
var memorySize uint64
if operation.memorySize != nil {
memSize, overflow := helper.BigUint64(operation.memorySize(st))
if overflow {
return nil, util.ErrMemSizeOverflow
}
if memorySize, overflow = helper.SafeMul(helper.ToWordSize(memSize), helper.WordSize); overflow {
return nil, util.ErrMemSizeOverflow
}
}
cost, flag, err = operation.gasCost(vm, c, st, mem, memorySize)
if err != nil {
return nil, err
}
c.quotaLeft, err = util.UseQuotaWithFlag(c.quotaLeft, cost, flag)
if err != nil {
return nil, err
}
if memorySize > 0 {
mem.resize(memorySize)
}
res, err := operation.execute(&pc, vm, c, mem, st)
if nodeConfig.IsDebug {
currentCode := ""
if currentPc < uint64(len(c.code)) {
currentCode = hex.EncodeToString(c.code[currentPc:])
}
storageMap, err := c.db.DebugGetStorage()
if err != nil {
nodeConfig.interpreterLog.Error("vm step, get storage failed")
}
nodeConfig.interpreterLog.Info("vm step",
"blockType", c.block.BlockType,
"address", c.block.AccountAddress.String(),
"height", c.block.Height,
"fromHash", c.block.FromBlockHash.String(),
"\ncurrent code", currentCode,
"\nop", opCodeToString[op],
"pc", currentPc,
"quotaLeft", c.quotaLeft,
"\nstack", st.print(),
"\nmemory", mem.print(),
"\nstorage", util.PrintMap(storageMap))
}
if operation.returns {
c.returnData = res
}
switch {
case err != nil:
return nil, err
case operation.halts:
return res, nil
case operation.reverts:
return res, util.ErrExecutionReverted
case !operation.jumps:
pc++
}
}
panic(util.ErrExecutionCanceled)
}