-
Notifications
You must be signed in to change notification settings - Fork 95
/
interpreter.go
123 lines (108 loc) · 2.77 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
package vm
import (
"encoding/hex"
"fmt"
"github.com/vitelabs/go-vite/common/fork"
"github.com/vitelabs/go-vite/common/helper"
"github.com/vitelabs/go-vite/vm/util"
"sync/atomic"
)
type Interpreter struct {
instructionSet [256]operation
}
var (
simpleInterpreter = &Interpreter{simpleInstructionSet}
offchainSimpleInterpreter = &Interpreter{offchainSimpleInstructionSet}
mintInterpreter = &Interpreter{mintInstructionSet}
offchainMintInterpreter = &Interpreter{offchainMintInstructionSet}
)
func NewInterpreter(blockHeight uint64, offChain bool) *Interpreter {
if fork.IsMintFork(blockHeight) {
if offChain {
return offchainMintInterpreter
} else {
return mintInterpreter
}
} else {
if offChain {
return offchainSimpleInterpreter
} else {
return simpleInterpreter
}
}
}
func (i *Interpreter) Run(vm *VM, c *contract) (ret []byte, err error) {
c.returnData = nil
var (
op opCode
mem = newMemory()
st = newStack()
pc = uint64(0)
cost uint64
)
for atomic.LoadInt32(&vm.abort) == 0 {
currentPc := pc
op = c.getOp(pc)
operation := i.instructionSet[op]
if !operation.valid {
return nil, fmt.Errorf("invalid opcode 0x%x", int(op))
}
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, err = operation.gasCost(vm, c, st, mem, memorySize)
if err != nil {
return nil, err
}
c.quotaLeft, err = util.UseQuota(c.quotaLeft, cost)
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:])
}
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, "quotaRefund", c.quotaRefund,
"\nstack", st.print(),
"\nmemory", mem.print(),
"\nstorage", util.PrintMap(c.db.DebugGetStorage()))
}
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++
}
}
return nil, nil
}