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