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  }