mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 18:32:23 +00:00
New access-witness module (#235)
Use plain addresses/slot numbers instead of hashing them in the witness
This commit is contained in:
parent
a926f60699
commit
a0e1995bc4
13 changed files with 234 additions and 459 deletions
|
|
@ -30,6 +30,8 @@ import (
|
|||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/ethereum/go-ethereum/trie/utils"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
// Proof-of-stake protocol constants.
|
||||
|
|
@ -354,7 +356,7 @@ func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types.
|
|||
state.AddBalance(w.Address, amount)
|
||||
|
||||
// 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.
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/ethereum/go-ethereum/trie/utils"
|
||||
"github.com/holiman/uint256"
|
||||
"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
|
||||
if config.IsVerkle(header.Number, header.Time) {
|
||||
uncleCoinbase := utils.GetTreeKeyBalance(uncle.Coinbase.Bytes())
|
||||
state.Witness().TouchAddressOnReadAndComputeGas(uncleCoinbase)
|
||||
state.Witness().TouchAddressOnReadAndComputeGas(uncle.Coinbase.Bytes(), uint256.Int{}, utils.BalanceLeafKey)
|
||||
}
|
||||
state.AddBalance(uncle.Coinbase, r)
|
||||
|
||||
|
|
@ -577,14 +577,10 @@ func accumulateRewards(config *params.ChainConfig, state *state.StateDB, header
|
|||
reward.Add(reward, r)
|
||||
}
|
||||
if config.IsVerkle(header.Number, header.Time) {
|
||||
coinbase := utils.GetTreeKeyBalance(header.Coinbase.Bytes())
|
||||
state.Witness().TouchAddressOnReadAndComputeGas(coinbase)
|
||||
coinbase[31] = utils.VersionLeafKey // mark version
|
||||
state.Witness().TouchAddressOnReadAndComputeGas(coinbase)
|
||||
coinbase[31] = utils.NonceLeafKey // mark nonce
|
||||
state.Witness().TouchAddressOnReadAndComputeGas(coinbase)
|
||||
coinbase[31] = utils.CodeKeccakLeafKey // mark code keccak
|
||||
state.Witness().TouchAddressOnReadAndComputeGas(coinbase)
|
||||
state.Witness().TouchAddressOnReadAndComputeGas(header.Coinbase.Bytes(), uint256.Int{}, utils.BalanceLeafKey)
|
||||
state.Witness().TouchAddressOnReadAndComputeGas(header.Coinbase.Bytes(), uint256.Int{}, utils.VersionLeafKey)
|
||||
state.Witness().TouchAddressOnReadAndComputeGas(header.Coinbase.Bytes(), uint256.Int{}, utils.NonceLeafKey)
|
||||
state.Witness().TouchAddressOnReadAndComputeGas(header.Coinbase.Bytes(), uint256.Int{}, utils.CodeKeccakLeafKey)
|
||||
}
|
||||
state.AddBalance(header.Coinbase, reward)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,147 +20,160 @@ import (
|
|||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"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:
|
||||
// * the first bit is set if the branch has been edited
|
||||
// * the second bit is set if the branch has been read
|
||||
type Mode byte
|
||||
type mode byte
|
||||
|
||||
const (
|
||||
AccessWitnessReadFlag = Mode(1)
|
||||
AccessWitnessWriteFlag = Mode(2)
|
||||
AccessWitnessReadFlag = mode(1)
|
||||
AccessWitnessWriteFlag = mode(2)
|
||||
)
|
||||
|
||||
var zeroTreeIndex uint256.Int
|
||||
|
||||
// AccessWitness lists the locations of the state that are being accessed
|
||||
// during the production of a block.
|
||||
type AccessWitness struct {
|
||||
// Branches flags if a given branch has been loaded
|
||||
Branches map[VerkleStem]Mode
|
||||
branches map[branchAccessKey]mode
|
||||
chunks map[chunkAccessKey]mode
|
||||
|
||||
// Chunks contains the initial value of each address
|
||||
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
|
||||
pointCache *utils.PointCache
|
||||
}
|
||||
|
||||
func NewAccessWitness(statedb *StateDB) *AccessWitness {
|
||||
func NewAccessWitness(pointCache *utils.PointCache) *AccessWitness {
|
||||
return &AccessWitness{
|
||||
Branches: make(map[VerkleStem]Mode),
|
||||
Chunks: make(map[common.Hash]Mode),
|
||||
InitialValue: make(map[string][]byte),
|
||||
CodeLocations: make(map[string]map[uint64]struct{}),
|
||||
statedb: statedb,
|
||||
branches: make(map[branchAccessKey]mode),
|
||||
chunks: make(map[chunkAccessKey]mode),
|
||||
pointCache: pointCache,
|
||||
}
|
||||
}
|
||||
|
||||
func (aw *AccessWitness) HasCodeChunk(addr []byte, chunknr uint64) bool {
|
||||
if locs, ok := aw.CodeLocations[string(addr)]; ok {
|
||||
if _, ok = locs[chunknr]; ok {
|
||||
return true
|
||||
}
|
||||
// Merge is used to merge the witness that got generated during the execution
|
||||
// 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 {
|
||||
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
|
||||
// maintains a cache of which (address, chunk) were calculated, in order to avoid
|
||||
// calling GetTreeKey more than once per chunk.
|
||||
func (aw *AccessWitness) SetCachedCodeChunk(addr []byte, chunknr uint64) {
|
||||
if locs, ok := aw.CodeLocations[string(addr)]; ok {
|
||||
if _, ok = locs[chunknr]; ok {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
aw.CodeLocations[string(addr)] = map[uint64]struct{}{}
|
||||
// Key returns, predictably, the list of keys that were touched during the
|
||||
// buildup of the access witness.
|
||||
func (aw *AccessWitness) Keys() [][]byte {
|
||||
// TODO: consider if parallelizing this is worth it, probably depending on len(aw.chunks).
|
||||
keys := make([][]byte, 0, len(aw.chunks))
|
||||
for chunk := range aw.chunks {
|
||||
basePoint := aw.pointCache.GetTreeKeyHeader(chunk.addr[:])
|
||||
key := utils.GetTreeKeyWithEvaluatedAddess(basePoint, &chunk.treeIndex, chunk.leafKey)
|
||||
keys = append(keys, key)
|
||||
}
|
||||
|
||||
aw.CodeLocations[string(addr)][chunknr] = struct{}{}
|
||||
return keys
|
||||
}
|
||||
|
||||
func (aw *AccessWitness) touchAddressOnWrite(addr []byte) (bool, bool, bool) {
|
||||
var stem VerkleStem
|
||||
var stemWrite, chunkWrite, chunkFill bool
|
||||
copy(stem[:], addr[:31])
|
||||
|
||||
// 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
|
||||
func (aw *AccessWitness) Copy() *AccessWitness {
|
||||
naw := &AccessWitness{
|
||||
branches: make(map[branchAccessKey]mode),
|
||||
chunks: make(map[chunkAccessKey]mode),
|
||||
pointCache: aw.pointCache,
|
||||
}
|
||||
|
||||
chunkValue := aw.Chunks[common.BytesToHash(addr)]
|
||||
// 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
|
||||
naw.Merge(aw)
|
||||
return naw
|
||||
}
|
||||
|
||||
// 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 []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 {
|
||||
func (aw *AccessWitness) TouchAndChargeProofOfAbsence(addr []byte) 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 {
|
||||
gas += params.WitnessBranchReadCost
|
||||
}
|
||||
|
|
@ -180,231 +193,62 @@ func (aw *AccessWitness) touchAddressAndChargeGas(addr []byte, isWrite bool) uin
|
|||
return gas
|
||||
}
|
||||
|
||||
func (aw *AccessWitness) TouchAddressOnWriteAndComputeGas(addr []byte) uint64 {
|
||||
return aw.touchAddressAndChargeGas(addr, true)
|
||||
}
|
||||
// touchAddress adds any missing access event to the witness.
|
||||
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 {
|
||||
return aw.touchAddressAndChargeGas(addr, false)
|
||||
}
|
||||
// Read access.
|
||||
var branchRead, chunkRead bool
|
||||
if _, hasStem := aw.branches[branchKey]; !hasStem {
|
||||
branchRead = true
|
||||
aw.branches[branchKey] = AccessWitnessReadFlag
|
||||
}
|
||||
if _, hasSelector := aw.chunks[chunkKey]; !hasSelector {
|
||||
chunkRead = true
|
||||
aw.chunks[chunkKey] = AccessWitnessReadFlag
|
||||
}
|
||||
|
||||
// Merge is used to merge the witness that got generated during the execution
|
||||
// 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]
|
||||
// Write access.
|
||||
var branchWrite, chunkWrite, chunkFill bool
|
||||
if isWrite {
|
||||
if (aw.branches[branchKey] & AccessWitnessWriteFlag) == 0 {
|
||||
branchWrite = true
|
||||
aw.branches[branchKey] |= AccessWitnessWriteFlag
|
||||
}
|
||||
}
|
||||
|
||||
for k, chunk := range other.Chunks {
|
||||
if _, ok := aw.Chunks[k]; !ok {
|
||||
aw.Chunks[k] = chunk
|
||||
chunkValue := aw.chunks[chunkKey]
|
||||
if (chunkValue & AccessWitnessWriteFlag) == 0 {
|
||||
chunkWrite = true
|
||||
aw.chunks[chunkKey] |= AccessWitnessWriteFlag
|
||||
}
|
||||
|
||||
// TODO: charge chunk filling costs if the leaf was previously empty in the state
|
||||
}
|
||||
|
||||
for k, v := range other.InitialValue {
|
||||
if _, ok := aw.InitialValue[k]; !ok {
|
||||
aw.InitialValue[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// TODO see if merging improves performance
|
||||
//for k, v := range other.addrToPoint {
|
||||
//if _, ok := aw.addrToPoint[k]; !ok {
|
||||
//aw.addrToPoint[k] = v
|
||||
//}
|
||||
//}
|
||||
return branchRead, chunkRead, branchWrite, chunkWrite, chunkFill
|
||||
}
|
||||
|
||||
// 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
|
||||
type branchAccessKey struct {
|
||||
addr common.Address
|
||||
treeIndex uint256.Int
|
||||
}
|
||||
|
||||
func (aw *AccessWitness) KeyVals() map[string][]byte {
|
||||
result := make(map[string][]byte)
|
||||
for k, v := range aw.InitialValue {
|
||||
result[k] = v
|
||||
}
|
||||
return result
|
||||
func newBranchAccessKey(addr []byte, treeIndex uint256.Int) branchAccessKey {
|
||||
var sk branchAccessKey
|
||||
copy(sk.addr[:], addr)
|
||||
sk.treeIndex = treeIndex
|
||||
return sk
|
||||
}
|
||||
|
||||
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 naw
|
||||
type chunkAccessKey struct {
|
||||
branchAccessKey
|
||||
leafKey byte
|
||||
}
|
||||
|
||||
func (aw *AccessWitness) GetTreeKeyVersionCached(addr []byte) []byte {
|
||||
return aw.statedb.db.(*cachingDB).addrToPoint.GetTreeKeyVersionCached(addr)
|
||||
}
|
||||
|
||||
func (aw *AccessWitness) TouchAndChargeProofOfAbsence(addr []byte) 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.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 {
|
||||
var (
|
||||
gas uint64
|
||||
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 {
|
||||
var gas uint64
|
||||
gas += aw.TouchAddressOnWriteAndComputeGas(utils.GetTreeKeyBalance(callerAddr[:]))
|
||||
gas += aw.TouchAddressOnWriteAndComputeGas(utils.GetTreeKeyBalance(targetAddr[:]))
|
||||
return gas
|
||||
}
|
||||
|
||||
// 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
|
||||
func newChunkAccessKey(branchKey branchAccessKey, leafKey byte) chunkAccessKey {
|
||||
var lk chunkAccessKey
|
||||
lk.branchAccessKey = branchKey
|
||||
lk.leafKey = leafKey
|
||||
return lk
|
||||
}
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@ func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error)
|
|||
hasher: crypto.NewKeccakState(),
|
||||
}
|
||||
if tr.IsVerkle() {
|
||||
sdb.witness = NewAccessWitness(sdb)
|
||||
sdb.witness = sdb.NewAccessWitness()
|
||||
// if sdb.snaps == nil {
|
||||
// snapconfig := snapshot.Config{
|
||||
// CacheSize: 256,
|
||||
|
|
@ -206,9 +206,13 @@ func (s *StateDB) Snaps() *snapshot.Tree {
|
|||
return s.snaps
|
||||
}
|
||||
|
||||
func (s *StateDB) NewAccessWitness() *AccessWitness {
|
||||
return NewAccessWitness(s.db.(*cachingDB).addrToPoint)
|
||||
}
|
||||
|
||||
func (s *StateDB) Witness() *AccessWitness {
|
||||
if s.witness == nil {
|
||||
s.witness = NewAccessWitness(s)
|
||||
s.witness = s.NewAccessWitness()
|
||||
}
|
||||
return s.witness
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
// Create a new context to be used in the EVM environment.
|
||||
txContext := NewEVMTxContext(msg)
|
||||
txContext.Accesses = state.NewAccessWitness(statedb)
|
||||
txContext.Accesses = statedb.NewAccessWitness()
|
||||
evm.Reset(txContext, statedb)
|
||||
|
||||
// Apply the transaction to the current state (included in the env).
|
||||
|
|
|
|||
|
|
@ -20,9 +20,6 @@ import (
|
|||
"math/big"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
|
|
@ -52,13 +49,11 @@ type Contract struct {
|
|||
CallerAddress common.Address
|
||||
caller ContractRef
|
||||
self ContractRef
|
||||
addressPoint *verkle.Point
|
||||
|
||||
jumpdests map[common.Hash]bitvec // Aggregated result of JUMPDEST analysis.
|
||||
analysis bitvec // Locally cached result of JUMPDEST analysis
|
||||
|
||||
Code []byte
|
||||
Chunks trie.ChunkedCode
|
||||
CodeHash common.Hash
|
||||
CodeAddr *common.Address
|
||||
Input []byte
|
||||
|
|
@ -180,14 +175,6 @@ func (c *Contract) Address() common.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)
|
||||
func (c *Contract) Value() *big.Int {
|
||||
return c.value
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, chainConfig
|
|||
chainRules: chainConfig.Rules(blockCtx.BlockNumber, blockCtx.Random != nil, 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)
|
||||
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.
|
||||
func (evm *EVM) Reset(txCtx TxContext, statedb StateDB) {
|
||||
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.StateDB = statedb
|
||||
|
|
@ -530,7 +530,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
|
|||
}
|
||||
|
||||
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)
|
||||
err = ErrOutOfGas
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
trieUtils "github.com/ethereum/go-ethereum/trie/utils"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
// 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)
|
||||
slot := stack.Back(0)
|
||||
if evm.chainRules.IsVerkle {
|
||||
index := trieUtils.GetTreeKeyCodeSize(slot.Bytes())
|
||||
usedGas += evm.TxContext.Accesses.TouchAddressOnReadAndComputeGas(index)
|
||||
usedGas += evm.TxContext.Accesses.TouchAddressOnReadAndComputeGas(slot.Bytes(), uint256.Int{}, trieUtils.CodeSizeLeafKey)
|
||||
}
|
||||
|
||||
return usedGas, nil
|
||||
|
|
@ -113,8 +113,8 @@ func gasSLoad(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySiz
|
|||
|
||||
if evm.chainRules.IsVerkle {
|
||||
where := stack.Back(0)
|
||||
index := trieUtils.GetTreeKeyStorageSlotWithEvaluatedAddress(contract.AddressPoint(), where.Bytes())
|
||||
usedGas += evm.Accesses.TouchAddressOnReadAndComputeGas(index)
|
||||
treeIndex, subIndex := trieUtils.GetTreeKeyStorageSlotTreeIndexes(where.Bytes())
|
||||
usedGas += evm.Accesses.TouchAddressOnReadAndComputeGas(contract.Address().Bytes(), *treeIndex, subIndex)
|
||||
}
|
||||
|
||||
return usedGas, nil
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
trieUtils "github.com/ethereum/go-ethereum/trie/utils"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
|
@ -347,8 +346,7 @@ func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
|
|||
slot := scope.Stack.peek()
|
||||
cs := uint64(interpreter.evm.StateDB.GetCodeSize(slot.Bytes20()))
|
||||
if interpreter.evm.chainRules.IsVerkle {
|
||||
index := trieUtils.GetTreeKeyCodeSize(slot.Bytes())
|
||||
statelessGas := interpreter.evm.Accesses.TouchAddressOnReadAndComputeGas(index)
|
||||
statelessGas := interpreter.evm.Accesses.TouchAddressOnReadAndComputeGas(slot.Bytes(), uint256.Int{}, trieUtils.CodeSizeLeafKey)
|
||||
scope.Contract.UseGas(statelessGas)
|
||||
}
|
||||
slot.SetUint64(cs)
|
||||
|
|
@ -373,79 +371,42 @@ func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
|
|||
uint64CodeOffset = 0xffffffffffffffff
|
||||
}
|
||||
|
||||
contractAddr := scope.Contract.Address()
|
||||
paddedCodeCopy, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(scope.Contract.Code, uint64CodeOffset, length.Uint64())
|
||||
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)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// touchChunkOnReadAndChargeGas 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 {
|
||||
// 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 {
|
||||
// touchCodeChunksRangeOnReadAndChargeGas is a helper function to touch every chunk in a code range and charge witness gas costs
|
||||
func touchCodeChunksRangeOnReadAndChargeGas(contractAddr []byte, startPC, size uint64, codeLen uint64, accesses *state.AccessWitness) uint64 {
|
||||
// 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,
|
||||
// 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 len(code) == 0 && size == 0 || offset > uint64(len(code)) {
|
||||
if (codeLen == 0 && size == 0) || startPC > codeLen {
|
||||
return 0
|
||||
}
|
||||
var (
|
||||
statelessGasCharged uint64
|
||||
endOffset uint64
|
||||
)
|
||||
if code != nil && offset+size > uint64(len(code)) {
|
||||
endOffset = uint64(len(code))
|
||||
} else {
|
||||
endOffset = offset + size
|
||||
|
||||
// endPC is the last PC that must be touched.
|
||||
endPC := startPC + size - 1
|
||||
if startPC+size > codeLen {
|
||||
endPC = codeLen
|
||||
}
|
||||
|
||||
// endOffset - 1 since if the end offset is aligned on a chunk boundary,
|
||||
// the last chunk should not be included.
|
||||
for i := offset / 31; i <= (endOffset-1)/31; i++ {
|
||||
// only charge for+cache the chunk if it isn't already present
|
||||
if !accesses.HasCodeChunk(contract.Address().Bytes(), i) {
|
||||
index := trieUtils.GetTreeKeyCodeChunkWithEvaluatedAddress(contract.AddressPoint(), uint256.NewInt(i))
|
||||
|
||||
var overflow bool
|
||||
statelessGasCharged, overflow = math.SafeAdd(statelessGasCharged, accesses.TouchAddressOnReadAndComputeGas(index))
|
||||
if overflow {
|
||||
panic("overflow when adding gas")
|
||||
}
|
||||
|
||||
accesses.SetCachedCodeChunk(contract.Address().Bytes(), i)
|
||||
var statelessGasCharged uint64
|
||||
for chunkNumber := startPC / 31; chunkNumber <= endPC/31; chunkNumber++ {
|
||||
treeIndex := *uint256.NewInt((chunkNumber + 128) / 256)
|
||||
subIndex := byte((chunkNumber + 128) % 256)
|
||||
gas := accesses.TouchAddressOnReadAndComputeGas(contractAddr, treeIndex, subIndex)
|
||||
var overflow bool
|
||||
statelessGasCharged, overflow = math.SafeAdd(statelessGasCharged, gas)
|
||||
if overflow {
|
||||
panic("overflow when adding gas")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -468,12 +429,12 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
|
|||
if interpreter.evm.chainRules.IsVerkle {
|
||||
code := interpreter.evm.StateDB.GetCode(addr)
|
||||
contract := &Contract{
|
||||
Code: code,
|
||||
Chunks: trie.ChunkedCode(code),
|
||||
self: AccountRef(addr),
|
||||
Code: code,
|
||||
self: AccountRef(addr),
|
||||
}
|
||||
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)
|
||||
} else {
|
||||
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 {
|
||||
// touch next chunk if PUSH1 is at the boundary. if so, *pc has
|
||||
// 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)
|
||||
}
|
||||
} else {
|
||||
|
|
@ -1026,7 +988,8 @@ func makePush(size uint64, pushByteSize int) executionFunc {
|
|||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,10 +21,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"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
|
||||
|
|
@ -152,8 +148,6 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
|||
logged bool // deferred EVMLogger should ignore already logged steps
|
||||
res []byte // result of the opcode execution function
|
||||
debug = in.evm.Config.Tracer != nil
|
||||
|
||||
chunkEvals [][]byte
|
||||
)
|
||||
// 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
|
||||
|
|
@ -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
|
||||
// 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
|
||||
|
|
@ -200,10 +179,11 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
|||
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
|
||||
// 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
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"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 {
|
||||
|
|
@ -53,8 +53,8 @@ func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
|
|||
value := common.Hash(y.Bytes32())
|
||||
|
||||
if evm.chainRules.IsVerkle {
|
||||
index := trieUtils.GetTreeKeyStorageSlotWithEvaluatedAddress(contract.AddressPoint(), x.Bytes())
|
||||
cost += evm.Accesses.TouchAddressOnWriteAndComputeGas(index)
|
||||
treeIndex, subIndex := utils.GetTreeKeyStorageSlotTreeIndexes(x.Bytes())
|
||||
cost += evm.Accesses.TouchAddressOnWriteAndComputeGas(contract.Address().Bytes(), *treeIndex, subIndex)
|
||||
}
|
||||
|
||||
if current == value { // noop (1)
|
||||
|
|
@ -113,9 +113,9 @@ func gasSLoadEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me
|
|||
|
||||
if evm.chainRules.IsVerkle {
|
||||
where := stack.Back(0)
|
||||
treeIndex, subIndex := utils.GetTreeKeyStorageSlotTreeIndexes(where.Bytes())
|
||||
addr := contract.Address()
|
||||
index := trieUtils.GetTreeKeyStorageSlot(addr[:], where)
|
||||
gasUsed += evm.Accesses.TouchAddressOnReadAndComputeGas(index)
|
||||
gasUsed += evm.Accesses.TouchAddressOnReadAndComputeGas(addr.Bytes(), *treeIndex, subIndex)
|
||||
}
|
||||
|
||||
// Check slot presence in the access list
|
||||
|
|
|
|||
|
|
@ -144,7 +144,7 @@ func GetTreeKeyVersion(address []byte) []byte {
|
|||
}
|
||||
|
||||
func GetTreeKeyVersionWithEvaluatedAddress(addrp *verkle.Point) []byte {
|
||||
return getTreeKeyWithEvaluatedAddess(addrp, zero, VersionLeafKey)
|
||||
return GetTreeKeyWithEvaluatedAddess(addrp, zero, VersionLeafKey)
|
||||
}
|
||||
|
||||
func GetTreeKeyBalance(address []byte) []byte {
|
||||
|
|
@ -182,7 +182,7 @@ func GetTreeKeyCodeChunkWithEvaluatedAddress(addressPoint *verkle.Point, chunk *
|
|||
if len(subIndexMod) != 0 {
|
||||
subIndex = byte(subIndexMod[0])
|
||||
}
|
||||
return getTreeKeyWithEvaluatedAddess(addressPoint, treeIndex, subIndex)
|
||||
return GetTreeKeyWithEvaluatedAddess(addressPoint, treeIndex, subIndex)
|
||||
}
|
||||
|
||||
func GetTreeKeyStorageSlot(address []byte, storageKey *uint256.Int) []byte {
|
||||
|
|
@ -221,7 +221,7 @@ func PointToHash(evaluated *verkle.Point, suffix byte) []byte {
|
|||
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
|
||||
|
||||
poly[0].SetZero()
|
||||
|
|
@ -269,6 +269,11 @@ func EvaluateAddressPoint(address []byte) *verkle.Point {
|
|||
}
|
||||
|
||||
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
|
||||
// arithmetics operations could overflow. (e.g: imagine if storageKey is 2^256-1)
|
||||
pos := new(big.Int).SetBytes(storageKey)
|
||||
|
|
@ -286,5 +291,5 @@ func GetTreeKeyStorageSlotWithEvaluatedAddress(evaluated *verkle.Point, storageK
|
|||
posBytes := pos.Bytes()
|
||||
subIndex := posBytes[len(posBytes)-1]
|
||||
|
||||
return getTreeKeyWithEvaluatedAddess(evaluated, treeIndex, subIndex)
|
||||
return treeIndex, subIndex
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,11 +65,9 @@ func TestReproduceTree(t *testing.T) {
|
|||
}
|
||||
|
||||
root := verkle.New()
|
||||
kv := make(map[string][]byte)
|
||||
|
||||
for i, key := range presentKeys {
|
||||
root.Insert(key, values[i], nil)
|
||||
kv[string(key)] = values[i]
|
||||
}
|
||||
|
||||
proof, Cs, zis, yis, _ := verkle.MakeVerkleMultiProof(root, append(presentKeys, absentKeys...))
|
||||
|
|
@ -286,11 +284,9 @@ func TestReproduceCondrieuStemAggregationInProofOfAbsence(t *testing.T) {
|
|||
}
|
||||
|
||||
root := verkle.New()
|
||||
kv := make(map[string][]byte)
|
||||
|
||||
for i, key := range presentKeys {
|
||||
root.Insert(key, values[i], nil)
|
||||
kv[string(key)] = values[i]
|
||||
}
|
||||
|
||||
proof, Cs, zis, yis, _ := verkle.MakeVerkleMultiProof(root, append(presentKeys, absentKeys...))
|
||||
|
|
@ -333,11 +329,9 @@ func TestReproduceCondrieuPoAStemConflictWithAnotherStem(t *testing.T) {
|
|||
}
|
||||
|
||||
root := verkle.New()
|
||||
kv := make(map[string][]byte)
|
||||
|
||||
for i, key := range presentKeys {
|
||||
root.Insert(key, values[i], nil)
|
||||
kv[string(key)] = values[i]
|
||||
}
|
||||
|
||||
proof, Cs, zis, yis, _ := verkle.MakeVerkleMultiProof(root, append(presentKeys, absentKeys...))
|
||||
|
|
|
|||
Loading…
Reference in a new issue