Charge witness gas when calling/creating a contract (#60)
* Charge witness gas when calling/creating a contract Co-authored-by: Jared Wasinger <j-wasinger@hotmail.com> * gofmt * replace checks with evm.Access!=nil with IsCancun * remove double-charging of witness access costs for contract creation initialization Co-authored-by: Jared Wasinger <j-wasinger@hotmail.com>
This commit is contained in:
@ -288,6 +288,24 @@ func (s *StateDB) GetBalance(addr common.Address) *big.Int {
|
||||
return common.Big0
|
||||
}
|
||||
|
||||
func (s *StateDB) GetNonceLittleEndian(address common.Address) []byte {
|
||||
var nonceBytes [8]byte
|
||||
binary.LittleEndian.PutUint64(nonceBytes[:], s.GetNonce(address))
|
||||
return nonceBytes[:]
|
||||
}
|
||||
|
||||
func (s *StateDB) GetBalanceLittleEndian(address common.Address) []byte {
|
||||
var paddedBalance [32]byte
|
||||
balanceBytes := s.GetBalance(address).Bytes()
|
||||
// swap to little-endian
|
||||
for i, j := 0, len(balanceBytes)-1; i < j; i, j = i+1, j-1 {
|
||||
balanceBytes[i], balanceBytes[j] = balanceBytes[j], balanceBytes[i]
|
||||
}
|
||||
|
||||
copy(paddedBalance[:len(balanceBytes)], balanceBytes)
|
||||
return paddedBalance[:len(balanceBytes)]
|
||||
}
|
||||
|
||||
func (s *StateDB) GetNonce(addr common.Address) uint64 {
|
||||
stateObject := s.getStateObject(addr)
|
||||
if stateObject != nil {
|
||||
@ -486,20 +504,27 @@ func (s *StateDB) updateStateObject(obj *stateObject) {
|
||||
if err := s.trie.TryUpdateAccount(addr[:], &obj.data); err != nil {
|
||||
s.setError(fmt.Errorf("updateStateObject (%x) error: %w", addr[:], err))
|
||||
}
|
||||
if len(obj.code) > 0 && s.trie.IsVerkle() {
|
||||
cs := make([]byte, 32)
|
||||
binary.BigEndian.PutUint64(cs, uint64(len(obj.code)))
|
||||
if err := s.trie.TryUpdate(trieUtils.GetTreeKeyCodeSize(addr[:]), cs); err != nil {
|
||||
s.setError(fmt.Errorf("updateStateObject (%x) error: %w", addr[:], err))
|
||||
}
|
||||
if s.trie.IsVerkle() {
|
||||
if len(obj.code) > 0 {
|
||||
cs := make([]byte, 32)
|
||||
binary.BigEndian.PutUint64(cs, uint64(len(obj.code)))
|
||||
if err := s.trie.TryUpdate(trieUtils.GetTreeKeyCodeSize(addr[:]), cs); err != nil {
|
||||
s.setError(fmt.Errorf("updateStateObject (%x) error: %w", addr[:], err))
|
||||
}
|
||||
|
||||
if obj.dirtyCode {
|
||||
if chunks, err := trie.ChunkifyCode(addr, obj.code); err == nil {
|
||||
for i := range chunks {
|
||||
s.trie.TryUpdate(trieUtils.GetTreeKeyCodeChunk(addr[:], uint256.NewInt(uint64(i))), chunks[i][:])
|
||||
if obj.dirtyCode {
|
||||
if chunks, err := trie.ChunkifyCode(addr, obj.code); err == nil {
|
||||
for i := range chunks {
|
||||
s.trie.TryUpdate(trieUtils.GetTreeKeyCodeChunk(addr[:], uint256.NewInt(uint64(i))), chunks[i][:])
|
||||
}
|
||||
} else {
|
||||
s.setError(err)
|
||||
}
|
||||
} else {
|
||||
s.setError(err)
|
||||
}
|
||||
} else {
|
||||
cs := []byte{0}
|
||||
if err := s.trie.TryUpdate(trieUtils.GetTreeKeyCodeSize(addr[:]), cs); err != nil {
|
||||
s.setError(fmt.Errorf("updateStateObject (%x) error: %w", addr[:], err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,6 @@ import (
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
trieUtils "github.com/ethereum/go-ethereum/trie/utils"
|
||||
)
|
||||
|
||||
var emptyCodeHash = crypto.Keccak256Hash(nil)
|
||||
@ -261,6 +260,19 @@ func (st *StateTransition) preCheck() error {
|
||||
return st.buyGas()
|
||||
}
|
||||
|
||||
// tryConsumeGas tries to subtract gas from gasPool, setting the result in gasPool
|
||||
// if subtracting more gas than remains in gasPool, set gasPool = 0 and return false
|
||||
// otherwise, do the subtraction setting the result in gasPool and return true
|
||||
func tryConsumeGas(gasPool *uint64, gas uint64) bool {
|
||||
if *gasPool < gas {
|
||||
*gasPool = 0
|
||||
return false
|
||||
}
|
||||
|
||||
*gasPool -= gas
|
||||
return true
|
||||
}
|
||||
|
||||
// TransitionDb will transition the state by applying the current message and
|
||||
// returning the evm execution result with following fields.
|
||||
//
|
||||
@ -305,25 +317,33 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
|
||||
return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, st.gas, gas)
|
||||
}
|
||||
if st.evm.ChainConfig().IsCancun(st.evm.Context.BlockNumber) {
|
||||
if msg.To() != nil {
|
||||
toBalance := trieUtils.GetTreeKeyBalance(msg.To().Bytes())
|
||||
pre := st.state.GetBalance(*msg.To())
|
||||
gas += st.evm.Accesses.TouchAddressAndChargeGas(toBalance, pre.Bytes())
|
||||
var targetBalance, targetNonce, targetCodeSize, targetCodeKeccak, originBalance, originNonce []byte
|
||||
|
||||
// NOTE: Nonce also needs to be charged, because it is needed for execution
|
||||
// on the statless side.
|
||||
var preTN [8]byte
|
||||
fromNonce := trieUtils.GetTreeKeyNonce(msg.To().Bytes())
|
||||
binary.BigEndian.PutUint64(preTN[:], st.state.GetNonce(*msg.To()))
|
||||
gas += st.evm.Accesses.TouchAddressAndChargeGas(fromNonce, preTN[:])
|
||||
targetAddr := msg.To()
|
||||
originAddr := msg.From()
|
||||
|
||||
statelessGasOrigin := st.evm.Accesses.TouchTxOriginAndChargeGas(originAddr.Bytes())
|
||||
if !tryConsumeGas(&st.gas, statelessGasOrigin) {
|
||||
return nil, fmt.Errorf("insufficient gas to cover witness access costs")
|
||||
}
|
||||
originBalance = st.evm.StateDB.GetBalanceLittleEndian(originAddr)
|
||||
originNonce = st.evm.StateDB.GetNonceLittleEndian(originAddr)
|
||||
st.evm.Accesses.SetTxTouchedLeaves(originAddr.Bytes(), originBalance, originNonce)
|
||||
|
||||
if msg.To() != nil {
|
||||
statelessGasDest := st.evm.Accesses.TouchTxExistingAndChargeGas(targetAddr.Bytes())
|
||||
if !tryConsumeGas(&st.gas, statelessGasDest) {
|
||||
return nil, fmt.Errorf("insufficient gas to cover witness access costs")
|
||||
}
|
||||
targetBalance = st.evm.StateDB.GetBalanceLittleEndian(*targetAddr)
|
||||
targetNonce = st.evm.StateDB.GetNonceLittleEndian(*targetAddr)
|
||||
targetCodeKeccak = st.evm.StateDB.GetCodeHash(*targetAddr).Bytes()
|
||||
|
||||
codeSize := uint64(st.evm.StateDB.GetCodeSize(*targetAddr))
|
||||
var codeSizeBytes [32]byte
|
||||
binary.LittleEndian.PutUint64(codeSizeBytes[:8], codeSize)
|
||||
st.evm.Accesses.SetTxExistingTouchedLeaves(targetAddr.Bytes(), targetBalance, targetNonce, targetCodeSize, targetCodeKeccak)
|
||||
}
|
||||
fromBalance := trieUtils.GetTreeKeyBalance(msg.From().Bytes())
|
||||
preFB := st.state.GetBalance(msg.From()).Bytes()
|
||||
fromNonce := trieUtils.GetTreeKeyNonce(msg.From().Bytes())
|
||||
var preFN [8]byte
|
||||
binary.BigEndian.PutUint64(preFN[:], st.state.GetNonce(msg.From()))
|
||||
gas += st.evm.Accesses.TouchAddressAndChargeGas(fromNonce, preFN[:])
|
||||
gas += st.evm.Accesses.TouchAddressAndChargeGas(fromBalance, preFB[:])
|
||||
}
|
||||
st.gas -= gas
|
||||
|
||||
|
@ -19,6 +19,7 @@ package types
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/trie/utils"
|
||||
)
|
||||
|
||||
// AccessWitness lists the locations of the state that are being accessed
|
||||
@ -45,6 +46,125 @@ func NewAccessWitness() *AccessWitness {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO TouchAndCharge + SetLeafValue* does redundant calls to GetTreeKey*
|
||||
|
||||
func (aw *AccessWitness) TouchAndChargeProofOfAbsence(addr []byte) uint64 {
|
||||
var gas uint64
|
||||
gas += aw.TouchAddressAndChargeGas(utils.GetTreeKeyVersion(addr[:]), nil)
|
||||
gas += aw.TouchAddressAndChargeGas(utils.GetTreeKeyBalance(addr[:]), nil)
|
||||
gas += aw.TouchAddressAndChargeGas(utils.GetTreeKeyCodeSize(addr[:]), nil)
|
||||
gas += aw.TouchAddressAndChargeGas(utils.GetTreeKeyCodeKeccak(addr[:]), nil)
|
||||
gas += aw.TouchAddressAndChargeGas(utils.GetTreeKeyNonce(addr[:]), nil)
|
||||
return gas
|
||||
}
|
||||
|
||||
func (aw *AccessWitness) TouchAndChargeMessageCall(addr []byte) uint64 {
|
||||
var gas uint64
|
||||
gas += aw.TouchAddressAndChargeGas(utils.GetTreeKeyVersion(addr[:]), nil)
|
||||
gas += aw.TouchAddressAndChargeGas(utils.GetTreeKeyCodeSize(addr[:]), nil)
|
||||
return gas
|
||||
}
|
||||
|
||||
func (aw *AccessWitness) SetLeafValuesMessageCall(addr, codeSize []byte) {
|
||||
var data [32]byte
|
||||
aw.TouchAddress(utils.GetTreeKeyVersion(addr[:]), data[:])
|
||||
aw.TouchAddress(utils.GetTreeKeyCodeSize(addr[:]), codeSize[:])
|
||||
}
|
||||
|
||||
func (aw *AccessWitness) TouchAndChargeValueTransfer(callerAddr, targetAddr []byte) uint64 {
|
||||
var gas uint64
|
||||
gas += aw.TouchAddressAndChargeGas(utils.GetTreeKeyBalance(callerAddr[:]), nil)
|
||||
gas += aw.TouchAddressAndChargeGas(utils.GetTreeKeyBalance(targetAddr[:]), nil)
|
||||
return gas
|
||||
}
|
||||
|
||||
func (aw *AccessWitness) SetLeafValuesValueTransfer(callerAddr, targetAddr, callerBalance, targetBalance []byte) {
|
||||
aw.TouchAddress(utils.GetTreeKeyBalance(callerAddr[:]), callerBalance)
|
||||
aw.TouchAddress(utils.GetTreeKeyBalance(targetAddr[:]), targetBalance)
|
||||
}
|
||||
|
||||
// TouchAndChargeContractCreateInit charges access costs to initiate
|
||||
// a contract creation
|
||||
func (aw *AccessWitness) TouchAndChargeContractCreateInit(addr []byte) uint64 {
|
||||
var gas uint64
|
||||
gas += aw.TouchAddressAndChargeGas(utils.GetTreeKeyVersion(addr[:]), nil)
|
||||
gas += aw.TouchAddressAndChargeGas(utils.GetTreeKeyNonce(addr[:]), nil)
|
||||
return gas
|
||||
}
|
||||
|
||||
func (aw *AccessWitness) SetLeafValuesContractCreateInit(addr, nonce []byte) {
|
||||
var version [32]byte
|
||||
aw.TouchAddress(utils.GetTreeKeyVersion(addr[:]), version[:])
|
||||
aw.TouchAddress(utils.GetTreeKeyNonce(addr[:]), nonce)
|
||||
}
|
||||
|
||||
// TouchAndChargeContractCreateCompleted charges access access costs after
|
||||
// the completion of a contract creation to populate the created account in
|
||||
// the tree
|
||||
func (aw *AccessWitness) TouchAndChargeContractCreateCompleted(addr []byte, withValue bool) uint64 {
|
||||
var gas uint64
|
||||
gas += aw.TouchAddressAndChargeGas(utils.GetTreeKeyVersion(addr[:]), nil)
|
||||
gas += aw.TouchAddressAndChargeGas(utils.GetTreeKeyNonce(addr[:]), nil)
|
||||
if withValue {
|
||||
gas += aw.TouchAddressAndChargeGas(utils.GetTreeKeyBalance(addr[:]), nil)
|
||||
}
|
||||
gas += aw.TouchAddressAndChargeGas(utils.GetTreeKeyCodeSize(addr[:]), nil)
|
||||
gas += aw.TouchAddressAndChargeGas(utils.GetTreeKeyCodeKeccak(addr[:]), nil)
|
||||
return gas
|
||||
}
|
||||
|
||||
func (aw *AccessWitness) SetLeafValuesContractCreateCompleted(addr, codeSize, codeKeccak []byte) {
|
||||
aw.TouchAddress(utils.GetTreeKeyCodeSize(addr[:]), codeSize)
|
||||
aw.TouchAddress(utils.GetTreeKeyCodeKeccak(addr[:]), codeKeccak)
|
||||
}
|
||||
|
||||
func (aw *AccessWitness) TouchTxAndChargeGas(originAddr, targetAddr []byte) uint64 {
|
||||
var gasUsed uint64
|
||||
var version [32]byte
|
||||
gasUsed += aw.TouchAddressAndChargeGas(utils.GetTreeKeyVersion(originAddr[:]), version[:])
|
||||
gasUsed += aw.TouchAddressAndChargeGas(utils.GetTreeKeyBalance(originAddr[:]), nil)
|
||||
gasUsed += aw.TouchAddressAndChargeGas(utils.GetTreeKeyNonce(originAddr[:]), nil)
|
||||
|
||||
gasUsed += aw.TouchAddressAndChargeGas(utils.GetTreeKeyVersion(targetAddr[:]), version[:])
|
||||
gasUsed += aw.TouchAddressAndChargeGas(utils.GetTreeKeyBalance(targetAddr[:]), nil)
|
||||
gasUsed += aw.TouchAddressAndChargeGas(utils.GetTreeKeyNonce(targetAddr[:]), nil)
|
||||
gasUsed += aw.TouchAddressAndChargeGas(utils.GetTreeKeyCodeSize(targetAddr[:]), nil)
|
||||
gasUsed += aw.TouchAddressAndChargeGas(utils.GetTreeKeyCodeKeccak(targetAddr[:]), nil)
|
||||
return gasUsed
|
||||
}
|
||||
|
||||
func (aw *AccessWitness) TouchTxOriginAndChargeGas(originAddr []byte) uint64 {
|
||||
var gasUsed uint64
|
||||
var version [32]byte
|
||||
gasUsed += aw.TouchAddressAndChargeGas(utils.GetTreeKeyVersion(originAddr[:]), version[:])
|
||||
gasUsed += aw.TouchAddressAndChargeGas(utils.GetTreeKeyBalance(originAddr[:]), nil)
|
||||
gasUsed += aw.TouchAddressAndChargeGas(utils.GetTreeKeyNonce(originAddr[:]), nil)
|
||||
return gasUsed
|
||||
}
|
||||
|
||||
func (aw *AccessWitness) TouchTxExistingAndChargeGas(targetAddr []byte) uint64 {
|
||||
var gasUsed uint64
|
||||
var version [32]byte
|
||||
gasUsed += aw.TouchAddressAndChargeGas(utils.GetTreeKeyVersion(targetAddr[:]), version[:])
|
||||
gasUsed += aw.TouchAddressAndChargeGas(utils.GetTreeKeyBalance(targetAddr[:]), nil)
|
||||
gasUsed += aw.TouchAddressAndChargeGas(utils.GetTreeKeyNonce(targetAddr[:]), nil)
|
||||
gasUsed += aw.TouchAddressAndChargeGas(utils.GetTreeKeyCodeSize(targetAddr[:]), nil)
|
||||
gasUsed += aw.TouchAddressAndChargeGas(utils.GetTreeKeyCodeKeccak(targetAddr[:]), nil)
|
||||
return gasUsed
|
||||
}
|
||||
|
||||
func (aw *AccessWitness) SetTxTouchedLeaves(originAddr, originBalance, originNonce []byte) {
|
||||
aw.TouchAddress(utils.GetTreeKeyBalance(originAddr[:]), originBalance)
|
||||
aw.TouchAddress(utils.GetTreeKeyNonce(originAddr[:]), originNonce)
|
||||
}
|
||||
|
||||
func (aw *AccessWitness) SetTxExistingTouchedLeaves(targetAddr, targetBalance, targetNonce, targetCodeSize, targetCodeHash []byte) {
|
||||
aw.TouchAddress(utils.GetTreeKeyBalance(targetAddr[:]), targetBalance)
|
||||
aw.TouchAddress(utils.GetTreeKeyNonce(targetAddr[:]), targetNonce)
|
||||
aw.TouchAddress(utils.GetTreeKeyCodeSize(targetAddr[:]), targetCodeSize)
|
||||
aw.TouchAddress(utils.GetTreeKeyCodeKeccak(targetAddr[:]), targetCodeHash)
|
||||
}
|
||||
|
||||
// TouchAddress adds any missing addr to the witness and returns respectively
|
||||
// true if the stem or the stub weren't arleady present.
|
||||
func (aw *AccessWitness) TouchAddress(addr, value []byte) (bool, bool) {
|
||||
|
@ -202,6 +202,10 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
|
||||
|
||||
if !evm.StateDB.Exist(addr) {
|
||||
if !isPrecompile && evm.chainRules.IsEIP158 && value.Sign() == 0 {
|
||||
if evm.chainConfig.IsCancun(evm.Context.BlockNumber) {
|
||||
// proof of absence
|
||||
tryConsumeGas(&gas, evm.Accesses.TouchAndChargeProofOfAbsence(caller.Address().Bytes()))
|
||||
}
|
||||
// Calling a non existing account, don't do anything, but ping the tracer
|
||||
if evm.Config.Debug {
|
||||
if evm.depth == 0 {
|
||||
@ -216,6 +220,11 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
|
||||
}
|
||||
evm.StateDB.CreateAccount(addr)
|
||||
}
|
||||
if evm.chainConfig.IsCancun(evm.Context.BlockNumber) && value.Sign() != 0 {
|
||||
callerBalanceBefore := evm.StateDB.GetBalanceLittleEndian(caller.Address())
|
||||
targetBalanceBefore := evm.StateDB.GetBalanceLittleEndian(addr)
|
||||
evm.Accesses.SetLeafValuesValueTransfer(caller.Address().Bytes()[:], addr[:], callerBalanceBefore, targetBalanceBefore)
|
||||
}
|
||||
evm.Context.Transfer(evm.StateDB, caller.Address(), addr, value)
|
||||
|
||||
// Capture the tracer start/end events in debug mode
|
||||
@ -240,21 +249,16 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
|
||||
// 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 evm.chainConfig.IsCancun(evm.Context.BlockNumber) {
|
||||
codeSize := uint64(len(code))
|
||||
var codeSizeBytes [32]byte
|
||||
binary.LittleEndian.PutUint64(codeSizeBytes[:8], codeSize)
|
||||
evm.Accesses.SetLeafValuesMessageCall(addr[:], codeSizeBytes[:])
|
||||
}
|
||||
|
||||
if len(code) == 0 {
|
||||
ret, err = nil, nil // gas is unchanged
|
||||
} else {
|
||||
if evm.chainConfig.IsCancun(evm.Context.BlockNumber) {
|
||||
// Touch the account data
|
||||
var data [32]byte
|
||||
evm.Accesses.TouchAddress(utils.GetTreeKeyVersion(addr.Bytes()), data[:])
|
||||
binary.BigEndian.PutUint64(data[:], evm.StateDB.GetNonce(addr))
|
||||
evm.Accesses.TouchAddress(utils.GetTreeKeyNonce(addr[:]), data[:])
|
||||
evm.Accesses.TouchAddress(utils.GetTreeKeyBalance(addr[:]), evm.StateDB.GetBalance(addr).Bytes())
|
||||
binary.BigEndian.PutUint64(data[:], uint64(len(code)))
|
||||
evm.Accesses.TouchAddress(utils.GetTreeKeyCodeSize(addr[:]), data[:])
|
||||
evm.Accesses.TouchAddress(utils.GetTreeKeyCodeKeccak(addr[:]), evm.StateDB.GetCodeHash(addr).Bytes())
|
||||
}
|
||||
|
||||
addrCopy := addr
|
||||
// If the account has no code, we can abort here
|
||||
// The depth-check is already done, and precompiles handled above
|
||||
@ -316,6 +320,12 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
|
||||
if p, isPrecompile := evm.precompile(addr); isPrecompile {
|
||||
ret, gas, err = RunPrecompiledContract(p, input, gas)
|
||||
} else {
|
||||
if evm.chainConfig.IsCancun(evm.Context.BlockNumber) {
|
||||
codeSize := uint64(evm.StateDB.GetCodeSize(addr))
|
||||
var codeSizeBytes [32]byte
|
||||
binary.LittleEndian.PutUint64(codeSizeBytes[:8], codeSize)
|
||||
evm.Accesses.SetLeafValuesMessageCall(addr.Bytes()[:], codeSizeBytes[:])
|
||||
}
|
||||
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.
|
||||
@ -360,6 +370,12 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
|
||||
if p, isPrecompile := evm.precompile(addr); isPrecompile {
|
||||
ret, gas, err = RunPrecompiledContract(p, input, gas)
|
||||
} else {
|
||||
if evm.chainConfig.IsCancun(evm.Context.BlockNumber) {
|
||||
codeSize := uint64(evm.StateDB.GetCodeSize(addr))
|
||||
var codeSizeBytes [32]byte
|
||||
binary.LittleEndian.PutUint64(codeSizeBytes[:8], codeSize)
|
||||
evm.Accesses.SetLeafValuesMessageCall(addr.Bytes()[:], codeSizeBytes[:])
|
||||
}
|
||||
addrCopy := addr
|
||||
// Initialise a new contract and make initialise the delegate values
|
||||
contract := NewContract(caller, AccountRef(caller.Address()), nil, gas).AsDelegate()
|
||||
@ -412,6 +428,12 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
|
||||
if p, isPrecompile := evm.precompile(addr); isPrecompile {
|
||||
ret, gas, err = RunPrecompiledContract(p, input, gas)
|
||||
} else {
|
||||
if evm.chainConfig.IsCancun(evm.Context.BlockNumber) {
|
||||
codeSize := uint64(evm.StateDB.GetCodeSize(addr))
|
||||
var codeSizeBytes [32]byte
|
||||
binary.LittleEndian.PutUint64(codeSizeBytes[:8], codeSize)
|
||||
evm.Accesses.SetLeafValuesMessageCall(addr.Bytes()[:], codeSizeBytes[:])
|
||||
}
|
||||
// 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.
|
||||
@ -449,6 +471,13 @@ func (c *codeAndHash) Hash() common.Hash {
|
||||
|
||||
// create creates a new contract using code as deployment code.
|
||||
func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, value *big.Int, address common.Address, typ OpCode) ([]byte, common.Address, uint64, error) {
|
||||
var zeroVerkleLeaf [32]byte
|
||||
var balanceBefore []byte
|
||||
|
||||
if evm.chainConfig.IsCancun(evm.Context.BlockNumber) {
|
||||
evm.Accesses.SetLeafValuesContractCreateInit(address.Bytes()[:], zeroVerkleLeaf[:])
|
||||
}
|
||||
|
||||
// Depth check execution. Fail if we're trying to execute above the
|
||||
// limit.
|
||||
if evm.depth > int(params.CallCreateDepth) {
|
||||
@ -478,6 +507,14 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
|
||||
if evm.chainRules.IsEIP158 {
|
||||
evm.StateDB.SetNonce(address, 1)
|
||||
}
|
||||
if evm.chainConfig.IsCancun(evm.Context.BlockNumber) {
|
||||
// note: assumption is that the nonce, code size, code hash
|
||||
// will be 0x0000...00 at the target account before it is created
|
||||
// otherwise would imply contract creation collision which is
|
||||
// impossible if self-destruct is removed
|
||||
balanceBefore = evm.StateDB.GetBalanceLittleEndian(address)
|
||||
}
|
||||
|
||||
evm.Context.Transfer(evm.StateDB, caller.Address(), address, value)
|
||||
|
||||
// Initialise a new contract and set the code that is to be used by the EVM.
|
||||
@ -535,6 +572,18 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
|
||||
}
|
||||
}
|
||||
|
||||
if err == nil && evm.chainConfig.IsCancun(evm.Context.BlockNumber) {
|
||||
if !contract.UseGas(evm.Accesses.TouchAndChargeContractCreateCompleted(address.Bytes()[:], value.Sign() != 0)) {
|
||||
evm.StateDB.RevertToSnapshot(snapshot)
|
||||
err = ErrOutOfGas
|
||||
} else {
|
||||
evm.Accesses.SetLeafValuesContractCreateCompleted(address.Bytes()[:], zeroVerkleLeaf[:], zeroVerkleLeaf[:])
|
||||
if value.Sign() != 0 {
|
||||
evm.Accesses.TouchAddress(utils.GetTreeKeyBalance(address.Bytes()[:]), balanceBefore)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if evm.Config.Debug {
|
||||
if evm.depth == 0 {
|
||||
evm.Config.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
|
||||
|
@ -21,6 +21,7 @@ import (
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
trieUtils "github.com/ethereum/go-ethereum/trie/utils"
|
||||
)
|
||||
@ -408,13 +409,6 @@ func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize
|
||||
transfersValue = !stack.Back(2).IsZero()
|
||||
address = common.Address(stack.Back(1).Bytes20())
|
||||
)
|
||||
if evm.chainConfig.IsCancun(evm.Context.BlockNumber) {
|
||||
// Charge witness costs
|
||||
for i := trieUtils.VersionLeafKey; i <= trieUtils.CodeSizeLeafKey; i++ {
|
||||
index := trieUtils.GetTreeKeyAccountLeaf(address[:], byte(i))
|
||||
gas += evm.TxContext.Accesses.TouchAddressAndChargeGas(index, nil)
|
||||
}
|
||||
}
|
||||
|
||||
if evm.chainRules.IsEIP158 {
|
||||
if transfersValue && evm.StateDB.Empty(address) {
|
||||
@ -442,6 +436,20 @@ func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize
|
||||
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
if evm.chainConfig.IsCancun(evm.Context.BlockNumber) {
|
||||
if _, isPrecompile := evm.precompile(address); !isPrecompile {
|
||||
gas, overflow = math.SafeAdd(gas, evm.Accesses.TouchAndChargeMessageCall(address.Bytes()[:]))
|
||||
if overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
}
|
||||
if transfersValue {
|
||||
gas, overflow = math.SafeAdd(gas, evm.Accesses.TouchAndChargeValueTransfer(contract.Address().Bytes()[:], address.Bytes()[:]))
|
||||
if overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return gas, nil
|
||||
}
|
||||
@ -468,6 +476,15 @@ func gasCallCode(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memory
|
||||
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
if evm.chainConfig.IsCancun(evm.Context.BlockNumber) {
|
||||
address := common.Address(stack.Back(1).Bytes20())
|
||||
if _, isPrecompile := evm.precompile(address); !isPrecompile {
|
||||
gas, overflow = math.SafeAdd(gas, evm.Accesses.TouchAndChargeMessageCall(address.Bytes()))
|
||||
if overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
}
|
||||
}
|
||||
return gas, nil
|
||||
}
|
||||
|
||||
@ -484,6 +501,15 @@ func gasDelegateCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me
|
||||
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
if evm.chainConfig.IsCancun(evm.Context.BlockNumber) {
|
||||
address := common.Address(stack.Back(1).Bytes20())
|
||||
if _, isPrecompile := evm.precompile(address); !isPrecompile {
|
||||
gas, overflow = math.SafeAdd(gas, evm.Accesses.TouchAndChargeMessageCall(address.Bytes()))
|
||||
if overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
}
|
||||
}
|
||||
return gas, nil
|
||||
}
|
||||
|
||||
@ -500,6 +526,15 @@ func gasStaticCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memo
|
||||
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
if evm.chainConfig.IsCancun(evm.Context.BlockNumber) {
|
||||
address := common.Address(stack.Back(1).Bytes20())
|
||||
if _, isPrecompile := evm.precompile(address); !isPrecompile {
|
||||
gas, overflow = math.SafeAdd(gas, evm.Accesses.TouchAndChargeMessageCall(address.Bytes()))
|
||||
if overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
}
|
||||
}
|
||||
return gas, nil
|
||||
}
|
||||
|
||||
@ -520,6 +555,12 @@ func gasSelfdestruct(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me
|
||||
}
|
||||
}
|
||||
|
||||
if evm.chainConfig.IsCancun(evm.Context.BlockNumber) {
|
||||
// TODO turn this into a panic (when we are sure this method
|
||||
// will never execute when verkle is enabled)
|
||||
log.Warn("verkle witness accumulation not supported for selfdestruct")
|
||||
}
|
||||
|
||||
if !evm.StateDB.HasSuicided(contract.Address()) {
|
||||
evm.StateDB.AddRefund(params.SelfdestructRefundGas)
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ package vm
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
trieUtils "github.com/ethereum/go-ethereum/trie/utils"
|
||||
"github.com/holiman/uint256"
|
||||
@ -667,6 +668,13 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b
|
||||
input = scope.Memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64()))
|
||||
gas = scope.Contract.Gas
|
||||
)
|
||||
if interpreter.evm.chainConfig.IsCancun(interpreter.evm.Context.BlockNumber) {
|
||||
contractAddress := crypto.CreateAddress(scope.Contract.Address(), interpreter.evm.StateDB.GetNonce(scope.Contract.Address()))
|
||||
statelessGas := interpreter.evm.Accesses.TouchAndChargeContractCreateInit(contractAddress.Bytes()[:])
|
||||
if !tryConsumeGas(&gas, statelessGas) {
|
||||
return nil, ErrExecutionReverted
|
||||
}
|
||||
}
|
||||
if interpreter.evm.chainRules.IsEIP150 {
|
||||
gas -= gas / 64
|
||||
}
|
||||
@ -709,6 +717,14 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]
|
||||
input = scope.Memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64()))
|
||||
gas = scope.Contract.Gas
|
||||
)
|
||||
if interpreter.evm.chainConfig.IsCancun(interpreter.evm.Context.BlockNumber) {
|
||||
codeAndHash := &codeAndHash{code: input}
|
||||
contractAddress := crypto.CreateAddress2(scope.Contract.Address(), salt.Bytes32(), codeAndHash.Hash().Bytes())
|
||||
statelessGas := interpreter.evm.Accesses.TouchAndChargeContractCreateInit(contractAddress.Bytes()[:])
|
||||
if !tryConsumeGas(&gas, statelessGas) {
|
||||
return nil, ErrExecutionReverted
|
||||
}
|
||||
}
|
||||
|
||||
// Apply EIP150
|
||||
gas -= gas / 64
|
||||
|
@ -47,6 +47,9 @@ type StateDB interface {
|
||||
GetState(common.Address, common.Hash) common.Hash
|
||||
SetState(common.Address, common.Hash, common.Hash)
|
||||
|
||||
GetBalanceLittleEndian(address common.Address) []byte
|
||||
GetNonceLittleEndian(address common.Address) []byte
|
||||
|
||||
Suicide(common.Address) bool
|
||||
HasSuicided(common.Address) bool
|
||||
|
||||
|
Reference in New Issue
Block a user