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
|
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 {
|
func (s *StateDB) GetNonce(addr common.Address) uint64 {
|
||||||
stateObject := s.getStateObject(addr)
|
stateObject := s.getStateObject(addr)
|
||||||
if stateObject != nil {
|
if stateObject != nil {
|
||||||
@ -486,7 +504,8 @@ func (s *StateDB) updateStateObject(obj *stateObject) {
|
|||||||
if err := s.trie.TryUpdateAccount(addr[:], &obj.data); err != nil {
|
if err := s.trie.TryUpdateAccount(addr[:], &obj.data); err != nil {
|
||||||
s.setError(fmt.Errorf("updateStateObject (%x) error: %w", addr[:], err))
|
s.setError(fmt.Errorf("updateStateObject (%x) error: %w", addr[:], err))
|
||||||
}
|
}
|
||||||
if len(obj.code) > 0 && s.trie.IsVerkle() {
|
if s.trie.IsVerkle() {
|
||||||
|
if len(obj.code) > 0 {
|
||||||
cs := make([]byte, 32)
|
cs := make([]byte, 32)
|
||||||
binary.BigEndian.PutUint64(cs, uint64(len(obj.code)))
|
binary.BigEndian.PutUint64(cs, uint64(len(obj.code)))
|
||||||
if err := s.trie.TryUpdate(trieUtils.GetTreeKeyCodeSize(addr[:]), cs); err != nil {
|
if err := s.trie.TryUpdate(trieUtils.GetTreeKeyCodeSize(addr[:]), cs); err != nil {
|
||||||
@ -502,6 +521,12 @@ func (s *StateDB) updateStateObject(obj *stateObject) {
|
|||||||
s.setError(err)
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If state snapshotting is active, cache the data til commit. Note, this
|
// If state snapshotting is active, cache the data til commit. Note, this
|
||||||
|
@ -28,7 +28,6 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
trieUtils "github.com/ethereum/go-ethereum/trie/utils"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var emptyCodeHash = crypto.Keccak256Hash(nil)
|
var emptyCodeHash = crypto.Keccak256Hash(nil)
|
||||||
@ -261,6 +260,19 @@ func (st *StateTransition) preCheck() error {
|
|||||||
return st.buyGas()
|
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
|
// TransitionDb will transition the state by applying the current message and
|
||||||
// returning the evm execution result with following fields.
|
// 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)
|
return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, st.gas, gas)
|
||||||
}
|
}
|
||||||
if st.evm.ChainConfig().IsCancun(st.evm.Context.BlockNumber) {
|
if st.evm.ChainConfig().IsCancun(st.evm.Context.BlockNumber) {
|
||||||
if msg.To() != nil {
|
var targetBalance, targetNonce, targetCodeSize, targetCodeKeccak, originBalance, originNonce []byte
|
||||||
toBalance := trieUtils.GetTreeKeyBalance(msg.To().Bytes())
|
|
||||||
pre := st.state.GetBalance(*msg.To())
|
|
||||||
gas += st.evm.Accesses.TouchAddressAndChargeGas(toBalance, pre.Bytes())
|
|
||||||
|
|
||||||
// NOTE: Nonce also needs to be charged, because it is needed for execution
|
targetAddr := msg.To()
|
||||||
// on the statless side.
|
originAddr := msg.From()
|
||||||
var preTN [8]byte
|
|
||||||
fromNonce := trieUtils.GetTreeKeyNonce(msg.To().Bytes())
|
statelessGasOrigin := st.evm.Accesses.TouchTxOriginAndChargeGas(originAddr.Bytes())
|
||||||
binary.BigEndian.PutUint64(preTN[:], st.state.GetNonce(*msg.To()))
|
if !tryConsumeGas(&st.gas, statelessGasOrigin) {
|
||||||
gas += st.evm.Accesses.TouchAddressAndChargeGas(fromNonce, preTN[:])
|
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
|
st.gas -= gas
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ package types
|
|||||||
import (
|
import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/ethereum/go-ethereum/trie/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AccessWitness lists the locations of the state that are being accessed
|
// 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
|
// TouchAddress adds any missing addr to the witness and returns respectively
|
||||||
// true if the stem or the stub weren't arleady present.
|
// true if the stem or the stub weren't arleady present.
|
||||||
func (aw *AccessWitness) TouchAddress(addr, value []byte) (bool, bool) {
|
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 !evm.StateDB.Exist(addr) {
|
||||||
if !isPrecompile && evm.chainRules.IsEIP158 && value.Sign() == 0 {
|
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
|
// Calling a non existing account, don't do anything, but ping the tracer
|
||||||
if evm.Config.Debug {
|
if evm.Config.Debug {
|
||||||
if evm.depth == 0 {
|
if evm.depth == 0 {
|
||||||
@ -216,6 +220,11 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
|
|||||||
}
|
}
|
||||||
evm.StateDB.CreateAccount(addr)
|
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)
|
evm.Context.Transfer(evm.StateDB, caller.Address(), addr, value)
|
||||||
|
|
||||||
// Capture the tracer start/end events in debug mode
|
// 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.
|
// 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.
|
// The contract is a scoped environment for this execution context only.
|
||||||
code := evm.StateDB.GetCode(addr)
|
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 {
|
if len(code) == 0 {
|
||||||
ret, err = nil, nil // gas is unchanged
|
ret, err = nil, nil // gas is unchanged
|
||||||
} else {
|
} 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
|
addrCopy := addr
|
||||||
// If the account has no code, we can abort here
|
// If the account has no code, we can abort here
|
||||||
// The depth-check is already done, and precompiles handled above
|
// 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 {
|
if p, isPrecompile := evm.precompile(addr); isPrecompile {
|
||||||
ret, gas, err = RunPrecompiledContract(p, input, gas)
|
ret, gas, err = RunPrecompiledContract(p, input, gas)
|
||||||
} else {
|
} 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
|
addrCopy := addr
|
||||||
// Initialise a new contract and set the code that is to be used by the EVM.
|
// 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.
|
// 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 {
|
if p, isPrecompile := evm.precompile(addr); isPrecompile {
|
||||||
ret, gas, err = RunPrecompiledContract(p, input, gas)
|
ret, gas, err = RunPrecompiledContract(p, input, gas)
|
||||||
} else {
|
} 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
|
addrCopy := addr
|
||||||
// Initialise a new contract and make initialise the delegate values
|
// Initialise a new contract and make initialise the delegate values
|
||||||
contract := NewContract(caller, AccountRef(caller.Address()), nil, gas).AsDelegate()
|
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 {
|
if p, isPrecompile := evm.precompile(addr); isPrecompile {
|
||||||
ret, gas, err = RunPrecompiledContract(p, input, gas)
|
ret, gas, err = RunPrecompiledContract(p, input, gas)
|
||||||
} else {
|
} 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
|
// 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'
|
// leak the 'contract' to the outer scope, and make allocation for 'contract'
|
||||||
// even if the actual execution ends on RunPrecompiled above.
|
// 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.
|
// 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) {
|
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
|
// Depth check execution. Fail if we're trying to execute above the
|
||||||
// limit.
|
// limit.
|
||||||
if evm.depth > int(params.CallCreateDepth) {
|
if evm.depth > int(params.CallCreateDepth) {
|
||||||
@ -478,6 +507,14 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
|
|||||||
if evm.chainRules.IsEIP158 {
|
if evm.chainRules.IsEIP158 {
|
||||||
evm.StateDB.SetNonce(address, 1)
|
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)
|
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.
|
// 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.Config.Debug {
|
||||||
if evm.depth == 0 {
|
if evm.depth == 0 {
|
||||||
evm.Config.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
|
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"
|
||||||
"github.com/ethereum/go-ethereum/common/math"
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
trieUtils "github.com/ethereum/go-ethereum/trie/utils"
|
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()
|
transfersValue = !stack.Back(2).IsZero()
|
||||||
address = common.Address(stack.Back(1).Bytes20())
|
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 evm.chainRules.IsEIP158 {
|
||||||
if transfersValue && evm.StateDB.Empty(address) {
|
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 {
|
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
|
||||||
return 0, ErrGasUintOverflow
|
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
|
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 {
|
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
|
||||||
return 0, ErrGasUintOverflow
|
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
|
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 {
|
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
|
||||||
return 0, ErrGasUintOverflow
|
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
|
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 {
|
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
|
||||||
return 0, ErrGasUintOverflow
|
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
|
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()) {
|
if !evm.StateDB.HasSuicided(contract.Address()) {
|
||||||
evm.StateDB.AddRefund(params.SelfdestructRefundGas)
|
evm.StateDB.AddRefund(params.SelfdestructRefundGas)
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ package vm
|
|||||||
import (
|
import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
trieUtils "github.com/ethereum/go-ethereum/trie/utils"
|
trieUtils "github.com/ethereum/go-ethereum/trie/utils"
|
||||||
"github.com/holiman/uint256"
|
"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()))
|
input = scope.Memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64()))
|
||||||
gas = scope.Contract.Gas
|
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 {
|
if interpreter.evm.chainRules.IsEIP150 {
|
||||||
gas -= gas / 64
|
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()))
|
input = scope.Memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64()))
|
||||||
gas = scope.Contract.Gas
|
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
|
// Apply EIP150
|
||||||
gas -= gas / 64
|
gas -= gas / 64
|
||||||
|
@ -47,6 +47,9 @@ type StateDB interface {
|
|||||||
GetState(common.Address, common.Hash) common.Hash
|
GetState(common.Address, common.Hash) common.Hash
|
||||||
SetState(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
|
Suicide(common.Address) bool
|
||||||
HasSuicided(common.Address) bool
|
HasSuicided(common.Address) bool
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user