github.com/n1ghtfa1l/go-vnt@v0.6.4-alpha.6/core/wavm/wavm.go (about)

     1  // Copyright 2019 The go-vnt Authors
     2  // This file is part of the go-vnt library.
     3  //
     4  // The go-vnt library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-vnt library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-vnt library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package wavm
    18  
    19  import (
    20  	"bytes"
    21  	"encoding/json"
    22  	"math/big"
    23  	"sync/atomic"
    24  	"time"
    25  
    26  	"github.com/vntchain/go-vnt/common"
    27  	"github.com/vntchain/go-vnt/core/state"
    28  	"github.com/vntchain/go-vnt/core/vm"
    29  	errorsmsg "github.com/vntchain/go-vnt/core/vm"
    30  	"github.com/vntchain/go-vnt/core/vm/interface"
    31  	wasmcontract "github.com/vntchain/go-vnt/core/wavm/contract"
    32  	"github.com/vntchain/go-vnt/core/wavm/gas"
    33  	"github.com/vntchain/go-vnt/core/wavm/storage"
    34  	"github.com/vntchain/go-vnt/core/wavm/utils"
    35  	"github.com/vntchain/go-vnt/crypto"
    36  	"github.com/vntchain/go-vnt/params"
    37  	"github.com/vntchain/vnt-wasm/vnt"
    38  )
    39  
    40  var emptyCodeHash = crypto.Keccak256Hash(nil)
    41  var electionAddress = common.BytesToAddress([]byte{9})
    42  
    43  type WAVM struct {
    44  	// Context provides auxiliary blockchain related information
    45  	vm.Context
    46  	// StateDB gives access to the underlying state
    47  	StateDB inter.StateDB
    48  	// Depth is the current call stack
    49  	depth int
    50  	// Mutable is the current call mutable state
    51  	// -1:init state,0:unmutable,1:mutable
    52  	mutable int
    53  
    54  	// chainConfig contains information about the current chain
    55  	chainConfig *params.ChainConfig
    56  	// chain rules contains the chain rules for the current epoch
    57  	chainRules params.Rules
    58  	// virtual machine configuration options used to initialise the
    59  	// wavm.
    60  	wavmConfig Config
    61  	// global (to this context) vntchain virtual machine
    62  	// used throughout the execution of the tx.
    63  	abort int32
    64  	// callGasTemp holds the gas available for the current call. This is needed because the
    65  	// available gas is calculated in gasCall* according to the 63/64 rule and later
    66  	// applied in opCall*.
    67  	callGasTemp uint64
    68  
    69  	Wavm *Wavm
    70  }
    71  
    72  func (wavm *WAVM) GetCallGasTemp() uint64 {
    73  	return wavm.callGasTemp
    74  }
    75  
    76  func (wavm *WAVM) SetCallGasTemp(gas uint64) {
    77  	wavm.callGasTemp = gas
    78  }
    79  
    80  func (wavm *WAVM) GetChainConfig() *params.ChainConfig {
    81  	return wavm.chainConfig
    82  }
    83  
    84  // run runs the given contract and takes care of running precompiles with a fallback to the byte code interpreter.
    85  func runWavm(wavm *WAVM, contract *wasmcontract.WASMContract, input []byte, isCreate bool) ([]byte, error) {
    86  	if contract.CodeAddr != nil {
    87  		precompiles := vm.PrecompiledContractsHubble
    88  		if p := precompiles[*contract.CodeAddr]; p != nil {
    89  			return vm.RunPrecompiledContract(wavm, p, input, contract)
    90  		}
    91  	}
    92  	if len(contract.Code) == 0 {
    93  		return nil, nil
    94  	}
    95  	var code wasmcontract.WasmCode
    96  	decode, vmInput, err := utils.DecodeContractCode(contract.Code)
    97  	if err != nil {
    98  		return nil, err
    99  	}
   100  	code = decode
   101  	if isCreate == true {
   102  		input = vmInput
   103  	}
   104  
   105  	abi, err := GetAbi(code.Abi)
   106  	if err != nil {
   107  		return nil, err
   108  	}
   109  	gasRule := gas.NewGas(wavm.wavmConfig.DisableFloatingPoint)
   110  	gasTable := wavm.ChainConfig().GasTable(wavm.Context.BlockNumber)
   111  	gasCounter := gas.NewGasCounter(contract, gasTable)
   112  	crx := ChainContext{
   113  		CanTransfer: wavm.Context.CanTransfer,
   114  		Transfer:    wavm.Context.Transfer,
   115  		GetHash:     wavm.Context.GetHash,
   116  		// Message information
   117  		Origin:   wavm.Context.Origin,
   118  		GasPrice: wavm.Context.GasPrice,
   119  
   120  		// Block information
   121  		Coinbase:       wavm.Context.Coinbase,
   122  		GasLimit:       wavm.Context.GasLimit,
   123  		BlockNumber:    wavm.Context.BlockNumber,
   124  		Time:           wavm.Context.Time,
   125  		Difficulty:     wavm.Context.Difficulty,
   126  		Contract:       contract,
   127  		StateDB:        wavm.StateDB.(*state.StateDB),
   128  		Code:           code.Code,
   129  		Abi:            abi,
   130  		Wavm:           wavm,
   131  		IsCreated:      isCreate,
   132  		GasRule:        gasRule,
   133  		GasCounter:     gasCounter,
   134  		GasTable:       gasTable,
   135  		StorageMapping: make(map[uint64]storage.StorageMapping),
   136  	}
   137  	newwawm := NewWavm(crx, wavm.wavmConfig, isCreate)
   138  	wavm.Wavm = newwawm
   139  	err = newwawm.InstantiateModule(code.Code, []uint8{})
   140  	if err != nil {
   141  		return nil, err
   142  	}
   143  	mutable := MutableFunction(abi, newwawm.Module)
   144  	var res []byte
   145  	if isCreate == true {
   146  		// compile the wasm code: add gas counter, add statedb r/w
   147  		compiled, err := CompileModule(newwawm.Module, crx, mutable)
   148  		if err != nil {
   149  			return nil, err
   150  		}
   151  		res, err = newwawm.Apply(input, compiled, mutable)
   152  		if err != nil {
   153  			return nil, err
   154  		}
   155  		compileres, err := json.Marshal(compiled)
   156  		if err != nil {
   157  			return nil, err
   158  		}
   159  		code.Compiled = compileres
   160  		res = utils.CompressWasmAndAbi(code.Abi, code.Code, code.Compiled)
   161  	} else {
   162  		var compiled []vnt.Compiled
   163  		err = json.Unmarshal(code.Compiled, &compiled)
   164  		if err != nil {
   165  			return nil, err
   166  		}
   167  		res, err = newwawm.Apply(input, compiled, mutable)
   168  		if err != nil {
   169  			return nil, err
   170  		}
   171  	}
   172  	return res, err
   173  }
   174  
   175  func NewWAVM(ctx vm.Context, statedb inter.StateDB, chainConfig *params.ChainConfig, vmConfig vm.Config) *WAVM {
   176  	wavmConfig := Config{
   177  		Debug:       vmConfig.Debug,
   178  		Tracer:      vmConfig.Tracer,
   179  		NoRecursion: vmConfig.NoRecursion,
   180  	}
   181  	wavm := &WAVM{
   182  		Context:     ctx,
   183  		StateDB:     statedb,
   184  		wavmConfig:  wavmConfig,
   185  		chainConfig: chainConfig,
   186  		chainRules:  chainConfig.Rules(ctx.BlockNumber),
   187  		mutable:     -1,
   188  	}
   189  	return wavm
   190  }
   191  
   192  func (wavm *WAVM) Cancel() {
   193  	atomic.StoreInt32(&wavm.abort, 1)
   194  }
   195  
   196  func (wavm *WAVM) Create(caller vm.ContractRef, code []byte, gas uint64, value *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
   197  	// Depth check execution. Fail if we're trying to execute above the
   198  	// limit.
   199  	if wavm.depth > int(params.CallCreateDepth) {
   200  		return nil, common.Address{}, gas, errorsmsg.ErrDepth
   201  	}
   202  	if !wavm.CanTransfer(wavm.StateDB, caller.Address(), value) {
   203  		return nil, common.Address{}, gas, errorsmsg.ErrInsufficientBalance
   204  	}
   205  	// Ensure there's no existing contract already at the designated address
   206  	nonce := wavm.StateDB.GetNonce(caller.Address())
   207  	wavm.StateDB.SetNonce(caller.Address(), nonce+1)
   208  
   209  	contractAddr = crypto.CreateAddress(caller.Address(), nonce)
   210  	contractHash := wavm.StateDB.GetCodeHash(contractAddr)
   211  	if wavm.StateDB.GetNonce(contractAddr) != 0 || (contractHash != (common.Hash{}) && contractHash != emptyCodeHash) {
   212  		return nil, common.Address{}, 0, errorsmsg.ErrContractAddressCollision
   213  	}
   214  	// Create a new account on the state
   215  	snapshot := wavm.StateDB.Snapshot()
   216  	wavm.StateDB.CreateAccount(contractAddr)
   217  	wavm.StateDB.SetNonce(contractAddr, 1)
   218  	wavm.Transfer(wavm.StateDB, caller.Address(), contractAddr, value)
   219  
   220  	// initialise a new contract and set the code that is to be used by the
   221  	// wavm. The contract is a scoped environment for this execution context
   222  	// only.
   223  	contract := wasmcontract.NewWASMContract(caller, vm.AccountRef(contractAddr), value, gas)
   224  	contract.SetCallCode(&contractAddr, crypto.Keccak256Hash(code), code)
   225  
   226  	if wavm.wavmConfig.NoRecursion && wavm.depth > 0 {
   227  		return nil, contractAddr, gas, nil
   228  	}
   229  
   230  	if wavm.wavmConfig.Debug && wavm.depth == 0 {
   231  		wavm.wavmConfig.Tracer.CaptureStart(caller.Address(), contractAddr, true, code, gas, value)
   232  	}
   233  	start := time.Now()
   234  	ret, err = runWavm(wavm, contract, nil, true)
   235  	// check whether the max code size has been exceeded
   236  	maxCodeSizeExceeded := len(ret) > params.MaxCodeSize
   237  	// if the contract creation ran successfully and no errors were returned
   238  	// calculate the gas required to store the code. If the code could not
   239  	// be stored due to not enough gas set an error and let it be handled
   240  	// by the error checking condition below.
   241  	if err == nil && !maxCodeSizeExceeded {
   242  		createDataGas := uint64(len(ret)) * params.CreateDataGas / 2
   243  		if contract.UseGas(createDataGas) {
   244  			wavm.StateDB.SetCode(contractAddr, ret)
   245  		} else {
   246  			err = errorsmsg.ErrCodeStoreOutOfGas
   247  		}
   248  	}
   249  
   250  	// When an error was returned by the wavm or when setting the creation code
   251  	// above we revert to the snapshot and consume any gas remaining. Additionally
   252  	// also counts for code storage gas errors.
   253  	if maxCodeSizeExceeded || err != nil {
   254  		wavm.StateDB.RevertToSnapshot(snapshot)
   255  		if err != nil && err.Error() != errorsmsg.ErrExecutionReverted.Error() {
   256  			contract.UseGas(contract.Gas)
   257  		}
   258  	}
   259  	// Assign err if contract code size exceeds the max while the err is still empty.
   260  	if maxCodeSizeExceeded && err == nil {
   261  		err = errorsmsg.ErrMaxCodeSizeExceeded
   262  	}
   263  	if wavm.wavmConfig.Debug && wavm.depth == 0 {
   264  		wavm.wavmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
   265  	}
   266  	return ret, contractAddr, contract.Gas, err
   267  }
   268  
   269  // Call executes the contract associated with the addr with the given input as
   270  // parameters. It also handles any necessary value transfer required and takes
   271  // the necessary steps to create accounts and reverses the state in case of an
   272  // execution error or failed value transfer.
   273  func (wavm *WAVM) Call(caller vm.ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) {
   274  	if wavm.wavmConfig.NoRecursion && wavm.depth > 0 {
   275  		return nil, gas, nil
   276  	}
   277  	// Fail if we're trying to execute above the call depth limit
   278  	if wavm.depth > int(params.CallCreateDepth) {
   279  		return nil, gas, errorsmsg.ErrDepth
   280  	}
   281  	// Fail if we're trying to transfer more than the available balance
   282  	if !wavm.Context.CanTransfer(wavm.StateDB, caller.Address(), value) {
   283  		return nil, gas, errorsmsg.ErrInsufficientBalance
   284  	}
   285  	var (
   286  		to       = vm.AccountRef(addr)
   287  		snapshot = wavm.StateDB.Snapshot()
   288  	)
   289  	if !wavm.StateDB.Exist(addr) {
   290  		precompiles := vm.PrecompiledContractsHubble
   291  		if precompiles[addr] == nil && value.Sign() == 0 {
   292  			// Calling a non existing account, don't do antything, but ping the tracer
   293  			if wavm.wavmConfig.Debug && wavm.depth == 0 {
   294  				wavm.wavmConfig.Tracer.CaptureStart(caller.Address(), addr, false, input, gas, value)
   295  				wavm.wavmConfig.Tracer.CaptureEnd(ret, 0, 0, nil)
   296  			}
   297  			return nil, gas, nil
   298  		}
   299  		wavm.StateDB.CreateAccount(addr)
   300  	}
   301  	wavm.Transfer(wavm.StateDB, caller.Address(), to.Address(), value)
   302  
   303  	// Initialise a new contract and set the code that is to be used by the WAVM.
   304  	// The contract is a scoped environment for this execution context only.
   305  	contract := wasmcontract.NewWASMContract(caller, to, value, gas)
   306  
   307  	code := wavm.StateDB.GetCode(addr)
   308  
   309  	contract.SetCallCode(&addr, wavm.StateDB.GetCodeHash(addr), code)
   310  
   311  	start := time.Now()
   312  
   313  	// Capture the tracer start/end events in debug mode
   314  	if wavm.wavmConfig.Debug && wavm.depth == 0 {
   315  		wavm.wavmConfig.Tracer.CaptureStart(caller.Address(), addr, false, input, gas, value)
   316  
   317  		defer func() { // Lazy evaluation of the parameters
   318  			wavm.wavmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
   319  		}()
   320  	}
   321  	ret, err = runWavm(wavm, contract, input, false)
   322  	// When an error was returned by the WAVM or when setting the creation code
   323  	// above we revert to the snapshot and consume any gas remaining. Additionally
   324  	// this also counts for code storage gas errors.
   325  	if err != nil {
   326  		wavm.StateDB.RevertToSnapshot(snapshot)
   327  		if err.Error() != errorsmsg.ErrExecutionReverted.Error() && !bytes.Equal(to.Address().Bytes(), electionAddress.Bytes()) {
   328  			contract.UseGas(contract.Gas)
   329  		}
   330  	}
   331  	return ret, contract.Gas, err
   332  }
   333  
   334  // CallCode executes the contract associated with the addr with the given input
   335  // as parameters. It also handles any necessary value transfer required and takes
   336  // the necessary steps to create accounts and reverses the state in case of an
   337  // execution error or failed value transfer.
   338  //
   339  // CallCode differs from Call in the sense that it executes the given address'
   340  // code with the caller as context.
   341  func (wavm *WAVM) CallCode(caller vm.ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) {
   342  	if wavm.wavmConfig.NoRecursion && wavm.depth > 0 {
   343  		return nil, gas, nil
   344  	}
   345  
   346  	// Fail if we're trying to execute above the call depth limit
   347  	if wavm.depth > int(params.CallCreateDepth) {
   348  		return nil, gas, errorsmsg.ErrDepth
   349  	}
   350  	// Fail if we're trying to transfer more than the available balance
   351  	if !wavm.CanTransfer(wavm.StateDB, caller.Address(), value) {
   352  		return nil, gas, errorsmsg.ErrInsufficientBalance
   353  	}
   354  
   355  	var (
   356  		snapshot = wavm.StateDB.Snapshot()
   357  		to       = vm.AccountRef(caller.Address())
   358  	)
   359  	// initialise a new contract and set the code that is to be used by the
   360  	// WAVM. The contract is a scoped environment for this execution context
   361  	// only.
   362  	contract := wasmcontract.NewWASMContract(caller, to, value, gas)
   363  
   364  	code := wavm.StateDB.GetCode(addr)
   365  
   366  	contract.SetCallCode(&addr, wavm.StateDB.GetCodeHash(addr), code)
   367  
   368  	ret, err = runWavm(wavm, contract, input, false)
   369  	if err != nil {
   370  		wavm.StateDB.RevertToSnapshot(snapshot)
   371  		// if err != errExecutionReverted {
   372  		// 	contract.UseGas(contract.Gas)
   373  		// }
   374  	}
   375  	return ret, contract.Gas, err
   376  }
   377  func (wavm *WAVM) DelegateCall(caller vm.ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) {
   378  	if wavm.wavmConfig.NoRecursion && wavm.depth > 0 {
   379  		return nil, gas, nil
   380  	}
   381  	// Fail if we're trying to execute above the call depth limit
   382  	if wavm.depth > int(params.CallCreateDepth) {
   383  		return nil, gas, errorsmsg.ErrDepth
   384  	}
   385  
   386  	var (
   387  		snapshot = wavm.StateDB.Snapshot()
   388  		to       = vm.AccountRef(caller.Address())
   389  	)
   390  
   391  	// Initialise a new contract and make initialise the delegate values
   392  	ctr := wasmcontract.NewWASMContract(caller, to, nil, gas).AsDelegate()
   393  	contract := ctr.(*wasmcontract.WASMContract)
   394  
   395  	code := wavm.StateDB.GetCode(addr)
   396  
   397  	contract.SetCallCode(&addr, wavm.StateDB.GetCodeHash(addr), code)
   398  
   399  	ret, err = runWavm(wavm, contract, input, false)
   400  	if err != nil {
   401  		wavm.StateDB.RevertToSnapshot(snapshot)
   402  		// if err != errExecutionReverted {
   403  		// 	contract.UseGas(contract.Gas)
   404  		// }
   405  	}
   406  	return ret, contract.Gas, err
   407  }
   408  func (wavm *WAVM) StaticCall(caller vm.ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) {
   409  	return nil, 1, nil
   410  }
   411  func (wavm *WAVM) GetStateDb() inter.StateDB {
   412  	return wavm.StateDB
   413  }
   414  func (wavm *WAVM) ChainConfig() *params.ChainConfig {
   415  	return wavm.chainConfig
   416  }
   417  
   418  func (wavm *WAVM) GetContext() vm.Context {
   419  	return wavm.Context
   420  }
   421  
   422  func (wavm *WAVM) GetOrigin() common.Address {
   423  	return wavm.Origin
   424  }
   425  
   426  func (wavm *WAVM) GetTime() *big.Int {
   427  	return wavm.Time
   428  }
   429  
   430  func (wavm *WAVM) GetBlockNum() *big.Int {
   431  	return wavm.BlockNumber
   432  }