New access-witness module (#235)

Use plain addresses/slot numbers instead of hashing them in the witness
This commit is contained in:
Ignacio Hagopian 2023-08-29 15:59:06 -03:00 committed by Guillaume Ballet
parent a926f60699
commit a0e1995bc4
13 changed files with 234 additions and 459 deletions

View file

@ -30,6 +30,8 @@ import (
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/trie/utils"
"github.com/holiman/uint256"
) )
// Proof-of-stake protocol constants. // Proof-of-stake protocol constants.
@ -354,7 +356,7 @@ func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types.
state.AddBalance(w.Address, amount) state.AddBalance(w.Address, amount)
// The returned gas is not charged // The returned gas is not charged
state.Witness().TouchAddressOnWriteAndComputeGas(w.Address[:]) state.Witness().TouchAddressOnWriteAndComputeGas(w.Address[:], uint256.Int{}, utils.BalanceLeafKey)
} }
// No block reward which is issued by consensus layer instead. // No block reward which is issued by consensus layer instead.
} }

View file

@ -34,6 +34,7 @@ import (
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/trie/utils" "github.com/ethereum/go-ethereum/trie/utils"
"github.com/holiman/uint256"
"golang.org/x/crypto/sha3" "golang.org/x/crypto/sha3"
) )
@ -568,8 +569,7 @@ func accumulateRewards(config *params.ChainConfig, state *state.StateDB, header
// This should not happen, but it's useful for replay tests // This should not happen, but it's useful for replay tests
if config.IsVerkle(header.Number, header.Time) { if config.IsVerkle(header.Number, header.Time) {
uncleCoinbase := utils.GetTreeKeyBalance(uncle.Coinbase.Bytes()) state.Witness().TouchAddressOnReadAndComputeGas(uncle.Coinbase.Bytes(), uint256.Int{}, utils.BalanceLeafKey)
state.Witness().TouchAddressOnReadAndComputeGas(uncleCoinbase)
} }
state.AddBalance(uncle.Coinbase, r) state.AddBalance(uncle.Coinbase, r)
@ -577,14 +577,10 @@ func accumulateRewards(config *params.ChainConfig, state *state.StateDB, header
reward.Add(reward, r) reward.Add(reward, r)
} }
if config.IsVerkle(header.Number, header.Time) { if config.IsVerkle(header.Number, header.Time) {
coinbase := utils.GetTreeKeyBalance(header.Coinbase.Bytes()) state.Witness().TouchAddressOnReadAndComputeGas(header.Coinbase.Bytes(), uint256.Int{}, utils.BalanceLeafKey)
state.Witness().TouchAddressOnReadAndComputeGas(coinbase) state.Witness().TouchAddressOnReadAndComputeGas(header.Coinbase.Bytes(), uint256.Int{}, utils.VersionLeafKey)
coinbase[31] = utils.VersionLeafKey // mark version state.Witness().TouchAddressOnReadAndComputeGas(header.Coinbase.Bytes(), uint256.Int{}, utils.NonceLeafKey)
state.Witness().TouchAddressOnReadAndComputeGas(coinbase) state.Witness().TouchAddressOnReadAndComputeGas(header.Coinbase.Bytes(), uint256.Int{}, utils.CodeKeccakLeafKey)
coinbase[31] = utils.NonceLeafKey // mark nonce
state.Witness().TouchAddressOnReadAndComputeGas(coinbase)
coinbase[31] = utils.CodeKeccakLeafKey // mark code keccak
state.Witness().TouchAddressOnReadAndComputeGas(coinbase)
} }
state.AddBalance(header.Coinbase, reward) state.AddBalance(header.Coinbase, reward)
} }

View file

