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  }