Skip to content

Commit

Permalink
charge witness gas costs for calls, creates
Browse files Browse the repository at this point in the history
  • Loading branch information
jwasinger committed Jan 17, 2022
1 parent 99604b0 commit 15d84a9
Show file tree
Hide file tree
Showing 9 changed files with 391 additions and 78 deletions.
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 {
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[:])
}

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

0 comments on commit 15d84a9

Please sign in to comment.