github.com/core-coin/go-core@v1.1.7/core/vm/cvm.go (about) 1 // Copyright 2020 by the Authors 2 // This file is part of the go-core library. 3 // 4 // The go-core 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-core 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-core library. If not, see <http://www.gnu.org/licenses/>. 16 17 package vm 18 19 import ( 20 "errors" 21 "github.com/core-coin/uint256" 22 "math/big" 23 "sync/atomic" 24 "time" 25 26 "github.com/core-coin/go-core/common" 27 "github.com/core-coin/go-core/crypto" 28 "github.com/core-coin/go-core/params" 29 ) 30 31 // emptyCodeHash is used by create to ensure deployment is disallowed to already 32 // deployed contract addresses (relevant after the account abstraction). 33 var emptyCodeHash = crypto.SHA3Hash(nil) 34 35 type ( 36 // CanTransferFunc is the signature of a transfer guard function 37 CanTransferFunc func(StateDB, common.Address, *big.Int) bool 38 // TransferFunc is the signature of a transfer function 39 TransferFunc func(StateDB, common.Address, common.Address, *big.Int) 40 // GetHashFunc returns the n'th block hash in the blockchain 41 // and is used by the BLOCKHASH CVM op code. 42 GetHashFunc func(uint64) common.Hash 43 ) 44 45 func (cvm *CVM) precompile(addr common.Address) (PrecompiledContract, bool) { 46 var precompiles map[common.Address]PrecompiledContract 47 precompiles = PrecompiledContracts 48 p, ok := precompiles[addr] 49 return p, ok 50 } 51 52 // run runs the given contract and takes care of running precompiles with a fallback to the byte code interpreter. 53 func run(cvm *CVM, contract *Contract, input []byte, readOnly bool) ([]byte, error) { 54 for _, interpreter := range cvm.interpreters { 55 if interpreter.CanRun(contract.Code) { 56 if cvm.interpreter != interpreter { 57 // Ensure that the interpreter pointer is set back 58 // to its current value upon return. 59 defer func(i Interpreter) { 60 cvm.interpreter = i 61 }(cvm.interpreter) 62 cvm.interpreter = interpreter 63 } 64 return interpreter.Run(contract, input, readOnly) 65 } 66 } 67 return nil, errors.New("no compatible interpreter") 68 } 69 70 // Context provides the CVM with auxiliary information. Once provided 71 // it shouldn't be modified. 72 type Context struct { 73 // CanTransfer returns whether the account contains 74 // sufficient core to transfer the value 75 CanTransfer CanTransferFunc 76 // Transfer transfers core from one account to the other 77 Transfer TransferFunc 78 // GetHash returns the hash corresponding to n 79 GetHash GetHashFunc 80 81 // Message information 82 Origin common.Address // Provides information for ORIGIN 83 EnergyPrice *big.Int // Provides information for ENERGYPRICE 84 85 // Block information 86 Coinbase common.Address // Provides information for COINBASE 87 EnergyLimit uint64 // Provides information for ENERGYLIMIT 88 BlockNumber *big.Int // Provides information for NUMBER 89 Time *big.Int // Provides information for TIME 90 Difficulty *big.Int // Provides information for DIFFICULTY 91 } 92 93 // CVM is the Core Virtual Machine base object and provides 94 // the necessary tools to run a contract on the given state with 95 // the provided context. It should be noted that any error 96 // generated through any of the calls should be considered a 97 // revert-state-and-consume-all-energy operation, no checks on 98 // specific errors should ever be performed. The interpreter makes 99 // sure that any errors generated are to be considered faulty code. 100 // 101 // The CVM should never be reused and is not thread safe. 102 type CVM struct { 103 // Context provides auxiliary blockchain related information 104 Context 105 // StateDB gives access to the underlying state 106 StateDB StateDB 107 // Depth is the current call stack 108 depth int 109 110 // chainConfig contains information about the current chain 111 chainConfig *params.ChainConfig 112 // chain rules contains the chain rules for the current epoch 113 chainRules params.Rules 114 // virtual machine configuration options used to initialise the 115 // cvm. 116 vmConfig Config 117 // global (to this context) Core Virtual Machine 118 // used throughout the execution of the tx. 119 interpreters []Interpreter 120 interpreter Interpreter 121 // abort is used to abort the CVM calling operations 122 // NOTE: must be set atomically 123 abort int32 124 // callEnergyTemp holds the energy available for the current call. This is needed because the 125 // available energy is calculated in energyCall* according to the 63/64 rule and later 126 // applied in opCall*. 127 callEnergyTemp uint64 128 } 129 130 // NewCVM returns a new CVM. The returned CVM is not thread safe and should 131 // only ever be used *once*. 132 func NewCVM(ctx Context, statedb StateDB, chainConfig *params.ChainConfig, vmConfig Config) *CVM { 133 cvm := &CVM{ 134 Context: ctx, 135 StateDB: statedb, 136 vmConfig: vmConfig, 137 chainConfig: chainConfig, 138 chainRules: chainConfig.Rules(ctx.BlockNumber), 139 interpreters: make([]Interpreter, 0, 1), 140 } 141 142 if chainConfig.IsEWASM(ctx.BlockNumber) { 143 // to be implemented by CVM-C and Wagon PRs. 144 // if vmConfig.EWASMInterpreter != "" { 145 // extIntOpts := strings.Split(vmConfig.EWASMInterpreter, ":") 146 // path := extIntOpts[0] 147 // options := []string{} 148 // if len(extIntOpts) > 1 { 149 // options = extIntOpts[1..] 150 // } 151 // cvm.interpreters = append(cvm.interpreters, NewCVMVCInterpreter(cvm, vmConfig, options)) 152 // } else { 153 // cvm.interpreters = append(cvm.interpreters, NewEWASMInterpreter(cvm, vmConfig)) 154 // } 155 panic("No supported ewasm interpreter yet.") 156 } 157 158 // vmConfig.CVMInterpreter will be used by CVM-C, it won't be checked here 159 // as we always want to have the built-in CVM as the failover option. 160 cvm.interpreters = append(cvm.interpreters, NewCVMInterpreter(cvm, vmConfig)) 161 cvm.interpreter = cvm.interpreters[0] 162 163 return cvm 164 } 165 166 // Cancel cancels any running CVM operation. This may be called concurrently and 167 // it's safe to be called multiple times. 168 func (cvm *CVM) Cancel() { 169 atomic.StoreInt32(&cvm.abort, 1) 170 } 171 172 // Cancelled returns true if Cancel has been called 173 func (cvm *CVM) Cancelled() bool { 174 return atomic.LoadInt32(&cvm.abort) == 1 175 } 176 177 // Interpreter returns the current interpreter 178 func (cvm *CVM) Interpreter() Interpreter { 179 return cvm.interpreter 180 } 181 182 // Call executes the contract associated with the addr with the given input as 183 // parameters. It also handles any necessary value transfer required and takes 184 // the necessary steps to create accounts and reverses the state in case of an 185 // execution error or failed value transfer. 186 func (cvm *CVM) Call(caller ContractRef, addr common.Address, input []byte, energy uint64, value *big.Int) (ret []byte, leftOverEnergy uint64, err error) { 187 if cvm.vmConfig.NoRecursion && cvm.depth > 0 { 188 return nil, energy, nil 189 } 190 191 // Fail if we're trying to execute above the call depth limit 192 if cvm.depth > int(params.CallCreateDepth) { 193 return nil, energy, ErrDepth 194 } 195 // Fail if we're trying to transfer more than the available balance 196 if value.Sign() != 0 && !cvm.Context.CanTransfer(cvm.StateDB, caller.Address(), value) { 197 return nil, energy, ErrInsufficientBalance 198 } 199 200 snapshot := cvm.StateDB.Snapshot() 201 p, isPrecompile := cvm.precompile(addr) 202 203 if !cvm.StateDB.Exist(addr) { 204 if !isPrecompile && value.Sign() == 0 { 205 // Calling a non existing account, don't do anything, but ping the tracer 206 if cvm.vmConfig.Debug && cvm.depth == 0 { 207 cvm.vmConfig.Tracer.CaptureStart(caller.Address(), addr, false, input, energy, value) 208 cvm.vmConfig.Tracer.CaptureEnd(ret, 0, 0, nil) 209 } 210 return nil, energy, nil 211 } 212 cvm.StateDB.CreateAccount(addr) 213 } 214 cvm.Transfer(cvm.StateDB, caller.Address(), addr, value) 215 216 // Capture the tracer start/end events in debug mode 217 if cvm.vmConfig.Debug && cvm.depth == 0 { 218 cvm.vmConfig.Tracer.CaptureStart(caller.Address(), addr, false, input, energy, value) 219 220 defer func(startEnergy uint64, startTime time.Time) { // Lazy evaluation of the parameters 221 cvm.vmConfig.Tracer.CaptureEnd(ret, startEnergy-energy, time.Since(startTime), err) 222 }(energy, time.Now()) 223 } 224 if isPrecompile { 225 ret, energy, err = RunPrecompiledContract(p, input, energy) 226 } else { 227 // Initialise a new contract and set the code that is to be used by the CVM. 228 // The contract is a scoped environment for this execution context only. 229 code := cvm.StateDB.GetCode(addr) 230 if len(code) == 0 { 231 ret, err = nil, nil // energy is unchanged 232 } else { 233 addrCopy := addr 234 // If the account has no code, we can abort here 235 // The depth-check is already done, and precompiles handled above 236 contract := NewContract(caller, AccountRef(addrCopy), value, energy) 237 contract.SetCallCode(&addrCopy, cvm.StateDB.GetCodeHash(addrCopy), code) 238 ret, err = run(cvm, contract, input, false) 239 energy = contract.Energy 240 } 241 } 242 243 // When an error was returned by the CVM or when setting the creation code 244 // above we revert to the snapshot and consume any energy remaining. 245 if err != nil { 246 cvm.StateDB.RevertToSnapshot(snapshot) 247 if err != ErrExecutionReverted { 248 energy = 0 249 } 250 // TODO: consider clearing up unused snapshots: 251 //} else { 252 // cvm.StateDB.DiscardSnapshot(snapshot) 253 } 254 return ret, energy, err 255 } 256 257 // CallCode executes the contract associated with the addr with the given input 258 // as parameters. It also handles any necessary value transfer required and takes 259 // the necessary steps to create accounts and reverses the state in case of an 260 // execution error or failed value transfer. 261 // 262 // CallCode differs from Call in the sense that it executes the given address' 263 // code with the caller as context. 264 func (cvm *CVM) CallCode(caller ContractRef, addr common.Address, input []byte, energy uint64, value *big.Int) (ret []byte, leftOverEnergy uint64, err error) { 265 if cvm.vmConfig.NoRecursion && cvm.depth > 0 { 266 return nil, energy, nil 267 } 268 269 // Fail if we're trying to execute above the call depth limit 270 if cvm.depth > int(params.CallCreateDepth) { 271 return nil, energy, ErrDepth 272 } 273 // Fail if we're trying to transfer more than the available balance 274 // Note although it's noop to transfer X core to caller itself. But 275 // if caller doesn't have enough balance, it would be an error to allow 276 // over-charging itself. So the check here is necessary. 277 if !cvm.Context.CanTransfer(cvm.StateDB, caller.Address(), value) { 278 return nil, energy, ErrInsufficientBalance 279 } 280 281 var snapshot = cvm.StateDB.Snapshot() 282 283 // It is allowed to call precompiles, even via delegatecall 284 if p, isPrecompile := cvm.precompile(addr); isPrecompile { 285 ret, energy, err = RunPrecompiledContract(p, input, energy) 286 } else { 287 addrCopy := addr 288 // Initialise a new contract and set the code that is to be used by the CVM. 289 // The contract is a scoped environment for this execution context only. 290 contract := NewContract(caller, AccountRef(caller.Address()), value, energy) 291 contract.SetCallCode(&addrCopy, cvm.StateDB.GetCodeHash(addrCopy), cvm.StateDB.GetCode(addrCopy)) 292 ret, err = run(cvm, contract, input, false) 293 energy = contract.Energy 294 } 295 if err != nil { 296 cvm.StateDB.RevertToSnapshot(snapshot) 297 if err != ErrExecutionReverted { 298 energy = 0 299 } 300 } 301 return ret, energy, err 302 } 303 304 // DelegateCall executes the contract associated with the addr with the given input 305 // as parameters. It reverses the state in case of an execution error. 306 // 307 // DelegateCall differs from CallCode in the sense that it executes the given address' 308 // code with the caller as context and the caller is set to the caller of the caller. 309 func (cvm *CVM) DelegateCall(caller ContractRef, addr common.Address, input []byte, energy uint64) (ret []byte, leftOverEnergy uint64, err error) { 310 if cvm.vmConfig.NoRecursion && cvm.depth > 0 { 311 return nil, energy, nil 312 } 313 // Fail if we're trying to execute above the call depth limit 314 if cvm.depth > int(params.CallCreateDepth) { 315 return nil, energy, ErrDepth 316 } 317 318 var snapshot = cvm.StateDB.Snapshot() 319 320 // It is allowed to call precompiles, even via delegatecall 321 if p, isPrecompile := cvm.precompile(addr); isPrecompile { 322 ret, energy, err = RunPrecompiledContract(p, input, energy) 323 } else { 324 addrCopy := addr 325 // Initialise a new contract and make initialise the delegate values 326 contract := NewContract(caller, AccountRef(caller.Address()), nil, energy).AsDelegate() 327 contract.SetCallCode(&addrCopy, cvm.StateDB.GetCodeHash(addrCopy), cvm.StateDB.GetCode(addrCopy)) 328 ret, err = run(cvm, contract, input, false) 329 energy = contract.Energy 330 } 331 if err != nil { 332 cvm.StateDB.RevertToSnapshot(snapshot) 333 if err != ErrExecutionReverted { 334 energy = 0 335 } 336 } 337 return ret, energy, err 338 } 339 340 // StaticCall executes the contract associated with the addr with the given input 341 // as parameters while disallowing any modifications to the state during the call. 342 // Opcodes that attempt to perform such modifications will result in exceptions 343 // instead of performing the modifications. 344 func (cvm *CVM) StaticCall(caller ContractRef, addr common.Address, input []byte, energy uint64) (ret []byte, leftOverEnergy uint64, err error) { 345 if cvm.vmConfig.NoRecursion && cvm.depth > 0 { 346 return nil, energy, nil 347 } 348 // Fail if we're trying to execute above the call depth limit 349 if cvm.depth > int(params.CallCreateDepth) { 350 return nil, energy, ErrDepth 351 } 352 // We take a snapshot here. This is a bit counter-intuitive, and could probably be skipped. 353 // However, even a staticcall is considered a 'touch'. On mainnet, static calls were introduced 354 // after all empty accounts were deleted, so this is not required. However, if we omit this, 355 // then certain tests start failing; stRevertTest/RevertPrecompiledTouchExactOOG.json. 356 // We could change this, but for now it's left for legacy reasons 357 var snapshot = cvm.StateDB.Snapshot() 358 359 // We do an AddBalance of zero here, just in order to trigger a touch. 360 // but is the correct thing to do and matters on other networks, in tests, and potential 361 // future scenarios 362 cvm.StateDB.AddBalance(addr, big0) 363 364 if p, isPrecompile := cvm.precompile(addr); isPrecompile { 365 ret, energy, err = RunPrecompiledContract(p, input, energy) 366 } else { 367 // At this point, we use a copy of address. If we don't, the go compiler will 368 // leak the 'contract' to the outer scope, and make allocation for 'contract' 369 // even if the actual execution ends on RunPrecompiled above. 370 addrCopy := addr 371 // Initialise a new contract and set the code that is to be used by the CVM. 372 // The contract is a scoped environment for this execution context only. 373 contract := NewContract(caller, AccountRef(addrCopy), new(big.Int), energy) 374 contract.SetCallCode(&addrCopy, cvm.StateDB.GetCodeHash(addrCopy), cvm.StateDB.GetCode(addrCopy)) 375 // When an error was returned by the CVM or when setting the creation code 376 // above we revert to the snapshot and consume any energy remaining. 377 ret, err = run(cvm, contract, input, true) 378 energy = contract.Energy 379 } 380 if err != nil { 381 cvm.StateDB.RevertToSnapshot(snapshot) 382 if err != ErrExecutionReverted { 383 energy = 0 384 } 385 } 386 return ret, energy, err 387 } 388 389 type codeAndHash struct { 390 code []byte 391 hash common.Hash 392 } 393 394 func (c *codeAndHash) Hash() common.Hash { 395 if c.hash == (common.Hash{}) { 396 c.hash = crypto.SHA3Hash(c.code) 397 } 398 return c.hash 399 } 400 401 // create creates a new contract using code as deployment code. 402 func (cvm *CVM) create(caller ContractRef, codeAndHash *codeAndHash, energy uint64, value *big.Int, address common.Address) ([]byte, common.Address, uint64, error) { 403 // Depth check execution. Fail if we're trying to execute above the 404 // limit. 405 if cvm.depth > int(params.CallCreateDepth) { 406 return nil, common.Address{}, energy, ErrDepth 407 } 408 if !cvm.CanTransfer(cvm.StateDB, caller.Address(), value) { 409 return nil, common.Address{}, energy, ErrInsufficientBalance 410 } 411 nonce := cvm.StateDB.GetNonce(caller.Address()) 412 cvm.StateDB.SetNonce(caller.Address(), nonce+1) 413 414 // Ensure there's no existing contract already at the designated address 415 contractHash := cvm.StateDB.GetCodeHash(address) 416 if cvm.StateDB.GetNonce(address) != 0 || (contractHash != (common.Hash{}) && contractHash != emptyCodeHash) { 417 return nil, common.Address{}, 0, ErrContractAddressCollision 418 } 419 // Create a new account on the state 420 snapshot := cvm.StateDB.Snapshot() 421 cvm.StateDB.CreateAccount(address) 422 cvm.StateDB.SetNonce(address, 1) 423 cvm.Transfer(cvm.StateDB, caller.Address(), address, value) 424 425 // Initialise a new contract and set the code that is to be used by the CVM. 426 // The contract is a scoped environment for this execution context only. 427 contract := NewContract(caller, AccountRef(address), value, energy) 428 contract.SetCodeOptionalHash(&address, codeAndHash) 429 430 if cvm.vmConfig.NoRecursion && cvm.depth > 0 { 431 return nil, address, energy, nil 432 } 433 434 if cvm.vmConfig.Debug && cvm.depth == 0 { 435 cvm.vmConfig.Tracer.CaptureStart(caller.Address(), address, true, codeAndHash.code, energy, value) 436 } 437 start := time.Now() 438 439 ret, err := run(cvm, contract, nil, false) 440 441 // check whether the max code size has been exceeded 442 maxCodeSizeExceeded := len(ret) > params.MaxCodeSize 443 // if the contract creation ran successfully and no errors were returned 444 // calculate the energy required to store the code. If the code could not 445 // be stored due to not enough energy set an error and let it be handled 446 // by the error checking condition below. 447 if err == nil && !maxCodeSizeExceeded { 448 createDataEnergy := uint64(len(ret)) * params.CreateDataEnergy 449 if contract.UseEnergy(createDataEnergy) { 450 cvm.StateDB.SetCode(address, ret) 451 } else { 452 err = ErrCodeStoreOutOfEnergy 453 } 454 } 455 456 // When an error was returned by the CVM or when setting the creation code 457 // above we revert to the snapshot and consume any energy remaining. 458 if maxCodeSizeExceeded || (err != nil && err != ErrCodeStoreOutOfEnergy) { 459 cvm.StateDB.RevertToSnapshot(snapshot) 460 if err != ErrExecutionReverted { 461 contract.UseEnergy(contract.Energy) 462 } 463 } 464 // Assign err if contract code size exceeds the max while the err is still empty. 465 if maxCodeSizeExceeded && err == nil { 466 err = ErrMaxCodeSizeExceeded 467 } 468 if cvm.vmConfig.Debug && cvm.depth == 0 { 469 cvm.vmConfig.Tracer.CaptureEnd(ret, energy-contract.Energy, time.Since(start), err) 470 } 471 return ret, address, contract.Energy, err 472 473 } 474 475 // Create creates a new contract using code as deployment code. 476 func (cvm *CVM) Create(caller ContractRef, code []byte, energy uint64, value *big.Int) (ret []byte, contractAddr common.Address, leftOverEnergy uint64, err error) { 477 contractAddr = crypto.CreateAddress(caller.Address(), cvm.StateDB.GetNonce(caller.Address())) 478 return cvm.create(caller, &codeAndHash{code: code}, energy, value, contractAddr) 479 } 480 481 // Create2 creates a new contract using code as deployment code. 482 // 483 // The different between Create2 with Create is Create2 uses sha3(0xff ++ msg.sender ++ salt ++ sha3(init_code))[12:] 484 // instead of the usual sender-and-nonce-hash as the address where the contract is initialized at. 485 func (cvm *CVM) Create2(caller ContractRef, code []byte, energy uint64, endowment *big.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, leftOverEnergy uint64, err error) { 486 codeAndHash := &codeAndHash{code: code} 487 contractAddr = crypto.CreateAddress2(caller.Address(), common.Hash(salt.Bytes32()), codeAndHash.Hash().Bytes()) 488 return cvm.create(caller, codeAndHash, energy, endowment, contractAddr) 489 } 490 491 // ChainConfig returns the environment's chain configuration 492 func (cvm *CVM) ChainConfig() *params.ChainConfig { return cvm.chainConfig }