@ -20,147 +20,160 @@ 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" "github.com/ethereum/go-ethereum/trie/utils"
"github.com/holiman/uint256"
) )
type VerkleStem [31]byte // mode specifies how a tree location has been accessed
// Mode specifies how a tree location has been accessed
// for the byte value: // for the byte value:
// * the first bit is set if the branch has been edited // * the first bit is set if the branch has been edited
// * the second bit is set if the branch has been read // * the second bit is set if the branch has been read
type Mode byte type mode byte
const ( const (
AccessWitnessReadFlag = Mode(1) AccessWitnessReadFlag = mode(1)
AccessWitnessWriteFlag = Mode(2) AccessWitnessWriteFlag = mode(2)
) )
var zeroTreeIndex uint256.Int
// AccessWitness lists the locations of the state that are being accessed // AccessWitness lists the locations of the state that are being accessed
// during the production of a block. // during the production of a block.
type AccessWitness struct { type AccessWitness struct {
// Branches flags if a given branch has been loaded branches map[branchAccessKey]mode
Branches map[VerkleStem]Mode chunks map[chunkAccessKey]mode
// Chunks contains the initial value of each address pointCache *utils.PointCache
Chunks map[common.Hash]Mode
// InitialValue contains either `nil` if the location
// didn't exist before it was accessed, or the value
// that a location had before the execution of this
// block.
InitialValue map[string][]byte
// Caches which code chunks have been accessed, in order
// to reduce the number of times that GetTreeKeyCodeChunk
// is called.
CodeLocations map[string]map[uint64]struct{}
statedb *StateDB
} }
func NewAccessWitness(statedb *StateDB) *AccessWitness { func NewAccessWitness(pointCache *utils.PointCache) *AccessWitness {
return &AccessWitness{ return &AccessWitness{
Branches: make(map[VerkleStem]Mode), branches: make(map[branchAccessKey]mode),
Chunks: make(map[common.Hash]Mode), chunks: make(map[chunkAccessKey]mode),
InitialValue: make(map[string][]byte), pointCache: pointCache,
CodeLocations: make(map[string]map[uint64]struct{}),
statedb: statedb,
} }
} }
func (aw *AccessWitness) HasCodeChunk(addr []byte, chunknr uint64) bool { // Merge is used to merge the witness that got generated during the execution
if locs, ok := aw.CodeLocations[string(addr)]; ok { // of a tx, with the accumulation of witnesses that were generated during the
if _, ok = locs[chunknr]; ok { // execution of all the txs preceding this one in a given block.
return true func (aw *AccessWitness) Merge(other *AccessWitness) {
for k := range other.branches {
aw.branches[k] |= other.branches[k]
} }
for k, chunk := range other.chunks {
aw.chunks[k] |= chunk
} }
return false
} }
// SetCodeLeafValue does the same thing as SetLeafValue, but for code chunks. It // Key returns, predictably, the list of keys that were touched during the
// maintains a cache of which (address, chunk) were calculated, in order to avoid // buildup of the access witness.
// calling GetTreeKey more than once per chunk. func (aw *AccessWitness) Keys() [][]byte {
func (aw *AccessWitness) SetCachedCodeChunk(addr []byte, chunknr uint64) { // TODO: consider if parallelizing this is worth it, probably depending on len(aw.chunks).
if locs, ok := aw.CodeLocations[string(addr)]; ok { keys := make([][]byte, 0, len(aw.chunks))
if _, ok = locs[chunknr]; ok { for chunk := range aw.chunks {
return basePoint := aw.pointCache.GetTreeKeyHeader(chunk.addr[:])
key := utils.GetTreeKeyWithEvaluatedAddess(basePoint, &chunk.treeIndex, chunk.leafKey)
keys = append(keys, key)
} }
} else { return keys
aw.CodeLocations[string(addr)] = map[uint64]struct{}{}
}
aw.CodeLocations[string(addr)][chunknr] = struct{}{}
} }
func (aw *AccessWitness) touchAddressOnWrite(addr []byte) (bool, bool, bool) { func (aw *AccessWitness) Copy() *AccessWitness {
var stem VerkleStem naw := &AccessWitness{
var stemWrite, chunkWrite, chunkFill bool branches: make(map[branchAccessKey]mode),
copy(stem[:], addr[:31]) chunks: make(map[chunkAccessKey]mode),
pointCache: aw.pointCache,
// NOTE: stem, selector access flags already exist in their
// respective maps because this function is called at the end of
// processing a read access event
if (aw.Branches[stem] & AccessWitnessWriteFlag) == 0 {
stemWrite = true
aw.Branches[stem] |= AccessWitnessWriteFlag
} }
naw.Merge(aw)
chunkValue := aw.Chunks[common.BytesToHash(addr)] return naw
// if chunkValue.mode XOR AccessWitnessWriteFlag
if ((chunkValue & AccessWitnessWriteFlag) == 0) && ((chunkValue | AccessWitnessWriteFlag) != 0) {
chunkWrite = true
chunkValue |= AccessWitnessWriteFlag
aw.Chunks[common.BytesToHash(addr)] = chunkValue
}
// TODO charge chunk filling costs if the leaf was previously empty in the state
/*
if chunkWrite {
if _, err := verkleDb.TryGet(addr); err != nil {
chunkFill = true
}
}
*/
return stemWrite, chunkWrite, chunkFill
} }
// TouchAddress adds any missing addr to the witness and returns respectively func (aw *AccessWitness) TouchAndChargeProofOfAbsence(addr []byte) uint64 {
// true if the stem or the stub weren't arleady present.
func (aw *AccessWitness) touchAddress(addr []byte, isWrite bool) (bool, bool, bool, bool, bool) {
var (
stem [31]byte
stemRead, selectorRead bool
stemWrite, selectorWrite, chunkFill bool
)
copy(stem[:], addr[:31])
// Check for the presence of the stem
if _, hasStem := aw.Branches[stem]; !hasStem {
stemRead = true
aw.Branches[stem] = AccessWitnessReadFlag
}
// Check for the presence of the leaf selector
if _, hasSelector := aw.Chunks[common.BytesToHash(addr)]; !hasSelector {
selectorRead = true
aw.Chunks[common.BytesToHash(addr)] = AccessWitnessReadFlag
}
if isWrite {
stemWrite, selectorWrite, chunkFill = aw.touchAddressOnWrite(addr)
}
return stemRead, selectorRead, stemWrite, selectorWrite, chunkFill
}
func (aw *AccessWitness) touchAddressAndChargeGas(addr []byte, isWrite bool) uint64 {
var gas uint64 var gas uint64
gas += aw.TouchAddressOnReadAndComputeGas(addr, zeroTreeIndex, utils.VersionLeafKey)
gas += aw.TouchAddressOnReadAndComputeGas(addr, zeroTreeIndex, utils.BalanceLeafKey)
gas += aw.TouchAddressOnReadAndComputeGas(addr, zeroTreeIndex, utils.CodeSizeLeafKey)
gas += aw.TouchAddressOnReadAndComputeGas(addr, zeroTreeIndex, utils.CodeKeccakLeafKey)
gas += aw.TouchAddressOnReadAndComputeGas(addr, zeroTreeIndex, utils.NonceLeafKey)
return gas
}
stemRead, selectorRead, stemWrite, selectorWrite, selectorFill := aw.touchAddress(addr, isWrite) func (aw *AccessWitness) TouchAndChargeMessageCall(addr []byte) uint64 {
var gas uint64
gas += aw.TouchAddressOnReadAndComputeGas(addr, zeroTreeIndex, utils.VersionLeafKey)
gas += aw.TouchAddressOnReadAndComputeGas(addr, zeroTreeIndex, utils.CodeSizeLeafKey)
return gas
}
func (aw *AccessWitness) TouchAndChargeValueTransfer(callerAddr, targetAddr []byte) uint64 {
var gas uint64
gas += aw.TouchAddressOnWriteAndComputeGas(callerAddr, zeroTreeIndex, utils.BalanceLeafKey)
gas += aw.TouchAddressOnWriteAndComputeGas(targetAddr, zeroTreeIndex, utils.BalanceLeafKey)
return gas
}
// TouchAndChargeContractCreateInit charges access costs to initiate
// a contract creation
func (aw *AccessWitness) TouchAndChargeContractCreateInit(addr []byte, createSendsValue bool) uint64 {
var gas uint64
gas += aw.TouchAddressOnWriteAndComputeGas(addr, zeroTreeIndex, utils.VersionLeafKey)
gas += aw.TouchAddressOnWriteAndComputeGas(addr, zeroTreeIndex, utils.NonceLeafKey)
gas += aw.TouchAddressOnWriteAndComputeGas(addr, zeroTreeIndex, utils.CodeKeccakLeafKey)
if createSendsValue {
gas += aw.TouchAddressOnWriteAndComputeGas(addr, zeroTreeIndex, utils.BalanceLeafKey)
}
return gas
}
// 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) uint64 {
var gas uint64
gas += aw.TouchAddressOnWriteAndComputeGas(addr, zeroTreeIndex, utils.VersionLeafKey)
gas += aw.TouchAddressOnWriteAndComputeGas(addr, zeroTreeIndex, utils.BalanceLeafKey)
gas += aw.TouchAddressOnWriteAndComputeGas(addr, zeroTreeIndex, utils.CodeSizeLeafKey)
gas += aw.TouchAddressOnWriteAndComputeGas(addr, zeroTreeIndex, utils.CodeKeccakLeafKey)
gas += aw.TouchAddressOnWriteAndComputeGas(addr, zeroTreeIndex, utils.NonceLeafKey)
return gas
}
func (aw *AccessWitness) TouchTxOriginAndComputeGas(originAddr []byte) uint64 {
var gas uint64
gas += aw.TouchAddressOnReadAndComputeGas(originAddr, zeroTreeIndex, utils.VersionLeafKey)
gas += aw.TouchAddressOnReadAndComputeGas(originAddr, zeroTreeIndex, utils.CodeSizeLeafKey)
gas += aw.TouchAddressOnReadAndComputeGas(originAddr, zeroTreeIndex, utils.CodeKeccakLeafKey)
gas += aw.TouchAddressOnWriteAndComputeGas(originAddr, zeroTreeIndex, utils.NonceLeafKey)
gas += aw.TouchAddressOnWriteAndComputeGas(originAddr, zeroTreeIndex, utils.BalanceLeafKey)
return gas
}
func (aw *AccessWitness) TouchTxExistingAndComputeGas(targetAddr []byte, sendsValue bool) uint64 {
var gas uint64
gas += aw.TouchAddressOnReadAndComputeGas(targetAddr, zeroTreeIndex, utils.VersionLeafKey)
gas += aw.TouchAddressOnReadAndComputeGas(targetAddr, zeroTreeIndex, utils.CodeSizeLeafKey)
gas += aw.TouchAddressOnReadAndComputeGas(targetAddr, zeroTreeIndex, utils.CodeKeccakLeafKey)
gas += aw.TouchAddressOnReadAndComputeGas(targetAddr, zeroTreeIndex, utils.NonceLeafKey)
if sendsValue {
gas += aw.TouchAddressOnWriteAndComputeGas(targetAddr, zeroTreeIndex, utils.BalanceLeafKey)
} else {
gas += aw.TouchAddressOnReadAndComputeGas(targetAddr, zeroTreeIndex, utils.BalanceLeafKey)
}
return gas
}
func (aw *AccessWitness) TouchAddressOnWriteAndComputeGas(addr []byte, treeIndex uint256.Int, subIndex byte) uint64 {
return aw.touchAddressAndChargeGas(addr, treeIndex, subIndex, true)
}
func (aw *AccessWitness) TouchAddressOnReadAndComputeGas(addr []byte, treeIndex uint256.Int, subIndex byte) uint64 {
return aw.touchAddressAndChargeGas(addr, treeIndex, subIndex, false)
}
func (aw *AccessWitness) touchAddressAndChargeGas(addr []byte, treeIndex uint256.Int, subIndex byte, isWrite bool) uint64 {
stemRead, selectorRead, stemWrite, selectorWrite, selectorFill := aw.touchAddress(addr, treeIndex, subIndex, isWrite)
var gas uint64
if stemRead { if stemRead {
gas += params.WitnessBranchReadCost gas += params.WitnessBranchReadCost
} }
@ -180,231 +193,62 @@ func (aw *AccessWitness) touchAddressAndChargeGas(addr []byte, isWrite bool) uin
return gas return gas
} }
func (aw *AccessWitness) TouchAddressOnWriteAndComputeGas(addr []byte) uint64 { // touchAddress adds any missing access event to the witness.
return aw.touchAddressAndChargeGas(addr, true) func (aw *AccessWitness) touchAddress(addr []byte, treeIndex uint256.Int, subIndex byte, isWrite bool) (bool, bool, bool, bool, bool) {
} branchKey := newBranchAccessKey(addr, treeIndex)
chunkKey := newChunkAccessKey(branchKey, subIndex)
func (aw *AccessWitness) TouchAddressOnReadAndComputeGas(addr []byte) uint64 { // Read access.
return aw.touchAddressAndChargeGas(addr, false) var branchRead, chunkRead bool
} if _, hasStem := aw.branches[branchKey]; !hasStem {
branchRead = true
// Merge is used to merge the witness that got generated during the execution aw.branches[branchKey] = AccessWitnessReadFlag
// of a tx, with the accumulation of witnesses that were generated during the
// execution of all the txs preceding this one in a given block.
func (aw *AccessWitness) Merge(other *AccessWitness) {
for k := range other.Branches {
if _, ok := aw.Branches[k]; !ok {
aw.Branches[k] = other.Branches[k]
} }
if _, hasSelector := aw.chunks[chunkKey]; !hasSelector {
chunkRead = true
aw.chunks[chunkKey] = AccessWitnessReadFlag
} }
for k, chunk := range other.Chunks { // Write access.
if _, ok := aw.Chunks[k]; !ok { var branchWrite, chunkWrite, chunkFill bool
aw.Chunks[k] = chunk if isWrite {
} if (aw.branches[branchKey] & AccessWitnessWriteFlag) == 0 {
branchWrite = true
aw.branches[branchKey] |= AccessWitnessWriteFlag
} }
for k, v := range other.InitialValue { chunkValue := aw.chunks[chunkKey]
if _, ok := aw.InitialValue[k]; !ok { if (chunkValue & AccessWitnessWriteFlag) == 0 {
aw.InitialValue[k] = v chunkWrite = true
} aw.chunks[chunkKey] |= AccessWitnessWriteFlag
} }
// TODO see if merging improves performance // TODO: charge chunk filling costs if the leaf was previously empty in the state
//for k, v := range other.addrToPoint {
//if _, ok := aw.addrToPoint[k]; !ok {
//aw.addrToPoint[k] = v
//}
//}
}
// Key returns, predictably, the list of keys that were touched during the
// buildup of the access witness.
func (aw *AccessWitness) Keys() [][]byte {
keys := make([][]byte, 0, len(aw.Chunks))
for key := range aw.Chunks {
var k [32]byte
copy(k[:], key[:])
keys = append(keys, k[:])
}
return keys
}
func (aw *AccessWitness) KeyVals() map[string][]byte {
result := make(map[string][]byte)
for k, v := range aw.InitialValue {
result[k] = v
}
return result
}
func (aw *AccessWitness) Copy() *AccessWitness {
naw := &AccessWitness{
Branches: make(map[VerkleStem]Mode),
Chunks: make(map[common.Hash]Mode),
InitialValue: make(map[string][]byte),
} }
naw.Merge(aw) return branchRead, chunkRead, branchWrite, chunkWrite, chunkFill
return naw
} }
func (aw *AccessWitness) GetTreeKeyVersionCached(addr []byte) []byte { type branchAccessKey struct {
return aw.statedb.db.(*cachingDB).addrToPoint.GetTreeKeyVersionCached(addr) addr common.Address
treeIndex uint256.Int
} }
func (aw *AccessWitness) TouchAndChargeProofOfAbsence(addr []byte) uint64 { func newBranchAccessKey(addr []byte, treeIndex uint256.Int) branchAccessKey {
var ( var sk branchAccessKey
balancekey, cskey, ckkey, noncekey [32]byte copy(sk.addr[:], addr)
gas uint64 sk.treeIndex = treeIndex
) return sk
// Only evaluate the polynomial once
versionkey := aw.GetTreeKeyVersionCached(addr[:])
copy(balancekey[:], versionkey)
balancekey[31] = utils.BalanceLeafKey
copy(noncekey[:], versionkey)
noncekey[31] = utils.NonceLeafKey
copy(cskey[:], versionkey)
cskey[31] = utils.CodeSizeLeafKey
copy(ckkey[:], versionkey)
ckkey[31] = utils.CodeKeccakLeafKey
gas += aw.TouchAddressOnReadAndComputeGas(versionkey)
gas += aw.TouchAddressOnReadAndComputeGas(balancekey[:])
gas += aw.TouchAddressOnReadAndComputeGas(cskey[:])
gas += aw.TouchAddressOnReadAndComputeGas(ckkey[:])
gas += aw.TouchAddressOnReadAndComputeGas(noncekey[:])
return gas
} }
func (aw *AccessWitness) TouchAndChargeMessageCall(addr []byte) uint64 { type chunkAccessKey struct {
var ( branchAccessKey
gas uint64 leafKey byte
cskey [32]byte
)
// Only evaluate the polynomial once
versionkey := aw.GetTreeKeyVersionCached(addr[:])
copy(cskey[:], versionkey)
cskey[31] = utils.CodeSizeLeafKey
gas += aw.TouchAddressOnReadAndComputeGas(versionkey)
gas += aw.TouchAddressOnReadAndComputeGas(cskey[:])
return gas
} }
func (aw *AccessWitness) TouchAndChargeValueTransfer(callerAddr, targetAddr []byte) uint64 { func newChunkAccessKey(branchKey branchAccessKey, leafKey byte) chunkAccessKey {
var gas uint64 var lk chunkAccessKey
gas += aw.TouchAddressOnWriteAndComputeGas(utils.GetTreeKeyBalance(callerAddr[:])) lk.branchAccessKey = branchKey
gas += aw.TouchAddressOnWriteAndComputeGas(utils.GetTreeKeyBalance(targetAddr[:])) lk.leafKey = leafKey
return gas return lk
}
// TouchAndChargeContractCreateInit charges access costs to initiate
// a contract creation
func (aw *AccessWitness) TouchAndChargeContractCreateInit(addr []byte, createSendsValue bool) uint64 {
var (
balancekey, ckkey, noncekey [32]byte
gas uint64
)
// Only evaluate the polynomial once
versionkey := aw.GetTreeKeyVersionCached(addr[:])
copy(balancekey[:], versionkey)
balancekey[31] = utils.BalanceLeafKey
copy(noncekey[:], versionkey)
noncekey[31] = utils.NonceLeafKey
copy(ckkey[:], versionkey)
ckkey[31] = utils.CodeKeccakLeafKey
gas += aw.TouchAddressOnWriteAndComputeGas(versionkey)
gas += aw.TouchAddressOnWriteAndComputeGas(noncekey[:])
if createSendsValue {
gas += aw.TouchAddressOnWriteAndComputeGas(balancekey[:])
}
gas += aw.TouchAddressOnWriteAndComputeGas(ckkey[:])
return gas
}
// 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 (
balancekey, cskey, ckkey, noncekey [32]byte
gas uint64
)
// Only evaluate the polynomial once
versionkey := aw.GetTreeKeyVersionCached(addr[:])
copy(balancekey[:], versionkey)
balancekey[31] = utils.BalanceLeafKey
copy(noncekey[:], versionkey)
noncekey[31] = utils.NonceLeafKey
copy(cskey[:], versionkey)
cskey[31] = utils.CodeSizeLeafKey
copy(ckkey[:], versionkey)
ckkey[31] = utils.CodeKeccakLeafKey
gas += aw.TouchAddressOnWriteAndComputeGas(versionkey)
gas += aw.TouchAddressOnWriteAndComputeGas(balancekey[:])
gas += aw.TouchAddressOnWriteAndComputeGas(cskey[:])
gas += aw.TouchAddressOnWriteAndComputeGas(ckkey[:])
gas += aw.TouchAddressOnWriteAndComputeGas(noncekey[:])
return gas
}
func (aw *AccessWitness) TouchTxOriginAndComputeGas(originAddr []byte) uint64 {
var (
balancekey, cskey, ckkey, noncekey [32]byte
gas uint64
)
// Only evaluate the polynomial once
versionkey := aw.GetTreeKeyVersionCached(originAddr[:])
copy(balancekey[:], versionkey)
balancekey[31] = utils.BalanceLeafKey
copy(noncekey[:], versionkey)
noncekey[31] = utils.NonceLeafKey
copy(cskey[:], versionkey)
cskey[31] = utils.CodeSizeLeafKey
copy(ckkey[:], versionkey)
ckkey[31] = utils.CodeKeccakLeafKey
gas += aw.TouchAddressOnReadAndComputeGas(versionkey)
gas += aw.TouchAddressOnReadAndComputeGas(cskey[:])
gas += aw.TouchAddressOnReadAndComputeGas(ckkey[:])
gas += aw.TouchAddressOnWriteAndComputeGas(noncekey[:])
gas += aw.TouchAddressOnWriteAndComputeGas(balancekey[:])
return gas
}
func (aw *AccessWitness) TouchTxExistingAndComputeGas(targetAddr []byte, sendsValue bool) uint64 {
var (
balancekey, cskey, ckkey, noncekey [32]byte
gas uint64
)
// Only evaluate the polynomial once
versionkey := aw.GetTreeKeyVersionCached(targetAddr[:])
copy(balancekey[:], versionkey)
balancekey[31] = utils.BalanceLeafKey
copy(noncekey[:], versionkey)
noncekey[31] = utils.NonceLeafKey
copy(cskey[:], versionkey)
cskey[31] = utils.CodeSizeLeafKey
copy(ckkey[:], versionkey)
ckkey[31] = utils.CodeKeccakLeafKey
gas += aw.TouchAddressOnReadAndComputeGas(versionkey)
gas += aw.TouchAddressOnReadAndComputeGas(cskey[:])
gas += aw.TouchAddressOnReadAndComputeGas(ckkey[:])
gas += aw.TouchAddressOnReadAndComputeGas(noncekey[:])
gas += aw.TouchAddressOnReadAndComputeGas(balancekey[:])
if sendsValue {
gas += aw.TouchAddressOnWriteAndComputeGas(balancekey[:])
}
return gas
} }

