github.com/Bytom/bytom@v1.1.2-0.20210127130405-ae40204c0b09/protocol/vm/vm.go (about) 1 package vm 2 3 import ( 4 "encoding/hex" 5 "fmt" 6 "io" 7 "strings" 8 9 "github.com/bytom/bytom/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 136 return nil 137 } 138 139 func (vm *virtualMachine) push(data []byte, deferred bool) error { 140 cost := 8 + int64(len(data)) 141 if deferred { 142 vm.deferCost(cost) 143 } else { 144 err := vm.applyCost(cost) 145 if err != nil { 146 return err 147 } 148 } 149 vm.dataStack = append(vm.dataStack, data) 150 return nil 151 } 152 153 func (vm *virtualMachine) pushBool(b bool, deferred bool) error { 154 return vm.push(BoolBytes(b), deferred) 155 } 156 157 func (vm *virtualMachine) pushInt64(n int64, deferred bool) error { 158 return vm.push(Int64Bytes(n), deferred) 159 } 160 161 func (vm *virtualMachine) pop(deferred bool) ([]byte, error) { 162 if len(vm.dataStack) == 0 { 163 return nil, ErrDataStackUnderflow 164 } 165 res := vm.dataStack[len(vm.dataStack)-1] 166 vm.dataStack = vm.dataStack[:len(vm.dataStack)-1] 167 168 cost := 8 + int64(len(res)) 169 if deferred { 170 vm.deferCost(-cost) 171 } else { 172 vm.runLimit += cost 173 } 174 175 return res, nil 176 } 177 178 func (vm *virtualMachine) popInt64(deferred bool) (int64, error) { 179 bytes, err := vm.pop(deferred) 180 if err != nil { 181 return 0, err 182 } 183 n, err := AsInt64(bytes) 184 return n, err 185 } 186 187 func (vm *virtualMachine) top() ([]byte, error) { 188 if len(vm.dataStack) == 0 { 189 return nil, ErrDataStackUnderflow 190 } 191 return vm.dataStack[len(vm.dataStack)-1], nil 192 } 193 194 // positive cost decreases runlimit, negative cost increases it 195 func (vm *virtualMachine) applyCost(n int64) error { 196 if n > vm.runLimit { 197 vm.runLimit = 0 198 return ErrRunLimitExceeded 199 } 200 vm.runLimit -= n 201 return nil 202 } 203 204 func (vm *virtualMachine) deferCost(n int64) { 205 vm.deferredCost += n 206 } 207 208 func stackCost(stack [][]byte) int64 { 209 result := int64(8 * len(stack)) 210 for _, item := range stack { 211 result += int64(len(item)) 212 } 213 return result 214 } 215 216 func wrapErr(err error, vm *virtualMachine, args [][]byte) error { 217 if err == nil { 218 return nil 219 } 220 221 dis, errDis := Disassemble(vm.program) 222 if errDis != nil { 223 dis = "???" 224 } 225 226 dataArgs := make([]string, 0, len(args)) 227 for _, a := range args { 228 dataArgs = append(dataArgs, hex.EncodeToString(a)) 229 } 230 231 return errors.Wrap(err, fmt.Sprintf("%s [prog %x = %s; args %s]", err.Error(), vm.program, dis, strings.Join(dataArgs, " "))) 232 }