github.com/bytom/bytom@v1.1.2-0.20221014091027-bbcba3df6075/protocol/vm/vm.go (about) 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 63 for i, state := range context.StateData { 64 if err = vm.pushAltStack(state, false); err != nil { 65 return vm.runLimit, errors.Wrapf(err, "pushing initial statedata %d", i) 66 } 67 } 68 69 for i, arg := range context.Arguments { 70 if err = vm.pushDataStack(arg, false); err != nil { 71 return vm.runLimit, errors.Wrapf(err, "pushing initial argument %d", i) 72 } 73 } 74 75 if err = vm.run(); err == nil && vm.falseResult() { 76 err = ErrFalseVMResult 77 } 78 79 return vm.runLimit, wrapErr(err, vm, context.Arguments) 80 } 81 82 // falseResult returns true iff the stack is empty or the top 83 // item is false 84 func (vm *virtualMachine) falseResult() bool { 85 return len(vm.dataStack) == 0 || !AsBool(vm.dataStack[len(vm.dataStack)-1]) 86 } 87 88 func (vm *virtualMachine) run() error { 89 for vm.pc = 0; vm.pc < uint32(len(vm.program)); { // handle vm.pc updates in step 90 if err := vm.step(); err != nil { 91 return err 92 } 93 } 94 return nil 95 } 96 97 func (vm *virtualMachine) step() error { 98 inst, err := ParseOp(vm.program, vm.pc) 99 if err != nil { 100 return err 101 } 102 103 vm.nextPC = vm.pc + inst.Len 104 105 if TraceOut != nil { 106 opname := inst.Op.String() 107 fmt.Fprintf(TraceOut, "vm %d pc %d limit %d %s", vm.depth, vm.pc, vm.runLimit, opname) 108 if len(inst.Data) > 0 { 109 fmt.Fprintf(TraceOut, " %x", inst.Data) 110 } 111 fmt.Fprint(TraceOut, "\n") 112 } 113 114 if isExpansion[inst.Op] { 115 if vm.expansionReserved { 116 return ErrDisallowedOpcode 117 } 118 119 vm.pc = vm.nextPC 120 return vm.applyCost(1) 121 } 122 123 vm.deferredCost = 0 124 vm.data = inst.Data 125 if err = ops[inst.Op].fn(vm); err != nil { 126 return err 127 } 128 129 if err = vm.applyCost(vm.deferredCost); err != nil { 130 return err 131 } 132 133 vm.pc = vm.nextPC 134 if TraceOut != nil { 135 for i := len(vm.dataStack) - 1; i >= 0; i-- { 136 fmt.Fprintf(TraceOut, " stack %d: %x\n", len(vm.dataStack)-1-i, vm.dataStack[i]) 137 } 138 } 139 140 return nil 141 } 142 143 func (vm *virtualMachine) pushDataStack(data []byte, deferred bool) error { 144 cost := 8 + int64(len(data)) 145 if deferred { 146 vm.deferCost(cost) 147 } else if err := vm.applyCost(cost); err != nil { 148 return err 149 } 150 151 vm.dataStack = append(vm.dataStack, data) 152 return nil 153 } 154 155 func (vm *virtualMachine) pushAltStack(data []byte, deferred bool) error { 156 cost := 8 + int64(len(data)) 157 if deferred { 158 vm.deferCost(cost) 159 } else if err := vm.applyCost(cost); err != nil { 160 return err 161 } 162 163 vm.altStack = append(vm.altStack, data) 164 return nil 165 } 166 167 func (vm *virtualMachine) pushBool(b bool, deferred bool) error { 168 return vm.pushDataStack(BoolBytes(b), deferred) 169 } 170 171 func (vm *virtualMachine) pushBigInt(n *uint256.Int, deferred bool) error { 172 return vm.pushDataStack(BigIntBytes(n), deferred) 173 } 174 175 func (vm *virtualMachine) pop(deferred bool) ([]byte, error) { 176 if len(vm.dataStack) == 0 { 177 return nil, ErrDataStackUnderflow 178 } 179 180 res := vm.dataStack[len(vm.dataStack)-1] 181 vm.dataStack = vm.dataStack[:len(vm.dataStack)-1] 182 183 cost := 8 + int64(len(res)) 184 if deferred { 185 vm.deferCost(-cost) 186 } else { 187 vm.runLimit += cost 188 } 189 190 return res, nil 191 } 192 193 func (vm *virtualMachine) popBigInt(deferred bool) (*uint256.Int, error) { 194 bytes, err := vm.pop(deferred) 195 if err != nil { 196 return nil, err 197 } 198 199 return AsBigInt(bytes) 200 } 201 202 func (vm *virtualMachine) top() ([]byte, error) { 203 if len(vm.dataStack) == 0 { 204 return nil, ErrDataStackUnderflow 205 } 206 207 return vm.dataStack[len(vm.dataStack)-1], nil 208 } 209 210 // positive cost decreases runlimit, negative cost increases it 211 func (vm *virtualMachine) applyCost(n int64) error { 212 if n > vm.runLimit { 213 vm.runLimit = 0 214 return ErrRunLimitExceeded 215 } 216 217 vm.runLimit -= n 218 return nil 219 } 220 221 func (vm *virtualMachine) deferCost(n int64) { 222 vm.deferredCost += n 223 } 224 225 func stackCost(stack [][]byte) int64 { 226 result := int64(8 * len(stack)) 227 for _, item := range stack { 228 result += int64(len(item)) 229 } 230 return result 231 } 232 233 func wrapErr(err error, vm *virtualMachine, args [][]byte) error { 234 if err == nil { 235 return nil 236 } 237 238 dis, errDis := Disassemble(vm.program) 239 if errDis != nil { 240 dis = "???" 241 } 242 243 dataArgs := make([]string, 0, len(args)) 244 for _, a := range args { 245 dataArgs = append(dataArgs, hex.EncodeToString(a)) 246 } 247 248 return errors.Wrap(err, fmt.Sprintf("%s [prog %x = %s; args %s]", err.Error(), vm.program, dis, strings.Join(dataArgs, " "))) 249 }