View file

@ -174,7 +174,7 @@ func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error)
hasher: crypto.NewKeccakState(), hasher: crypto.NewKeccakState(),
} }
if tr.IsVerkle() { if tr.IsVerkle() {
sdb.witness = NewAccessWitness(sdb) sdb.witness = sdb.NewAccessWitness()
// if sdb.snaps == nil { // if sdb.snaps == nil {
// snapconfig := snapshot.Config{ // snapconfig := snapshot.Config{
// CacheSize: 256, // CacheSize: 256,
@ -206,9 +206,13 @@ func (s *StateDB) Snaps() *snapshot.Tree {
return s.snaps return s.snaps
} }
func (s *StateDB) NewAccessWitness() *AccessWitness {
return NewAccessWitness(s.db.(*cachingDB).addrToPoint)
}
func (s *StateDB) Witness() *AccessWitness { func (s *StateDB) Witness() *AccessWitness {
if s.witness == nil { if s.witness == nil {
s.witness = NewAccessWitness(s) s.witness = s.NewAccessWitness()
} }
return s.witness return s.witness
} }

View file

@ -320,7 +320,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
func applyTransaction(msg *Message, config *params.ChainConfig, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (*types.Receipt, error) { func applyTransaction(msg *Message, config *params.ChainConfig, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (*types.Receipt, error) {
// Create a new context to be used in the EVM environment. // Create a new context to be used in the EVM environment.
txContext := NewEVMTxContext(msg) txContext := NewEVMTxContext(msg)
txContext.Accesses = state.NewAccessWitness(statedb) txContext.Accesses = statedb.NewAccessWitness()
evm.Reset(txContext, statedb) evm.Reset(txContext, statedb)
// Apply the transaction to the current state (included in the env). // Apply the transaction to the current state (included in the env).

View file

@ -20,9 +20,6 @@ import (
"math/big" "math/big"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/trie/utils"
"github.com/gballet/go-verkle"
"github.com/holiman/uint256" "github.com/holiman/uint256"
) )
@ -52,13 +49,11 @@ type Contract struct {
CallerAddress common.Address CallerAddress common.Address
caller ContractRef caller ContractRef
self ContractRef self ContractRef
addressPoint *verkle.Point
jumpdests map[common.Hash]bitvec // Aggregated result of JUMPDEST analysis. jumpdests map[common.Hash]bitvec // Aggregated result of JUMPDEST analysis.
analysis bitvec // Locally cached result of JUMPDEST analysis analysis bitvec // Locally cached result of JUMPDEST analysis
Code []byte Code []byte
Chunks trie.ChunkedCode
CodeHash common.Hash CodeHash common.Hash
CodeAddr *common.Address CodeAddr *common.Address
Input []byte Input []byte
@ -180,14 +175,6 @@ func (c *Contract) Address() common.Address {
return c.self.Address() return c.self.Address()
} }
func (c *Contract) AddressPoint() *verkle.Point {
if c.addressPoint == nil {
c.addressPoint = utils.EvaluateAddressPoint(c.Address().Bytes())
}
return c.addressPoint
}
// Value returns the contract's value (sent to it from it's caller) // Value returns the contract's value (sent to it from it's caller)
func (c *Contract) Value() *big.Int { func (c *Contract) Value() *big.Int {
return c.value return c.value

View file

@ -138,7 +138,7 @@ func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, chainConfig
chainRules: chainConfig.Rules(blockCtx.BlockNumber, blockCtx.Random != nil, blockCtx.Time), chainRules: chainConfig.Rules(blockCtx.BlockNumber, blockCtx.Random != nil, blockCtx.Time),
} }
if txCtx.Accesses == nil && chainConfig.IsVerkle(blockCtx.BlockNumber, blockCtx.Time) { if txCtx.Accesses == nil && chainConfig.IsVerkle(blockCtx.BlockNumber, blockCtx.Time) {
txCtx.Accesses = state.NewAccessWitness(evm.StateDB.(*state.StateDB)) txCtx.Accesses = evm.StateDB.(*state.StateDB).NewAccessWitness()
} }
evm.interpreter = NewEVMInterpreter(evm) evm.interpreter = NewEVMInterpreter(evm)
return evm return evm
@ -148,7 +148,7 @@ func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, chainConfig
// This is not threadsafe and should only be done very cautiously. // This is not threadsafe and should only be done very cautiously.
func (evm *EVM) Reset(txCtx TxContext, statedb StateDB) { func (evm *EVM) Reset(txCtx TxContext, statedb StateDB) {
if txCtx.Accesses == nil && evm.chainRules.IsVerkle { if txCtx.Accesses == nil && evm.chainRules.IsVerkle {
txCtx.Accesses = state.NewAccessWitness(evm.StateDB.(*state.StateDB)) txCtx.Accesses = evm.StateDB.(*state.StateDB).NewAccessWitness()
} }
evm.TxContext = txCtx evm.TxContext = txCtx
evm.StateDB = statedb evm.StateDB = statedb
@ -530,7 +530,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
} }
if err == nil && evm.chainRules.IsVerkle { if err == nil && evm.chainRules.IsVerkle {
if !contract.UseGas(evm.Accesses.TouchAndChargeContractCreateCompleted(address.Bytes()[:], value.Sign() != 0)) { if !contract.UseGas(evm.Accesses.TouchAndChargeContractCreateCompleted(address.Bytes()[:])) {
evm.StateDB.RevertToSnapshot(snapshot) evm.StateDB.RevertToSnapshot(snapshot)
err = ErrOutOfGas err = ErrOutOfGas
} }

View file

@ -24,6 +24,7 @@ import (
"github.com/ethereum/go-ethereum/log" "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"
"github.com/holiman/uint256"
) )
// memoryGasCost calculates the quadratic gas for memory expansion. It does so // memoryGasCost calculates the quadratic gas for memory expansion. It does so
@ -101,8 +102,7 @@ func gasExtCodeSize(evm *EVM, contract *Contract, stack *Stack, mem *Memory, mem
usedGas := uint64(0) usedGas := uint64(0)
slot := stack.Back(0) slot := stack.Back(0)
if evm.chainRules.IsVerkle { if evm.chainRules.IsVerkle {
index := trieUtils.GetTreeKeyCodeSize(slot.Bytes()) usedGas += evm.TxContext.Accesses.TouchAddressOnReadAndComputeGas(slot.Bytes(), uint256.Int{}, trieUtils.CodeSizeLeafKey)
usedGas += evm.TxContext.Accesses.TouchAddressOnReadAndComputeGas(index)
} }
return usedGas, nil return usedGas, nil
@ -113,8 +113,8 @@ func gasSLoad(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySiz
if evm.chainRules.IsVerkle { if evm.chainRules.IsVerkle {
where := stack.Back(0) where := stack.Back(0)
index := trieUtils.GetTreeKeyStorageSlotWithEvaluatedAddress(contract.AddressPoint(), where.Bytes()) treeIndex, subIndex := trieUtils.GetTreeKeyStorageSlotTreeIndexes(where.Bytes())
usedGas += evm.Accesses.TouchAddressOnReadAndComputeGas(index) usedGas += evm.Accesses.TouchAddressOnReadAndComputeGas(contract.Address().Bytes(), *treeIndex, subIndex)
} }
return usedGas, nil return usedGas, nil

View file

@ -23,7 +23,6 @@ import (
"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/crypto"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie"
trieUtils "github.com/ethereum/go-ethereum/trie/utils" trieUtils "github.com/ethereum/go-ethereum/trie/utils"
"github.com/holiman/uint256" "github.com/holiman/uint256"
) )
@ -347,8 +346,7 @@ func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
slot := scope.Stack.peek() slot := scope.Stack.peek()
cs := uint64(interpreter.evm.StateDB.GetCodeSize(slot.Bytes20())) cs := uint64(interpreter.evm.StateDB.GetCodeSize(slot.Bytes20()))
if interpreter.evm.chainRules.IsVerkle { if interpreter.evm.chainRules.IsVerkle {
index := trieUtils.GetTreeKeyCodeSize(slot.Bytes()) statelessGas := interpreter.evm.Accesses.TouchAddressOnReadAndComputeGas(slot.Bytes(), uint256.Int{}, trieUtils.CodeSizeLeafKey)
statelessGas := interpreter.evm.Accesses.TouchAddressOnReadAndComputeGas(index)
scope.Contract.UseGas(statelessGas) scope.Contract.UseGas(statelessGas)
} }
slot.SetUint64(cs) slot.SetUint64(cs)
@ -373,80 +371,43 @@ func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
uint64CodeOffset = 0xffffffffffffffff uint64CodeOffset = 0xffffffffffffffff
} }
contractAddr := scope.Contract.Address()
paddedCodeCopy, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(scope.Contract.Code, uint64CodeOffset, length.Uint64()) paddedCodeCopy, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(scope.Contract.Code, uint64CodeOffset, length.Uint64())
if interpreter.evm.chainRules.IsVerkle { if interpreter.evm.chainRules.IsVerkle {
scope.Contract.UseGas(touchEachChunksOnReadAndChargeGas(copyOffset, nonPaddedCopyLength, scope.Contract, scope.Contract.Code, interpreter.evm.Accesses, scope.Contract.IsDeployment)) scope.Contract.UseGas(touchCodeChunksRangeOnReadAndChargeGas(contractAddr[:], copyOffset, nonPaddedCopyLength, uint64(len(scope.Contract.Code)), interpreter.evm.Accesses))
} }
scope.Memory.Set(memOffset.Uint64(), uint64(len(paddedCodeCopy)), paddedCodeCopy) scope.Memory.Set(memOffset.Uint64(), uint64(len(paddedCodeCopy)), paddedCodeCopy)
return nil, nil return nil, nil
} }
// touchChunkOnReadAndChargeGas is a helper function to touch every chunk in a code range and charge witness gas costs // touchCodeChunksRangeOnReadAndChargeGas is a helper function to touch every chunk in a code range and charge witness gas costs
func touchChunkOnReadAndChargeGas(chunks trie.ChunkedCode, offset uint64, evals [][]byte, code []byte, accesses *state.AccessWitness, deployment bool) uint64 { func touchCodeChunksRangeOnReadAndChargeGas(contractAddr []byte, startPC, size uint64, codeLen uint64, accesses *state.AccessWitness) uint64 {
// note that in the case where the executed code is outside the range of
// the contract code but touches the last leaf with contract code in it,
// we don't include the last leaf of code in the AccessWitness. The
// reason that we do not need the last leaf is the account's code size
// is already in the AccessWitness so a stateless verifier can see that
// the code from the last leaf is not needed.
if code != nil && offset > uint64(len(code)) {
return 0
}
var (
chunknr = offset / 31
statelessGasCharged uint64
)
// Build the chunk address from the evaluated address of its whole group
var index [32]byte
copy(index[:], evals[chunknr/256])
index[31] = byte((128 + chunknr) % 256)
var overflow bool
statelessGasCharged, overflow = math.SafeAdd(statelessGasCharged, accesses.TouchAddressOnReadAndComputeGas(index[:]))
if overflow {
panic("overflow when adding gas")
}
return statelessGasCharged
}
// touchEachChunksOnReadAndChargeGas is a helper function to touch every chunk in a code range and charge witness gas costs
func touchEachChunksOnReadAndChargeGas(offset, size uint64, contract *Contract, code []byte, accesses *state.AccessWitness, deployment bool) uint64 {
// note that in the case where the copied code is outside the range of the // note that in the case where the copied code is outside the range of the
// contract code but touches the last leaf with contract code in it, // contract code but touches the last leaf with contract code in it,
// we don't include the last leaf of code in the AccessWitness. The // we don't include the last leaf of code in the AccessWitness. The
// reason that we do not need the last leaf is the account's code size // reason that we do not need the last leaf is the account's code size
// is already in the AccessWitness so a stateless verifier can see that // is already in the AccessWitness so a stateless verifier can see that
// the code from the last leaf is not needed. // the code from the last leaf is not needed.
if len(code) == 0 && size == 0 || offset > uint64(len(code)) { if (codeLen == 0 && size == 0) || startPC > codeLen {
return 0 return 0
} }
var (
statelessGasCharged uint64 // endPC is the last PC that must be touched.
endOffset uint64 endPC := startPC + size - 1
) if startPC+size > codeLen {
if code != nil && offset+size > uint64(len(code)) { endPC = codeLen
endOffset = uint64(len(code))
} else {
endOffset = offset + size
} }
// endOffset - 1 since if the end offset is aligned on a chunk boundary, var statelessGasCharged uint64
// the last chunk should not be included. for chunkNumber := startPC / 31; chunkNumber <= endPC/31; chunkNumber++ {
for i := offset / 31; i <= (endOffset-1)/31; i++ { treeIndex := *uint256.NewInt((chunkNumber + 128) / 256)
// only charge for+cache the chunk if it isn't already present subIndex := byte((chunkNumber + 128) % 256)
if !accesses.HasCodeChunk(contract.Address().Bytes(), i) { gas := accesses.TouchAddressOnReadAndComputeGas(contractAddr, treeIndex, subIndex)
index := trieUtils.GetTreeKeyCodeChunkWithEvaluatedAddress(contract.AddressPoint(), uint256.NewInt(i))
var overflow bool var overflow bool
statelessGasCharged, overflow = math.SafeAdd(statelessGasCharged, accesses.TouchAddressOnReadAndComputeGas(index)) statelessGasCharged, overflow = math.SafeAdd(statelessGasCharged, gas)
if overflow { if overflow {
panic("overflow when adding gas") panic("overflow when adding gas")
} }
accesses.SetCachedCodeChunk(contract.Address().Bytes(), i)
}
} }
return statelessGasCharged return statelessGasCharged
@ -469,11 +430,11 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
code := interpreter.evm.StateDB.GetCode(addr) code := interpreter.evm.StateDB.GetCode(addr)
contract := &Contract{ contract := &Contract{
Code: code, Code: code,
Chunks: trie.ChunkedCode(code),
self: AccountRef(addr), self: AccountRef(addr),
} }
paddedCodeCopy, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(code, uint64CodeOffset, length.Uint64()) paddedCodeCopy, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(code, uint64CodeOffset, length.Uint64())
touchEachChunksOnReadAndChargeGas(copyOffset, nonPaddedCopyLength, contract, code, interpreter.evm.Accesses, false) gas := touchCodeChunksRangeOnReadAndChargeGas(addr[:], copyOffset, nonPaddedCopyLength, uint64(len(contract.Code)), interpreter.evm.Accesses)
scope.Contract.UseGas(gas)
scope.Memory.Set(memOffset.Uint64(), length.Uint64(), paddedCodeCopy) scope.Memory.Set(memOffset.Uint64(), length.Uint64(), paddedCodeCopy)
} else { } else {
codeCopy := getData(interpreter.evm.StateDB.GetCode(addr), uint64CodeOffset, length.Uint64()) codeCopy := getData(interpreter.evm.StateDB.GetCode(addr), uint64CodeOffset, length.Uint64())
@ -1001,7 +962,8 @@ func opPush1(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by
if interpreter.evm.chainRules.IsVerkle && *pc%31 == 0 { if interpreter.evm.chainRules.IsVerkle && *pc%31 == 0 {
// touch next chunk if PUSH1 is at the boundary. if so, *pc has // touch next chunk if PUSH1 is at the boundary. if so, *pc has
// advanced past this boundary. // advanced past this boundary.
statelessGas := touchEachChunksOnReadAndChargeGas(*pc+1, uint64(1), scope.Contract, scope.Contract.Code, interpreter.evm.Accesses, scope.Contract.IsDeployment) contractAddr := scope.Contract.Address()
statelessGas := touchCodeChunksRangeOnReadAndChargeGas(contractAddr[:], *pc+1, uint64(1), uint64(len(scope.Contract.Code)), interpreter.evm.Accesses)
scope.Contract.UseGas(statelessGas) scope.Contract.UseGas(statelessGas)
} }
} else { } else {
@ -1026,7 +988,8 @@ func makePush(size uint64, pushByteSize int) executionFunc {
} }
if interpreter.evm.chainRules.IsVerkle { if interpreter.evm.chainRules.IsVerkle {
statelessGas := touchEachChunksOnReadAndChargeGas(uint64(startMin), uint64(pushByteSize), scope.Contract, scope.Contract.Code, interpreter.evm.Accesses, scope.Contract.IsDeployment) contractAddr := scope.Contract.Address()
statelessGas := touchCodeChunksRangeOnReadAndChargeGas(contractAddr[:], uint64(startMin), uint64(pushByteSize), uint64(len(scope.Contract.Code)), interpreter.evm.Accesses)
scope.Contract.UseGas(statelessGas) scope.Contract.UseGas(statelessGas)
} }

View file

@ -21,10 +21,6 @@ import (
"github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/trie/utils"
"github.com/gballet/go-verkle"
"github.com/holiman/uint256"
) )
// Config are the configuration options for the Interpreter // Config are the configuration options for the Interpreter
@ -152,8 +148,6 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
logged bool // deferred EVMLogger should ignore already logged steps logged bool // deferred EVMLogger should ignore already logged steps
res []byte // result of the opcode execution function res []byte // result of the opcode execution function
debug = in.evm.Config.Tracer != nil debug = in.evm.Config.Tracer != nil
chunkEvals [][]byte
) )
// Don't move this deferred function, it's placed before the capturestate-deferred method, // Don't move this deferred function, it's placed before the capturestate-deferred method,
// so that it get's executed _after_: the capturestate needs the stacks before // so that it get's executed _after_: the capturestate needs the stacks before
@ -175,21 +169,6 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
}() }()
} }
// Evaluate one address per group of 256, 31-byte chunks
if in.evm.chainRules.IsCancun && !contract.IsDeployment {
contract.Chunks = trie.ChunkifyCode(contract.Code)
// number of extra stems to evaluate after the header stem
extraEvals := (len(contract.Chunks) + 127) / verkle.NodeWidth
chunkEvals = make([][]byte, extraEvals+1)
for i := 1; i < extraEvals+1; i++ {
chunkEvals[i] = utils.GetTreeKeyCodeChunkWithEvaluatedAddress(contract.AddressPoint(), uint256.NewInt(uint64(i)*256))
}
// Header account is already known, it's the header account
chunkEvals[0] = utils.GetTreeKeyVersionWithEvaluatedAddress(contract.AddressPoint())
}
// The Interpreter main run loop (contextual). This loop runs until either an // The Interpreter main run loop (contextual). This loop runs until either an
// explicit STOP, RETURN or SELFDESTRUCT is executed, an error occurred during // explicit STOP, RETURN or SELFDESTRUCT is executed, an error occurred during
// the execution of one of the operations or until the done flag is set by the // the execution of one of the operations or until the done flag is set by the
@ -200,10 +179,11 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
logged, pcCopy, gasCopy = false, pc, contract.Gas logged, pcCopy, gasCopy = false, pc, contract.Gas
} }
if contract.Chunks != nil { if in.evm.chainRules.IsCancun && !contract.IsDeployment {
// if the PC ends up in a new "chunk" of verkleized code, charge the // if the PC ends up in a new "chunk" of verkleized code, charge the
// associated costs. // associated costs.
contract.Gas -= touchChunkOnReadAndChargeGas(contract.Chunks, pc, chunkEvals, contract.Code, in.evm.TxContext.Accesses, contract.IsDeployment) contractAddr := contract.Address()
contract.Gas -= touchCodeChunksRangeOnReadAndChargeGas(contractAddr[:], pc, 1, uint64(len(contract.Code)), in.evm.TxContext.Accesses)
} }
// Get the operation from the jump table and validate the stack to ensure there are // Get the operation from the jump table and validate the stack to ensure there are

