|
|
@@ -25,6 +25,7 @@ import (
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
|
"github.com/ethereum/go-ethereum/crypto"
|
|
|
"github.com/ethereum/go-ethereum/params"
|
|
|
+ "github.com/holiman/uint256"
|
|
|
)
|
|
|
|
|
|
// emptyCodeHash is used by create to ensure deployment is disallowed to already
|
|
|
@@ -41,23 +42,24 @@ type (
|
|
|
GetHashFunc func(uint64) common.Hash
|
|
|
)
|
|
|
|
|
|
+func (evm *EVM) precompile(addr common.Address) (PrecompiledContract, bool) {
|
|
|
+ var precompiles map[common.Address]PrecompiledContract
|
|
|
+ switch {
|
|
|
+ case evm.chainRules.IsYoloV1:
|
|
|
+ precompiles = PrecompiledContractsYoloV1
|
|
|
+ case evm.chainRules.IsIstanbul:
|
|
|
+ precompiles = PrecompiledContractsIstanbul
|
|
|
+ case evm.chainRules.IsByzantium:
|
|
|
+ precompiles = PrecompiledContractsByzantium
|
|
|
+ default:
|
|
|
+ precompiles = PrecompiledContractsHomestead
|
|
|
+ }
|
|
|
+ p, ok := precompiles[addr]
|
|
|
+ return p, ok
|
|
|
+}
|
|
|
+
|
|
|
// run runs the given contract and takes care of running precompiles with a fallback to the byte code interpreter.
|
|
|
func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, error) {
|
|
|
- if contract.CodeAddr != nil {
|
|
|
- precompiles := PrecompiledContractsHomestead
|
|
|
- if evm.chainRules.IsByzantium {
|
|
|
- precompiles = PrecompiledContractsByzantium
|
|
|
- }
|
|
|
- if evm.chainRules.IsIstanbul {
|
|
|
- precompiles = PrecompiledContractsIstanbul
|
|
|
- }
|
|
|
- if evm.chainRules.IsYoloV1 {
|
|
|
- precompiles = PrecompiledContractsYoloV1
|
|
|
- }
|
|
|
- if p := precompiles[*contract.CodeAddr]; p != nil {
|
|
|
- return RunPrecompiledContract(p, input, contract)
|
|
|
- }
|
|
|
- }
|
|
|
for _, interpreter := range evm.interpreters {
|
|
|
if interpreter.CanRun(contract.Code) {
|
|
|
if evm.interpreter != interpreter {
|
|
|
@@ -199,22 +201,14 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
|
|
|
return nil, gas, ErrDepth
|
|
|
}
|
|
|
// Fail if we're trying to transfer more than the available balance
|
|
|
- if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) {
|
|
|
+ if value.Sign() != 0 && !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) {
|
|
|
return nil, gas, ErrInsufficientBalance
|
|
|
}
|
|
|
- var (
|
|
|
- to = AccountRef(addr)
|
|
|
- snapshot = evm.StateDB.Snapshot()
|
|
|
- )
|
|
|
+ snapshot := evm.StateDB.Snapshot()
|
|
|
+ p, isPrecompile := evm.precompile(addr)
|
|
|
+
|
|
|
if !evm.StateDB.Exist(addr) {
|
|
|
- precompiles := PrecompiledContractsHomestead
|
|
|
- if evm.chainRules.IsByzantium {
|
|
|
- precompiles = PrecompiledContractsByzantium
|
|
|
- }
|
|
|
- if evm.chainRules.IsIstanbul {
|
|
|
- precompiles = PrecompiledContractsIstanbul
|
|
|
- }
|
|
|
- if precompiles[addr] == nil && evm.chainRules.IsEIP158 && value.Sign() == 0 {
|
|
|
+ if !isPrecompile && evm.chainRules.IsEIP158 && value.Sign() == 0 {
|
|
|
// Calling a non existing account, don't do anything, but ping the tracer
|
|
|
if evm.vmConfig.Debug && evm.depth == 0 {
|
|
|
evm.vmConfig.Tracer.CaptureStart(caller.Address(), addr, false, input, gas, value)
|
|
|
@@ -224,35 +218,47 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
|
|
|
}
|
|
|
evm.StateDB.CreateAccount(addr)
|
|
|
}
|
|
|
- evm.Transfer(evm.StateDB, caller.Address(), to.Address(), value)
|
|
|
- // Initialise a new contract and set the code that is to be used by the EVM.
|
|
|
- // The contract is a scoped environment for this execution context only.
|
|
|
- contract := NewContract(caller, to, value, gas)
|
|
|
- contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
|
|
|
-
|
|
|
- // Even if the account has no code, we need to continue because it might be a precompile
|
|
|
- start := time.Now()
|
|
|
+ evm.Transfer(evm.StateDB, caller.Address(), addr, value)
|
|
|
|
|
|
// Capture the tracer start/end events in debug mode
|
|
|
if evm.vmConfig.Debug && evm.depth == 0 {
|
|
|
evm.vmConfig.Tracer.CaptureStart(caller.Address(), addr, false, input, gas, value)
|
|
|
-
|
|
|
- defer func() { // Lazy evaluation of the parameters
|
|
|
- evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
|
|
|
- }()
|
|
|
+ defer func(startGas uint64, startTime time.Time) { // Lazy evaluation of the parameters
|
|
|
+ evm.vmConfig.Tracer.CaptureEnd(ret, startGas-gas, time.Since(startTime), err)
|
|
|
+ }(gas, time.Now())
|
|
|
}
|
|
|
- ret, err = run(evm, contract, input, false)
|
|
|
|
|
|
+ if isPrecompile {
|
|
|
+ ret, gas, err = RunPrecompiledContract(p, input, gas)
|
|
|
+ } else {
|
|
|
+ // Initialise a new contract and set the code that is to be used by the EVM.
|
|
|
+ // The contract is a scoped environment for this execution context only.
|
|
|
+ code := evm.StateDB.GetCode(addr)
|
|
|
+ if len(code) == 0 {
|
|
|
+ ret, err = nil, nil // gas is unchanged
|
|
|
+ } else {
|
|
|
+ addrCopy := addr
|
|
|
+ // If the account has no code, we can abort here
|
|
|
+ // The depth-check is already done, and precompiles handled above
|
|
|
+ contract := NewContract(caller, AccountRef(addrCopy), value, gas)
|
|
|
+ contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), code)
|
|
|
+ ret, err = run(evm, contract, input, false)
|
|
|
+ gas = contract.Gas
|
|
|
+ }
|
|
|
+ }
|
|
|
// When an error was returned by the EVM or when setting the creation code
|
|
|
// above we revert to the snapshot and consume any gas remaining. Additionally
|
|
|
// when we're in homestead this also counts for code storage gas errors.
|
|
|
if err != nil {
|
|
|
evm.StateDB.RevertToSnapshot(snapshot)
|
|
|
if err != ErrExecutionReverted {
|
|
|
- contract.UseGas(contract.Gas)
|
|
|
+ gas = 0
|
|
|
}
|
|
|
+ // TODO: consider clearing up unused snapshots:
|
|
|
+ //} else {
|
|
|
+ // evm.StateDB.DiscardSnapshot(snapshot)
|
|
|
}
|
|
|
- return ret, contract.Gas, err
|
|
|
+ return ret, gas, err
|
|
|
}
|
|
|
|
|
|
// CallCode executes the contract associated with the addr with the given input
|
|
|
@@ -277,23 +283,27 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
|
|
|
if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) {
|
|
|
return nil, gas, ErrInsufficientBalance
|
|
|
}
|
|
|
- var (
|
|
|
- snapshot = evm.StateDB.Snapshot()
|
|
|
- to = AccountRef(caller.Address())
|
|
|
- )
|
|
|
- // Initialise a new contract and set the code that is to be used by the EVM.
|
|
|
- // The contract is a scoped environment for this execution context only.
|
|
|
- contract := NewContract(caller, to, value, gas)
|
|
|
- contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
|
|
|
-
|
|
|
- ret, err = run(evm, contract, input, false)
|
|
|
+ var snapshot = evm.StateDB.Snapshot()
|
|
|
+
|
|
|
+ // It is allowed to call precompiles, even via delegatecall
|
|
|
+ if p, isPrecompile := evm.precompile(addr); isPrecompile {
|
|
|
+ ret, gas, err = RunPrecompiledContract(p, input, gas)
|
|
|
+ } else {
|
|
|
+ addrCopy := addr
|
|
|
+ // Initialise a new contract and set the code that is to be used by the EVM.
|
|
|
+ // The contract is a scoped environment for this execution context only.
|
|
|
+ contract := NewContract(caller, AccountRef(caller.Address()), value, gas)
|
|
|
+ contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy))
|
|
|
+ ret, err = run(evm, contract, input, false)
|
|
|
+ gas = contract.Gas
|
|
|
+ }
|
|
|
if err != nil {
|
|
|
evm.StateDB.RevertToSnapshot(snapshot)
|
|
|
if err != ErrExecutionReverted {
|
|
|
- contract.UseGas(contract.Gas)
|
|
|
+ gas = 0
|
|
|
}
|
|
|
}
|
|
|
- return ret, contract.Gas, err
|
|
|
+ return ret, gas, err
|
|
|
}
|
|
|
|
|
|
// DelegateCall executes the contract associated with the addr with the given input
|
|
|
@@ -309,22 +319,26 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
|
|
|
if evm.depth > int(params.CallCreateDepth) {
|
|
|
return nil, gas, ErrDepth
|
|
|
}
|
|
|
- var (
|
|
|
- snapshot = evm.StateDB.Snapshot()
|
|
|
- to = AccountRef(caller.Address())
|
|
|
- )
|
|
|
- // Initialise a new contract and make initialise the delegate values
|
|
|
- contract := NewContract(caller, to, nil, gas).AsDelegate()
|
|
|
- contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
|
|
|
-
|
|
|
- ret, err = run(evm, contract, input, false)
|
|
|
+ var snapshot = evm.StateDB.Snapshot()
|
|
|
+
|
|
|
+ // It is allowed to call precompiles, even via delegatecall
|
|
|
+ if p, isPrecompile := evm.precompile(addr); isPrecompile {
|
|
|
+ ret, gas, err = RunPrecompiledContract(p, input, gas)
|
|
|
+ } else {
|
|
|
+ addrCopy := addr
|
|
|
+ // Initialise a new contract and make initialise the delegate values
|
|
|
+ contract := NewContract(caller, AccountRef(caller.Address()), nil, gas).AsDelegate()
|
|
|
+ contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy))
|
|
|
+ ret, err = run(evm, contract, input, false)
|
|
|
+ gas = contract.Gas
|
|
|
+ }
|
|
|
if err != nil {
|
|
|
evm.StateDB.RevertToSnapshot(snapshot)
|
|
|
if err != ErrExecutionReverted {
|
|
|
- contract.UseGas(contract.Gas)
|
|
|
+ gas = 0
|
|
|
}
|
|
|
}
|
|
|
- return ret, contract.Gas, err
|
|
|
+ return ret, gas, err
|
|
|
}
|
|
|
|
|
|
// StaticCall executes the contract associated with the addr with the given input
|
|
|
@@ -339,32 +353,43 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
|
|
|
if evm.depth > int(params.CallCreateDepth) {
|
|
|
return nil, gas, ErrDepth
|
|
|
}
|
|
|
- var (
|
|
|
- to = AccountRef(addr)
|
|
|
- snapshot = evm.StateDB.Snapshot()
|
|
|
- )
|
|
|
- // Initialise a new contract and set the code that is to be used by the EVM.
|
|
|
- // The contract is a scoped environment for this execution context only.
|
|
|
- contract := NewContract(caller, to, new(big.Int), gas)
|
|
|
- contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
|
|
|
+ // We take a snapshot here. This is a bit counter-intuitive, and could probably be skipped.
|
|
|
+ // However, even a staticcall is considered a 'touch'. On mainnet, static calls were introduced
|
|
|
+ // after all empty accounts were deleted, so this is not required. However, if we omit this,
|
|
|
+ // then certain tests start failing; stRevertTest/RevertPrecompiledTouchExactOOG.json.
|
|
|
+ // We could change this, but for now it's left for legacy reasons
|
|
|
+ var snapshot = evm.StateDB.Snapshot()
|
|
|
|
|
|
// We do an AddBalance of zero here, just in order to trigger a touch.
|
|
|
// This doesn't matter on Mainnet, where all empties are gone at the time of Byzantium,
|
|
|
// but is the correct thing to do and matters on other networks, in tests, and potential
|
|
|
// future scenarios
|
|
|
- evm.StateDB.AddBalance(addr, big.NewInt(0))
|
|
|
-
|
|
|
- // When an error was returned by the EVM or when setting the creation code
|
|
|
- // above we revert to the snapshot and consume any gas remaining. Additionally
|
|
|
- // when we're in Homestead this also counts for code storage gas errors.
|
|
|
- ret, err = run(evm, contract, input, true)
|
|
|
+ evm.StateDB.AddBalance(addr, big0)
|
|
|
+
|
|
|
+ if p, isPrecompile := evm.precompile(addr); isPrecompile {
|
|
|
+ ret, gas, err = RunPrecompiledContract(p, input, gas)
|
|
|
+ } else {
|
|
|
+ // At this point, we use a copy of address. If we don't, the go compiler will
|
|
|
+ // leak the 'contract' to the outer scope, and make allocation for 'contract'
|
|
|
+ // even if the actual execution ends on RunPrecompiled above.
|
|
|
+ addrCopy := addr
|
|
|
+ // Initialise a new contract and set the code that is to be used by the EVM.
|
|
|
+ // The contract is a scoped environment for this execution context only.
|
|
|
+ contract := NewContract(caller, AccountRef(addrCopy), new(big.Int), gas)
|
|
|
+ contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy))
|
|
|
+ // When an error was returned by the EVM or when setting the creation code
|
|
|
+ // above we revert to the snapshot and consume any gas remaining. Additionally
|
|
|
+ // when we're in Homestead this also counts for code storage gas errors.
|
|
|
+ ret, err = run(evm, contract, input, true)
|
|
|
+ gas = contract.Gas
|
|
|
+ }
|
|
|
if err != nil {
|
|
|
evm.StateDB.RevertToSnapshot(snapshot)
|
|
|
if err != ErrExecutionReverted {
|
|
|
- contract.UseGas(contract.Gas)
|
|
|
+ gas = 0
|
|
|
}
|
|
|
}
|
|
|
- return ret, contract.Gas, err
|
|
|
+ return ret, gas, err
|
|
|
}
|
|
|
|
|
|
type codeAndHash struct {
|
|
|
@@ -466,9 +491,9 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I
|
|
|
//
|
|
|
// The different between Create2 with Create is Create2 uses sha3(0xff ++ msg.sender ++ salt ++ sha3(init_code))[12:]
|
|
|
// instead of the usual sender-and-nonce-hash as the address where the contract is initialized at.
|
|
|
-func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *big.Int, salt *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
|
|
|
+func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *big.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
|
|
|
codeAndHash := &codeAndHash{code: code}
|
|
|
- contractAddr = crypto.CreateAddress2(caller.Address(), common.BigToHash(salt), codeAndHash.Hash().Bytes())
|
|
|
+ contractAddr = crypto.CreateAddress2(caller.Address(), common.Hash(salt.Bytes32()), codeAndHash.Hash().Bytes())
|
|
|
return evm.create(caller, codeAndHash, gas, endowment, contractAddr)
|
|
|
}
|
|
|
|