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 }*/