github.com/sberex/go-sberex@v1.8.2-0.20181113200658-ed96ac38f7d7/core/vm/vm_jit.go (about)

     1  // This file is part of the go-sberex library. The go-sberex library is 
     2  // free software: you can redistribute it and/or modify it under the terms 
     3  // of the GNU Lesser General Public License as published by the Free 
     4  // Software Foundation, either version 3 of the License, or (at your option)
     5  // any later version.
     6  //
     7  // The go-sberex library is distributed in the hope that it will be useful, 
     8  // but WITHOUT ANY WARRANTY; without even the implied warranty of
     9  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 
    10  // General Public License <http://www.gnu.org/licenses/> for more details.
    11  
    12  // +build evmjit
    13  
    14  package vm
    15  
    16  /*
    17  
    18  void* evmjit_create();
    19  int   evmjit_run(void* _jit, void* _data, void* _env);
    20  void  evmjit_destroy(void* _jit);
    21  
    22  // Shared library evmjit (e.g. libevmjit.so) is expected to be installed in /usr/local/lib
    23  #cgo LDFLAGS: -levmjit
    24  */
    25  import "C"
    26  
    27  /*
    28  import (
    29  	"bytes"
    30  	"errors"
    31  	"fmt"
    32  	"math/big"
    33  	"unsafe"
    34  
    35  	"github.com/Sberex/go-sberex/core/state"
    36  	"github.com/Sberex/go-sberex/crypto"
    37  	"github.com/Sberex/go-sberex/params"
    38  )
    39  
    40  type JitVm struct {
    41  	env        EVM
    42  	me         ContextRef
    43  	callerAddr []byte
    44  	price      *big.Int
    45  	data       RuntimeData
    46  }
    47  
    48  type i256 [32]byte
    49  
    50  type RuntimeData struct {
    51  	gas          int64
    52  	gasPrice     int64
    53  	callData     *byte
    54  	callDataSize uint64
    55  	address      i256
    56  	caller       i256
    57  	origin       i256
    58  	callValue    i256
    59  	coinBase     i256
    60  	difficulty   i256
    61  	gasLimit     i256
    62  	number       uint64
    63  	timestamp    int64
    64  	code         *byte
    65  	codeSize     uint64
    66  	codeHash     i256
    67  }
    68  
    69  func hash2llvm(h []byte) i256 {
    70  	var m i256
    71  	copy(m[len(m)-len(h):], h) // right aligned copy
    72  	return m
    73  }
    74  
    75  func llvm2hash(m *i256) []byte {
    76  	return C.GoBytes(unsafe.Pointer(m), C.int(len(m)))
    77  }
    78  
    79  func llvm2hashRef(m *i256) []byte {
    80  	return (*[1 << 30]byte)(unsafe.Pointer(m))[:len(m):len(m)]
    81  }
    82  
    83  func address2llvm(addr []byte) i256 {
    84  	n := hash2llvm(addr)
    85  	bswap(&n)
    86  	return n
    87  }
    88  
    89  // bswap swap bytes of the 256-bit integer on LLVM side
    90  // TODO: Do not change memory on LLVM side, that can conflict with memory access optimizations
    91  func bswap(m *i256) *i256 {
    92  	for i, l := 0, len(m); i < l/2; i++ {
    93  		m[i], m[l-i-1] = m[l-i-1], m[i]
    94  	}
    95  	return m
    96  }
    97  
    98  func trim(m []byte) []byte {
    99  	skip := 0
   100  	for i := 0; i < len(m); i++ {
   101  		if m[i] == 0 {
   102  			skip++
   103  		} else {
   104  			break
   105  		}
   106  	}
   107  	return m[skip:]
   108  }
   109  
   110  func getDataPtr(m []byte) *byte {
   111  	var p *byte
   112  	if len(m) > 0 {
   113  		p = &m[0]
   114  	}
   115  	return p
   116  }
   117  
   118  func big2llvm(n *big.Int) i256 {
   119  	m := hash2llvm(n.Bytes())
   120  	bswap(&m)
   121  	return m
   122  }
   123  
   124  func llvm2big(m *i256) *big.Int {
   125  	n := big.NewInt(0)
   126  	for i := 0; i < len(m); i++ {
   127  		b := big.NewInt(int64(m[i]))
   128  		b.Lsh(b, uint(i)*8)
   129  		n.Add(n, b)
   130  	}
   131  	return n
   132  }
   133  
   134  // llvm2bytesRef creates a []byte slice that references byte buffer on LLVM side (as of that not controller by GC)
   135  // User must ensure that referenced memory is available to Go until the data is copied or not needed any more
   136  func llvm2bytesRef(data *byte, length uint64) []byte {
   137  	if length == 0 {
   138  		return nil
   139  	}
   140  	if data == nil {
   141  		panic("Unexpected nil data pointer")
   142  	}
   143  	return (*[1 << 30]byte)(unsafe.Pointer(data))[:length:length]
   144  }
   145  
   146  func untested(condition bool, message string) {
   147  	if condition {
   148  		panic("Condition `" + message + "` tested. Remove assert.")
   149  	}
   150  }
   151  
   152  func assert(condition bool, message string) {
   153  	if !condition {
   154  		panic("Assert `" + message + "` failed!")
   155  	}
   156  }
   157  
   158  func NewJitVm(env EVM) *JitVm {
   159  	return &JitVm{env: env}
   160  }
   161  
   162  func (self *JitVm) Run(me, caller ContextRef, code []byte, value, gas, price *big.Int, callData []byte) (ret []byte, err error) {
   163  	// TODO: depth is increased but never checked by VM. VM should not know about it at all.
   164  	self.env.SetDepth(self.env.Depth() + 1)
   165  
   166  	// TODO: Move it to Env.Call() or sth
   167  	if Precompiled[string(me.Address())] != nil {
   168  		// if it's address of precompiled contract
   169  		// fallback to standard VM
   170  		stdVm := New(self.env)
   171  		return stdVm.Run(me, caller, code, value, gas, price, callData)
   172  	}
   173  
   174  	if self.me != nil {
   175  		panic("JitVm.Run() can be called only once per JitVm instance")
   176  	}
   177  
   178  	self.me = me
   179  	self.callerAddr = caller.Address()
   180  	self.price = price
   181  
   182  	self.data.gas = gas.Int64()
   183  	self.data.gasPrice = price.Int64()
   184  	self.data.callData = getDataPtr(callData)
   185  	self.data.callDataSize = uint64(len(callData))
   186  	self.data.address = address2llvm(self.me.Address())
   187  	self.data.caller = address2llvm(caller.Address())
   188  	self.data.origin = address2llvm(self.env.Origin())
   189  	self.data.callValue = big2llvm(value)
   190  	self.data.coinBase = address2llvm(self.env.Coinbase())
   191  	self.data.difficulty = big2llvm(self.env.Difficulty())
   192  	self.data.gasLimit = big2llvm(self.env.GasLimit())
   193  	self.data.number = self.env.BlockNumber().Uint64()
   194  	self.data.timestamp = self.env.Time()
   195  	self.data.code = getDataPtr(code)
   196  	self.data.codeSize = uint64(len(code))
   197  	self.data.codeHash = hash2llvm(crypto.Keccak256(code)) // TODO: Get already computed hash?
   198  
   199  	jit := C.evmjit_create()
   200  	retCode := C.evmjit_run(jit, unsafe.Pointer(&self.data), unsafe.Pointer(self))
   201  
   202  	if retCode < 0 {
   203  		err = errors.New("OOG from JIT")
   204  		gas.SetInt64(0) // Set gas to 0, JIT does not bother
   205  	} else {
   206  		gas.SetInt64(self.data.gas)
   207  		if retCode == 1 { // RETURN
   208  			ret = C.GoBytes(unsafe.Pointer(self.data.callData), C.int(self.data.callDataSize))
   209  		} else if retCode == 2 { // SUICIDE
   210  			// TODO: Suicide support logic should be moved to Env to be shared by VM implementations
   211  			state := self.Env().State()
   212  			receiverAddr := llvm2hashRef(bswap(&self.data.address))
   213  			receiver := state.GetOrNewStateObject(receiverAddr)
   214  			balance := state.GetBalance(me.Address())
   215  			receiver.AddBalance(balance)
   216  			state.Delete(me.Address())
   217  		}
   218  	}
   219  
   220  	C.evmjit_destroy(jit)
   221  	return
   222  }
   223  
   224  func (self *JitVm) Printf(format string, v ...interface{}) VirtualMachine {
   225  	return self
   226  }
   227  
   228  func (self *JitVm) Endl() VirtualMachine {
   229  	return self
   230  }
   231  
   232  func (self *JitVm) Env() EVM {
   233  	return self.env
   234  }
   235  
   236  //export env_sha3
   237  func env_sha3(dataPtr *byte, length uint64, resultPtr unsafe.Pointer) {
   238  	data := llvm2bytesRef(dataPtr, length)
   239  	hash := crypto.Keccak256(data)
   240  	result := (*i256)(resultPtr)
   241  	*result = hash2llvm(hash)
   242  }
   243  
   244  //export env_sstore
   245  func env_sstore(vmPtr unsafe.Pointer, indexPtr unsafe.Pointer, valuePtr unsafe.Pointer) {
   246  	vm := (*JitVm)(vmPtr)
   247  	index := llvm2hash(bswap((*i256)(indexPtr)))
   248  	value := llvm2hash(bswap((*i256)(valuePtr)))
   249  	value = trim(value)
   250  	if len(value) == 0 {
   251  		prevValue := vm.env.State().GetState(vm.me.Address(), index)
   252  		if len(prevValue) != 0 {
   253  			vm.Env().State().Refund(vm.callerAddr, GasSStoreRefund)
   254  		}
   255  	}
   256  
   257  	vm.env.State().SetState(vm.me.Address(), index, value)
   258  }
   259  
   260  //export env_sload
   261  func env_sload(vmPtr unsafe.Pointer, indexPtr unsafe.Pointer, resultPtr unsafe.Pointer) {
   262  	vm := (*JitVm)(vmPtr)
   263  	index := llvm2hash(bswap((*i256)(indexPtr)))
   264  	value := vm.env.State().GetState(vm.me.Address(), index)
   265  	result := (*i256)(resultPtr)
   266  	*result = hash2llvm(value)
   267  	bswap(result)
   268  }
   269  
   270  //export env_balance
   271  func env_balance(_vm unsafe.Pointer, _addr unsafe.Pointer, _result unsafe.Pointer) {
   272  	vm := (*JitVm)(_vm)
   273  	addr := llvm2hash((*i256)(_addr))
   274  	balance := vm.Env().State().GetBalance(addr)
   275  	result := (*i256)(_result)
   276  	*result = big2llvm(balance)
   277  }
   278  
   279  //export env_blockhash
   280  func env_blockhash(_vm unsafe.Pointer, _number unsafe.Pointer, _result unsafe.Pointer) {
   281  	vm := (*JitVm)(_vm)
   282  	number := llvm2big((*i256)(_number))
   283  	result := (*i256)(_result)
   284  
   285  	currNumber := vm.Env().BlockNumber()
   286  	limit := big.NewInt(0).Sub(currNumber, big.NewInt(256))
   287  	if number.Cmp(limit) >= 0 && number.Cmp(currNumber) < 0 {
   288  		hash := vm.Env().GetHash(uint64(number.Int64()))
   289  		*result = hash2llvm(hash)
   290  	} else {
   291  		*result = i256{}
   292  	}
   293  }
   294  
   295  //export env_call
   296  func env_call(_vm unsafe.Pointer, _gas *int64, _receiveAddr unsafe.Pointer, _value unsafe.Pointer, inDataPtr unsafe.Pointer, inDataLen uint64, outDataPtr *byte, outDataLen uint64, _codeAddr unsafe.Pointer) bool {
   297  	vm := (*JitVm)(_vm)
   298  
   299  	//fmt.Printf("env_call (depth %d)\n", vm.Env().Depth())
   300  
   301  	defer func() {
   302  		if r := recover(); r != nil {
   303  			fmt.Printf("Recovered in env_call (depth %d, out %p %d): %s\n", vm.Env().Depth(), outDataPtr, outDataLen, r)
   304  		}
   305  	}()
   306  
   307  	balance := vm.Env().State().GetBalance(vm.me.Address())
   308  	value := llvm2big((*i256)(_value))
   309  
   310  	if balance.Cmp(value) >= 0 {
   311  		receiveAddr := llvm2hash((*i256)(_receiveAddr))
   312  		inData := C.GoBytes(inDataPtr, C.int(inDataLen))
   313  		outData := llvm2bytesRef(outDataPtr, outDataLen)
   314  		codeAddr := llvm2hash((*i256)(_codeAddr))
   315  		gas := big.NewInt(*_gas)
   316  		var out []byte
   317  		var err error
   318  		if bytes.Equal(codeAddr, receiveAddr) {
   319  			out, err = vm.env.Call(vm.me, codeAddr, inData, gas, vm.price, value)
   320  		} else {
   321  			out, err = vm.env.CallCode(vm.me, codeAddr, inData, gas, vm.price, value)
   322  		}
   323  		*_gas = gas.Int64()
   324  		if err == nil {
   325  			copy(outData, out)
   326  			return true
   327  		}
   328  	}
   329  
   330  	return false
   331  }
   332  
   333  //export env_create
   334  func env_create(_vm unsafe.Pointer, _gas *int64, _value unsafe.Pointer, initDataPtr unsafe.Pointer, initDataLen uint64, _result unsafe.Pointer) {
   335  	vm := (*JitVm)(_vm)
   336  
   337  	value := llvm2big((*i256)(_value))
   338  	initData := C.GoBytes(initDataPtr, C.int(initDataLen)) // TODO: Unnecessary if low balance
   339  	result := (*i256)(_result)
   340  	*result = i256{}
   341  
   342  	gas := big.NewInt(*_gas)
   343  	ret, suberr, ref := vm.env.Create(vm.me, nil, initData, gas, vm.price, value)
   344  	if suberr == nil {
   345  		dataGas := big.NewInt(int64(len(ret))) // TODO: Not the best design. env.Create can do it, it has the reference to gas counter
   346  		dataGas.Mul(dataGas, params.CreateDataGas)
   347  		gas.Sub(gas, dataGas)
   348  		*result = hash2llvm(ref.Address())
   349  	}
   350  	*_gas = gas.Int64()
   351  }
   352  
   353  //export env_log
   354  func env_log(_vm unsafe.Pointer, dataPtr unsafe.Pointer, dataLen uint64, _topic1 unsafe.Pointer, _topic2 unsafe.Pointer, _topic3 unsafe.Pointer, _topic4 unsafe.Pointer) {
   355  	vm := (*JitVm)(_vm)
   356  
   357  	data := C.GoBytes(dataPtr, C.int(dataLen))
   358  
   359  	topics := make([][]byte, 0, 4)
   360  	if _topic1 != nil {
   361  		topics = append(topics, llvm2hash((*i256)(_topic1)))
   362  	}
   363  	if _topic2 != nil {
   364  		topics = append(topics, llvm2hash((*i256)(_topic2)))
   365  	}
   366  	if _topic3 != nil {
   367  		topics = append(topics, llvm2hash((*i256)(_topic3)))
   368  	}
   369  	if _topic4 != nil {
   370  		topics = append(topics, llvm2hash((*i256)(_topic4)))
   371  	}
   372  
   373  	vm.Env().AddLog(state.NewLog(vm.me.Address(), topics, data, vm.env.BlockNumber().Uint64()))
   374  }
   375  
   376  //export env_extcode
   377  func env_extcode(_vm unsafe.Pointer, _addr unsafe.Pointer, o_size *uint64) *byte {
   378  	vm := (*JitVm)(_vm)
   379  	addr := llvm2hash((*i256)(_addr))
   380  	code := vm.Env().State().GetCode(addr)
   381  	*o_size = uint64(len(code))
   382  	return getDataPtr(code)
   383  }*/