OSDN Git Service

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