Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Message call witness gas charging #44

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 23 additions & 21 deletions core/chain_makers.go
Original file line number Diff line number Diff line change
Expand Up @@ -328,31 +328,33 @@ func GenerateVerkleChain(config *params.ChainConfig, parent *types.Block, engine
}

// Generate an associated verkle proof
if tr := statedb.GetTrie(); tr.IsVerkle() {
vtr := tr.(*trie.VerkleTrie)
// Generate the proof if we are using a verkle tree
// WORKAROUND: make sure all keys are resolved
// before building the proof. Ultimately, node
// resolution can be done with a prefetcher or
// from GetCommitmentsAlongPath.

keys := statedb.Witness().Keys()
for _, key := range keys {
out, err := vtr.TryGet(key)
/*
if tr := statedb.GetTrie(); tr.IsVerkle() {
vtr := tr.(*trie.VerkleTrie)
// Generate the proof if we are using a verkle tree
// WORKAROUND: make sure all keys are resolved
// before building the proof. Ultimately, node
// resolution can be done with a prefetcher or
// from GetCommitmentsAlongPath.
keys := statedb.Witness().Keys()
for _, key := range keys {
out, err := vtr.TryGet(key)
if err != nil {
panic(err)
}
if len(out) == 0 {
panic(fmt.Sprintf("%x should be present in the tree", key))
}
}
vtr.Hash()
_, err := vtr.ProveAndSerialize(keys, statedb.Witness().KeyVals())
//block.SetVerkleProof(p)
if err != nil {
panic(err)
}
if len(out) == 0 {
panic(fmt.Sprintf("%x should be present in the tree", key))
}
}
vtr.Hash()
_, err := vtr.ProveAndSerialize(keys, statedb.Witness().KeyVals())
//block.SetVerkleProof(p)
if err != nil {
panic(err)
}
}
*/
return block, b.receipts
}
return nil, nil
Expand Down
49 changes: 37 additions & 12 deletions core/state/statedb.go
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,24 @@ func (s *StateDB) GetBalance(addr common.Address) *big.Int {
return common.Big0
}

func (s *StateDB) GetNonceLittleEndian(address common.Address) []byte {
jwasinger marked this conversation as resolved.
Show resolved Hide resolved
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 {
Expand Down Expand Up @@ -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))
}
}
}
Expand Down
58 changes: 39 additions & 19 deletions core/state_transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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.
//
Expand Down Expand Up @@ -304,26 +316,34 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
if 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.Accesses != nil {
var targetBalance, targetNonce, targetCodeSize, targetCodeKeccak, originBalance, originNonce []byte

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 {
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
// 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[:])
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

Expand Down
120 changes: 120 additions & 0 deletions core/types/access_witness.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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[:])
jwasinger marked this conversation as resolved.
Show resolved Hide resolved
}

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) {
Expand Down
Loading