View file

@ -22,7 +22,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/params" "github.com/ethereum/go-ethereum/params"
trieUtils "github.com/ethereum/go-ethereum/trie/utils" "github.com/ethereum/go-ethereum/trie/utils"
) )
func makeGasSStoreFunc(clearingRefund uint64) gasFunc { func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
@ -53,8 +53,8 @@ func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
value := common.Hash(y.Bytes32()) value := common.Hash(y.Bytes32())
if evm.chainRules.IsVerkle { if evm.chainRules.IsVerkle {
index := trieUtils.GetTreeKeyStorageSlotWithEvaluatedAddress(contract.AddressPoint(), x.Bytes()) treeIndex, subIndex := utils.GetTreeKeyStorageSlotTreeIndexes(x.Bytes())
cost += evm.Accesses.TouchAddressOnWriteAndComputeGas(index) cost += evm.Accesses.TouchAddressOnWriteAndComputeGas(contract.Address().Bytes(), *treeIndex, subIndex)
} }
if current == value { // noop (1) if current == value { // noop (1)
@ -113,9 +113,9 @@ func gasSLoadEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me
if evm.chainRules.IsVerkle { if evm.chainRules.IsVerkle {
where := stack.Back(0) where := stack.Back(0)
treeIndex, subIndex := utils.GetTreeKeyStorageSlotTreeIndexes(where.Bytes())
addr := contract.Address() addr := contract.Address()
index := trieUtils.GetTreeKeyStorageSlot(addr[:], where) gasUsed += evm.Accesses.TouchAddressOnReadAndComputeGas(addr.Bytes(), *treeIndex, subIndex)
gasUsed += evm.Accesses.TouchAddressOnReadAndComputeGas(index)
} }
// Check slot presence in the access list // Check slot presence in the access list

View file

@ -144,7 +144,7 @@ func GetTreeKeyVersion(address []byte) []byte {
} }
func GetTreeKeyVersionWithEvaluatedAddress(addrp *verkle.Point) []byte { func GetTreeKeyVersionWithEvaluatedAddress(addrp *verkle.Point) []byte {
return getTreeKeyWithEvaluatedAddess(addrp, zero, VersionLeafKey) return GetTreeKeyWithEvaluatedAddess(addrp, zero, VersionLeafKey)
} }
func GetTreeKeyBalance(address []byte) []byte { func GetTreeKeyBalance(address []byte) []byte {
@ -182,7 +182,7 @@ func GetTreeKeyCodeChunkWithEvaluatedAddress(addressPoint *verkle.Point, chunk *
if len(subIndexMod) != 0 { if len(subIndexMod) != 0 {
subIndex = byte(subIndexMod[0]) subIndex = byte(subIndexMod[0])
} }
return getTreeKeyWithEvaluatedAddess(addressPoint, treeIndex, subIndex) return GetTreeKeyWithEvaluatedAddess(addressPoint, treeIndex, subIndex)
} }
func GetTreeKeyStorageSlot(address []byte, storageKey *uint256.Int) []byte { func GetTreeKeyStorageSlot(address []byte, storageKey *uint256.Int) []byte {
@ -221,7 +221,7 @@ func PointToHash(evaluated *verkle.Point, suffix byte) []byte {
return retb[:] return retb[:]
} }
func getTreeKeyWithEvaluatedAddess(evaluated *verkle.Point, treeIndex *uint256.Int, subIndex byte) []byte { func GetTreeKeyWithEvaluatedAddess(evaluated *verkle.Point, treeIndex *uint256.Int, subIndex byte) []byte {
var poly [5]fr.Element var poly [5]fr.Element
poly[0].SetZero() poly[0].SetZero()
@ -269,6 +269,11 @@ func EvaluateAddressPoint(address []byte) *verkle.Point {
} }
func GetTreeKeyStorageSlotWithEvaluatedAddress(evaluated *verkle.Point, storageKey []byte) []byte { func GetTreeKeyStorageSlotWithEvaluatedAddress(evaluated *verkle.Point, storageKey []byte) []byte {
treeIndex, subIndex := GetTreeKeyStorageSlotTreeIndexes(storageKey)
return GetTreeKeyWithEvaluatedAddess(evaluated, treeIndex, subIndex)
}
func GetTreeKeyStorageSlotTreeIndexes(storageKey []byte) (*uint256.Int, byte) {
// Note that `pos` must be a big.Int and not a uint256.Int, because the subsequent // Note that `pos` must be a big.Int and not a uint256.Int, because the subsequent
// arithmetics operations could overflow. (e.g: imagine if storageKey is 2^256-1) // arithmetics operations could overflow. (e.g: imagine if storageKey is 2^256-1)
pos := new(big.Int).SetBytes(storageKey) pos := new(big.Int).SetBytes(storageKey)
@ -286,5 +291,5 @@ func GetTreeKeyStorageSlotWithEvaluatedAddress(evaluated *verkle.Point, storageK
posBytes := pos.Bytes() posBytes := pos.Bytes()
subIndex := posBytes[len(posBytes)-1] subIndex := posBytes[len(posBytes)-1]
return getTreeKeyWithEvaluatedAddess(evaluated, treeIndex, subIndex) return treeIndex, subIndex
} }

View file

@ -65,11 +65,9 @@ func TestReproduceTree(t *testing.T) {
} }
root := verkle.New() root := verkle.New()
kv := make(map[string][]byte)
for i, key := range presentKeys { for i, key := range presentKeys {
root.Insert(key, values[i], nil) root.Insert(key, values[i], nil)
kv[string(key)] = values[i]
} }
proof, Cs, zis, yis, _ := verkle.MakeVerkleMultiProof(root, append(presentKeys, absentKeys...)) proof, Cs, zis, yis, _ := verkle.MakeVerkleMultiProof(root, append(presentKeys, absentKeys...))
@ -286,11 +284,9 @@ func TestReproduceCondrieuStemAggregationInProofOfAbsence(t *testing.T) {
} }
root := verkle.New() root := verkle.New()
kv := make(map[string][]byte)
for i, key := range presentKeys { for i, key := range presentKeys {
root.Insert(key, values[i], nil) root.Insert(key, values[i], nil)
kv[string(key)] = values[i]
} }
proof, Cs, zis, yis, _ := verkle.MakeVerkleMultiProof(root, append(presentKeys, absentKeys...)) proof, Cs, zis, yis, _ := verkle.MakeVerkleMultiProof(root, append(presentKeys, absentKeys...))
@ -333,11 +329,9 @@ func TestReproduceCondrieuPoAStemConflictWithAnotherStem(t *testing.T) {
} }
root := verkle.New() root := verkle.New()
kv := make(map[string][]byte)
for i, key := range presentKeys { for i, key := range presentKeys {
root.Insert(key, values[i], nil) root.Insert(key, values[i], nil)
kv[string(key)] = values[i]
} }
proof, Cs, zis, yis, _ := verkle.MakeVerkleMultiProof(root, append(presentKeys, absentKeys...)) proof, Cs, zis, yis, _ := verkle.MakeVerkleMultiProof(root, append(presentKeys, absentKeys...))