OSDN Git Service

add dpos consensus
[bytom/vapor.git] / protocol / vm / vm.go
1 package vm
2
3 import (
4         "encoding/hex"
5         "fmt"
6         "io"
7         "strings"
8
9         "github.com/vapor/errors"
10 )
11
12 type virtualMachine struct {
13         context *Context
14
15         program      []byte // the program currently executing
16         pc, nextPC   uint32
17         runLimit     int64
18         deferredCost int64
19
20         expansionReserved bool
21
22         // Stores the data parsed out of an opcode. Used as input to
23         // data-pushing opcodes.
24         data []byte
25
26         // CHECKPREDICATE spawns a child vm with depth+1
27         depth int
28
29         // In each of these stacks, stack[len(stack)-1] is the top element.
30         dataStack [][]byte
31         altStack  [][]byte
32 }
33
34 // TraceOut - if non-nil - will receive trace output during
35 // execution.
36 var TraceOut io.Writer
37
38 // Verify program by running VM
39 func Verify(context *Context, gasLimit int64) (gasLeft int64, err error) {
40         defer func() {
41                 if r := recover(); r != nil {
42                         if rErr, ok := r.(error); ok {
43                                 err = errors.Sub(ErrUnexpected, rErr)
44                         } else {
45                                 err = errors.Wrap(ErrUnexpected, r)
46                         }
47                 }
48         }()
49
50         if context.VMVersion != 1 {
51                 return gasLimit, ErrUnsupportedVM
52         }
53
54         vm := &virtualMachine{
55                 expansionReserved: context.TxVersion != nil && *context.TxVersion == 1,
56                 program:           context.Code,
57                 runLimit:          gasLimit,
58                 context:           context,
59         }
60
61         args := context.Arguments
62         for i, arg := range args {
63                 err = vm.push(arg, false)
64                 if err != nil {
65                         return vm.runLimit, errors.Wrapf(err, "pushing initial argument %d", i)
66                 }
67         }
68
69         err = vm.run()
70         if err == nil && vm.falseResult() {
71                 err = ErrFalseVMResult
72         }
73
74         return vm.runLimit, wrapErr(err, vm, args)
75 }
76
77 // falseResult returns true iff the stack is empty or the top
78 // item is false
79 func (vm *virtualMachine) falseResult() bool {
80         return len(vm.dataStack) == 0 || !AsBool(vm.dataStack[len(vm.dataStack)-1])
81 }
82
83 func (vm *virtualMachine) run() error {
84         for vm.pc = 0; vm.pc < uint32(len(vm.program)); { // handle vm.pc updates in step
85                 err := vm.step()
86                 if err != nil {
87                         return err
88                 }
89         }
90         return nil
91 }
92
93 func (vm *virtualMachine) step() error {
94         inst, err := ParseOp(vm.program, vm.pc)
95         if err != nil {
96                 return err
97         }
98
99         vm.nextPC = vm.pc + inst.Len
100
101         if TraceOut != nil {
102                 opname := inst.Op.String()
103                 fmt.Fprintf(TraceOut, "vm %d pc %d limit %d %s", vm.depth, vm.pc, vm.runLimit, opname)
104                 if len(inst.Data) > 0 {
105                         fmt.Fprintf(TraceOut, " %x", inst.Data)
106                 }
107                 fmt.Fprint(TraceOut, "\n")
108         }
109
110         if isExpansion[inst.Op] {
111                 if vm.expansionReserved {
112                         return ErrDisallowedOpcode
113                 }
114                 vm.pc = vm.nextPC
115                 return vm.applyCost(1)
116         }
117
118         vm.deferredCost = 0
119         vm.data = inst.Data
120         err = ops[inst.Op].fn(vm)
121         if err != nil {
122                 return err
123         }
124         err = vm.applyCost(vm.deferredCost)
125         if err != nil {
126                 return err
127         }
128         vm.pc = vm.nextPC
129
130         if TraceOut != nil {
131                 for i := len(vm.dataStack) - 1; i >= 0; i-- {
132                         fmt.Fprintf(TraceOut, "  stack %d: %x\n", len(vm.dataStack)-1-i, vm.dataStack[i])
133                 }
134         }
135         return nil
136 }
137
138 func (vm *virtualMachine) push(data []byte, deferred bool) error {
139         cost := 8 + int64(len(data))
140         if deferred {
141                 vm.deferCost(cost)
142         } else {
143                 err := vm.applyCost(cost)
144                 if err != nil {
145                         return err
146                 }
147         }
148         vm.dataStack = append(vm.dataStack, data)
149         return nil
150 }
151
152 func (vm *virtualMachine) pushBool(b bool, deferred bool) error {
153         return vm.push(BoolBytes(b), deferred)
154 }
155
156 func (vm *virtualMachine) pushInt64(n int64, deferred bool) error {
157         return vm.push(Int64Bytes(n), deferred)
158 }
159
160 func (vm *virtualMachine) pop(deferred bool) ([]byte, error) {
161         if len(vm.dataStack) == 0 {
162                 return nil, ErrDataStackUnderflow
163         }
164         res := vm.dataStack[len(vm.dataStack)-1]
165         vm.dataStack = vm.dataStack[:len(vm.dataStack)-1]
166
167         cost := 8 + int64(len(res))
168         if deferred {
169                 vm.deferCost(-cost)
170         } else {
171                 vm.runLimit += cost
172         }
173
174         return res, nil
175 }
176
177 func (vm *virtualMachine) popInt64(deferred bool) (int64, error) {
178         bytes, err := vm.pop(deferred)
179         if err != nil {
180                 return 0, err
181         }
182         n, err := AsInt64(bytes)
183         return n, err
184 }
185
186 func (vm *virtualMachine) top() ([]byte, error) {
187         if len(vm.dataStack) == 0 {
188                 return nil, ErrDataStackUnderflow
189         }
190         return vm.dataStack[len(vm.dataStack)-1], nil
191 }
192
193 // positive cost decreases runlimit, negative cost increases it
194 func (vm *virtualMachine) applyCost(n int64) error {
195         if n > vm.runLimit {
196                 vm.runLimit = 0
197                 return ErrRunLimitExceeded
198         }
199         vm.runLimit -= n
200         return nil
201 }
202
203 func (vm *virtualMachine) deferCost(n int64) {
204         vm.deferredCost += n
205 }
206
207 func stackCost(stack [][]byte) int64 {
208         result := int64(8 * len(stack))
209         for _, item := range stack {
210                 result += int64(len(item))
211         }
212         return result
213 }
214
215 func wrapErr(err error, vm *virtualMachine, args [][]byte) error {
216         if err == nil {
217                 return nil
218         }
219
220         dis, errDis := Disassemble(vm.program)
221         if errDis != nil {
222                 dis = "???"
223         }
224
225         dataArgs := make([]string, 0, len(args))
226         for _, a := range args {
227                 dataArgs = append(dataArgs, hex.EncodeToString(a))
228         }
229
230         return errors.Wrap(err, fmt.Sprintf("%s [prog %x = %s; args %s]", err.Error(), vm.program, dis, strings.Join(dataArgs, " ")))
231 }