9 "github.com/holiman/uint256"
11 "github.com/bytom/bytom/errors"
14 type virtualMachine struct {
17 program []byte // the program currently executing
22 expansionReserved bool
24 // Stores the data parsed out of an opcode. Used as input to
25 // data-pushing opcodes.
28 // CHECKPREDICATE spawns a child vm with depth+1
31 // In each of these stacks, stack[len(stack)-1] is the top element.
36 // TraceOut - if non-nil - will receive trace output during
38 var TraceOut io.Writer
40 // Verify program by running VM
41 func Verify(context *Context, gasLimit int64) (gasLeft int64, err error) {
43 if r := recover(); r != nil {
44 if rErr, ok := r.(error); ok {
45 err = errors.Sub(ErrUnexpected, rErr)
47 err = errors.Wrap(ErrUnexpected, r)
52 if context.VMVersion != 1 {
53 return gasLimit, ErrUnsupportedVM
56 vm := &virtualMachine{
57 expansionReserved: context.TxVersion != nil && *context.TxVersion == 1,
58 program: context.Code,
63 for i, state := range context.StateData {
64 if err = vm.pushAlt(state, false); err != nil {
65 return vm.runLimit, errors.Wrapf(err, "pushing initial statedata %d", i)
69 for i, arg := range context.Arguments {
70 if err = vm.push(arg, false); err != nil {
71 return vm.runLimit, errors.Wrapf(err, "pushing initial argument %d", i)
76 if err == nil && vm.falseResult() {
77 err = ErrFalseVMResult
80 return vm.runLimit, wrapErr(err, vm, context.Arguments)
83 // falseResult returns true iff the stack is empty or the top
85 func (vm *virtualMachine) falseResult() bool {
86 return len(vm.dataStack) == 0 || !AsBool(vm.dataStack[len(vm.dataStack)-1])
89 func (vm *virtualMachine) run() error {
90 for vm.pc = 0; vm.pc < uint32(len(vm.program)); { // handle vm.pc updates in step
91 if err := vm.step(); err != nil {
98 func (vm *virtualMachine) step() error {
99 inst, err := ParseOp(vm.program, vm.pc)
104 vm.nextPC = vm.pc + inst.Len
107 opname := inst.Op.String()
108 fmt.Fprintf(TraceOut, "vm %d pc %d limit %d %s", vm.depth, vm.pc, vm.runLimit, opname)
109 if len(inst.Data) > 0 {
110 fmt.Fprintf(TraceOut, " %x", inst.Data)
112 fmt.Fprint(TraceOut, "\n")
115 if isExpansion[inst.Op] {
116 if vm.expansionReserved {
117 return ErrDisallowedOpcode
120 return vm.applyCost(1)
125 if err = ops[inst.Op].fn(vm); err != nil {
129 if err = vm.applyCost(vm.deferredCost); err != 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])
143 func (vm *virtualMachine) push(data []byte, deferred bool) error {
144 cost := 8 + int64(len(data))
147 } else if err := vm.applyCost(cost); err != nil {
151 vm.dataStack = append(vm.dataStack, data)
155 func (vm *virtualMachine) pushAlt(data []byte, deferred bool) error {
156 cost := 8 + int64(len(data))
159 } else if err := vm.applyCost(cost); err != nil {
163 vm.altStack = append(vm.altStack, data)
167 func (vm *virtualMachine) pushBool(b bool, deferred bool) error {
168 return vm.push(BoolBytes(b), deferred)
171 func (vm *virtualMachine) pushInt64(n int64, deferred bool) error {
172 return vm.push(Int64Bytes(n), deferred)
175 func (vm *virtualMachine) pushBigInt(n *uint256.Int, deferred bool) error {
176 return vm.push(BigIntBytes(n), deferred)
179 func (vm *virtualMachine) pop(deferred bool) ([]byte, error) {
180 if len(vm.dataStack) == 0 {
181 return nil, ErrDataStackUnderflow
183 res := vm.dataStack[len(vm.dataStack)-1]
184 vm.dataStack = vm.dataStack[:len(vm.dataStack)-1]
186 cost := 8 + int64(len(res))
196 func (vm *virtualMachine) popInt64(deferred bool) (int64, error) {
197 bytes, err := vm.pop(deferred)
202 return AsInt64(bytes)
205 func (vm *virtualMachine) popBigInt(deferred bool) (*uint256.Int, error) {
206 bytes, err := vm.pop(deferred)
211 return AsBigInt(bytes)
214 func (vm *virtualMachine) top() ([]byte, error) {
215 if len(vm.dataStack) == 0 {
216 return nil, ErrDataStackUnderflow
219 return vm.dataStack[len(vm.dataStack)-1], nil
222 // positive cost decreases runlimit, negative cost increases it
223 func (vm *virtualMachine) applyCost(n int64) error {
226 return ErrRunLimitExceeded
233 func (vm *virtualMachine) deferCost(n int64) {
237 func stackCost(stack [][]byte) int64 {
238 result := int64(8 * len(stack))
239 for _, item := range stack {
240 result += int64(len(item))
245 func wrapErr(err error, vm *virtualMachine, args [][]byte) error {
250 dis, errDis := Disassemble(vm.program)
255 dataArgs := make([]string, 0, len(args))
256 for _, a := range args {
257 dataArgs = append(dataArgs, hex.EncodeToString(a))
260 return errors.Wrap(err, fmt.Sprintf("%s [prog %x = %s; args %s]", err.Error(), vm.program, dis, strings.Join(dataArgs, " ")))