|
|
|
@ -44,14 +44,16 @@ var zeroTreeIndex uint256.Int |
|
|
|
|
type AccessEvents struct { |
|
|
|
|
branches map[branchAccessKey]mode |
|
|
|
|
chunks map[chunkAccessKey]mode |
|
|
|
|
fills map[chunkAccessKey]struct{} |
|
|
|
|
|
|
|
|
|
pointCache *utils.PointCache |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func NewAccessEvents(pointCache *utils.PointCache) *AccessEvents { |
|
|
|
|
func NewAccessEvents(pointCache *utils.PointCache, fills map[chunkAccessKey]struct{}) *AccessEvents { |
|
|
|
|
return &AccessEvents{ |
|
|
|
|
branches: make(map[branchAccessKey]mode), |
|
|
|
|
chunks: make(map[chunkAccessKey]mode), |
|
|
|
|
fills: fills, |
|
|
|
|
pointCache: pointCache, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -66,6 +68,9 @@ func (ae *AccessEvents) Merge(other *AccessEvents) { |
|
|
|
|
for k, chunk := range other.chunks { |
|
|
|
|
ae.chunks[k] |= chunk |
|
|
|
|
} |
|
|
|
|
for k := range other.fills { |
|
|
|
|
ae.fills[k] = struct{}{} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Keys returns, predictably, the list of keys that were touched during the
|
|
|
|
@ -85,6 +90,7 @@ func (ae *AccessEvents) Copy() *AccessEvents { |
|
|
|
|
cpy := &AccessEvents{ |
|
|
|
|
branches: maps.Clone(ae.branches), |
|
|
|
|
chunks: maps.Clone(ae.chunks), |
|
|
|
|
fills: maps.Clone(ae.fills), |
|
|
|
|
pointCache: ae.pointCache, |
|
|
|
|
} |
|
|
|
|
return cpy |
|
|
|
@ -92,13 +98,13 @@ func (ae *AccessEvents) Copy() *AccessEvents { |
|
|
|
|
|
|
|
|
|
// AddAccount returns the gas to be charged for each of the currently cold
|
|
|
|
|
// member fields of an account.
|
|
|
|
|
func (ae *AccessEvents) AddAccount(addr common.Address, isWrite bool) uint64 { |
|
|
|
|
func (ae *AccessEvents) AddAccount(addr common.Address, isWrite, isFill bool) uint64 { |
|
|
|
|
var gas uint64 |
|
|
|
|
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.VersionLeafKey, isWrite) |
|
|
|
|
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BalanceLeafKey, isWrite) |
|
|
|
|
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.NonceLeafKey, isWrite) |
|
|
|
|
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeKeccakLeafKey, isWrite) |
|
|
|
|
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeSizeLeafKey, isWrite) |
|
|
|
|
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.VersionLeafKey, isWrite, isFill) |
|
|
|
|
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BalanceLeafKey, isWrite, isFill) |
|
|
|
|
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.NonceLeafKey, isWrite, isFill) |
|
|
|
|
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeKeccakLeafKey, isWrite, isFill) |
|
|
|
|
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeSizeLeafKey, isWrite, isFill) |
|
|
|
|
return gas |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -107,28 +113,28 @@ func (ae *AccessEvents) AddAccount(addr common.Address, isWrite bool) uint64 { |
|
|
|
|
// call to that account.
|
|
|
|
|
func (ae *AccessEvents) MessageCallGas(destination common.Address) uint64 { |
|
|
|
|
var gas uint64 |
|
|
|
|
gas += ae.touchAddressAndChargeGas(destination, zeroTreeIndex, utils.VersionLeafKey, false) |
|
|
|
|
gas += ae.touchAddressAndChargeGas(destination, zeroTreeIndex, utils.CodeSizeLeafKey, false) |
|
|
|
|
gas += ae.touchAddressAndChargeGas(destination, zeroTreeIndex, utils.VersionLeafKey, false, false) |
|
|
|
|
gas += ae.touchAddressAndChargeGas(destination, zeroTreeIndex, utils.CodeSizeLeafKey, false, false) |
|
|
|
|
return gas |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// ValueTransferGas returns the gas to be charged for each of the currently
|
|
|
|
|
// cold balance member fields of the caller and the callee accounts.
|
|
|
|
|
func (ae *AccessEvents) ValueTransferGas(callerAddr, targetAddr common.Address) uint64 { |
|
|
|
|
func (ae *AccessEvents) ValueTransferGas(callerAddr, targetAddr common.Address, isFill bool) uint64 { |
|
|
|
|
var gas uint64 |
|
|
|
|
gas += ae.touchAddressAndChargeGas(callerAddr, zeroTreeIndex, utils.BalanceLeafKey, true) |
|
|
|
|
gas += ae.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BalanceLeafKey, true) |
|
|
|
|
gas += ae.touchAddressAndChargeGas(callerAddr, zeroTreeIndex, utils.BalanceLeafKey, true, false) |
|
|
|
|
gas += ae.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BalanceLeafKey, true, isFill) |
|
|
|
|
return gas |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// ContractCreateInitGas returns the access gas costs for the initialization of
|
|
|
|
|
// a contract creation.
|
|
|
|
|
func (ae *AccessEvents) ContractCreateInitGas(addr common.Address, createSendsValue bool) uint64 { |
|
|
|
|
func (ae *AccessEvents) ContractCreateInitGas(addr common.Address, createSendsValue, isFill bool) uint64 { |
|
|
|
|
var gas uint64 |
|
|
|
|
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.VersionLeafKey, true) |
|
|
|
|
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.NonceLeafKey, true) |
|
|
|
|
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.VersionLeafKey, true, true) |
|
|
|
|
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.NonceLeafKey, true, true) |
|
|
|
|
if createSendsValue { |
|
|
|
|
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BalanceLeafKey, true) |
|
|
|
|
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BalanceLeafKey, true, isFill) |
|
|
|
|
} |
|
|
|
|
return gas |
|
|
|
|
} |
|
|
|
@ -136,33 +142,33 @@ func (ae *AccessEvents) ContractCreateInitGas(addr common.Address, createSendsVa |
|
|
|
|
// AddTxOrigin adds the member fields of the sender account to the access event list,
|
|
|
|
|
// so that cold accesses are not charged, since they are covered by the 21000 gas.
|
|
|
|
|
func (ae *AccessEvents) AddTxOrigin(originAddr common.Address) { |
|
|
|
|
ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.VersionLeafKey, false) |
|
|
|
|
ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.BalanceLeafKey, true) |
|
|
|
|
ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.NonceLeafKey, true) |
|
|
|
|
ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.CodeKeccakLeafKey, false) |
|
|
|
|
ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.CodeSizeLeafKey, false) |
|
|
|
|
ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.VersionLeafKey, false, false) |
|
|
|
|
ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.BalanceLeafKey, true, false) |
|
|
|
|
ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.NonceLeafKey, true, false) |
|
|
|
|
ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.CodeKeccakLeafKey, false, false) |
|
|
|
|
ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.CodeSizeLeafKey, false, false) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// AddTxDestination adds the member fields of the sender account to the access event list,
|
|
|
|
|
// so that cold accesses are not charged, since they are covered by the 21000 gas.
|
|
|
|
|
func (ae *AccessEvents) AddTxDestination(addr common.Address, sendsValue bool) { |
|
|
|
|
ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.VersionLeafKey, false) |
|
|
|
|
ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BalanceLeafKey, sendsValue) |
|
|
|
|
ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.NonceLeafKey, false) |
|
|
|
|
ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeKeccakLeafKey, false) |
|
|
|
|
ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeSizeLeafKey, false) |
|
|
|
|
func (ae *AccessEvents) AddTxDestination(addr common.Address, sendsValue, isFill bool) { |
|
|
|
|
ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.VersionLeafKey, false, false) |
|
|
|
|
ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BalanceLeafKey, sendsValue, isFill) |
|
|
|
|
ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.NonceLeafKey, false, false) |
|
|
|
|
ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeKeccakLeafKey, false, false) |
|
|
|
|
ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeSizeLeafKey, false, false) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// SlotGas returns the amount of gas to be charged for a cold storage access.
|
|
|
|
|
func (ae *AccessEvents) SlotGas(addr common.Address, slot common.Hash, isWrite bool) uint64 { |
|
|
|
|
func (ae *AccessEvents) SlotGas(addr common.Address, slot common.Hash, isWrite, isFill bool) uint64 { |
|
|
|
|
treeIndex, subIndex := utils.StorageIndex(slot.Bytes()) |
|
|
|
|
return ae.touchAddressAndChargeGas(addr, *treeIndex, subIndex, isWrite) |
|
|
|
|
return ae.touchAddressAndChargeGas(addr, *treeIndex, subIndex, isWrite, isFill) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// touchAddressAndChargeGas adds any missing access event to the access event list, and returns the cold
|
|
|
|
|
// access cost to be charged, if need be.
|
|
|
|
|
func (ae *AccessEvents) touchAddressAndChargeGas(addr common.Address, treeIndex uint256.Int, subIndex byte, isWrite bool) uint64 { |
|
|
|
|
stemRead, selectorRead, stemWrite, selectorWrite, selectorFill := ae.touchAddress(addr, treeIndex, subIndex, isWrite) |
|
|
|
|
func (ae *AccessEvents) touchAddressAndChargeGas(addr common.Address, treeIndex uint256.Int, subIndex byte, isWrite, isFill bool) uint64 { |
|
|
|
|
stemRead, selectorRead, stemWrite, selectorWrite, selectorFill := ae.touchAddress(addr, treeIndex, subIndex, isWrite, isFill) |
|
|
|
|
|
|
|
|
|
var gas uint64 |
|
|
|
|
if stemRead { |
|
|
|
@ -184,7 +190,7 @@ func (ae *AccessEvents) touchAddressAndChargeGas(addr common.Address, treeIndex |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// touchAddress adds any missing access event to the access event list.
|
|
|
|
|
func (ae *AccessEvents) touchAddress(addr common.Address, treeIndex uint256.Int, subIndex byte, isWrite bool) (bool, bool, bool, bool, bool) { |
|
|
|
|
func (ae *AccessEvents) touchAddress(addr common.Address, treeIndex uint256.Int, subIndex byte, isWrite, isFill bool) (bool, bool, bool, bool, bool) { |
|
|
|
|
branchKey := newBranchAccessKey(addr, treeIndex) |
|
|
|
|
chunkKey := newChunkAccessKey(branchKey, subIndex) |
|
|
|
|
|
|
|
|
@ -212,7 +218,12 @@ func (ae *AccessEvents) touchAddress(addr common.Address, treeIndex uint256.Int, |
|
|
|
|
chunkWrite = true |
|
|
|
|
ae.chunks[chunkKey] |= AccessWitnessWriteFlag |
|
|
|
|
} |
|
|
|
|
// TODO: charge chunk filling costs if the leaf was previously empty in the state
|
|
|
|
|
_, ok := ae.fills[chunkKey] |
|
|
|
|
if isFill && !ok { |
|
|
|
|
chunkFill = true |
|
|
|
|
ae.fills[chunkKey] = struct{}{} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
return branchRead, chunkRead, branchWrite, chunkWrite, chunkFill |
|
|
|
|
} |
|
|
|
@ -242,7 +253,7 @@ func newChunkAccessKey(branchKey branchAccessKey, leafKey byte) chunkAccessKey { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// CodeChunksRangeGas is a helper function to touch every chunk in a code range and charge witness gas costs
|
|
|
|
|
func (ae *AccessEvents) CodeChunksRangeGas(contractAddr common.Address, startPC, size uint64, codeLen uint64, isWrite bool) uint64 { |
|
|
|
|
func (ae *AccessEvents) CodeChunksRangeGas(contractAddr common.Address, startPC, size uint64, codeLen uint64, isWrite, isFill bool) 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
|
|
|
|
@ -265,7 +276,7 @@ func (ae *AccessEvents) CodeChunksRangeGas(contractAddr common.Address, startPC, |
|
|
|
|
for chunkNumber := startPC / 31; chunkNumber <= endPC/31; chunkNumber++ { |
|
|
|
|
treeIndex := *uint256.NewInt((chunkNumber + 128) / 256) |
|
|
|
|
subIndex := byte((chunkNumber + 128) % 256) |
|
|
|
|
gas := ae.touchAddressAndChargeGas(contractAddr, treeIndex, subIndex, isWrite) |
|
|
|
|
gas := ae.touchAddressAndChargeGas(contractAddr, treeIndex, subIndex, isWrite, isFill) |
|
|
|
|
var overflow bool |
|
|
|
|
statelessGasCharged, overflow = math.SafeAdd(statelessGasCharged, gas) |
|
|
|
|
if overflow { |
|
|
|
@ -280,7 +291,7 @@ func (ae *AccessEvents) CodeChunksRangeGas(contractAddr common.Address, startPC, |
|
|
|
|
// Note that an access in write mode implies an access in read mode, whereas an
|
|
|
|
|
// access in read mode does not imply an access in write mode.
|
|
|
|
|
func (ae *AccessEvents) VersionGas(addr common.Address, isWrite bool) uint64 { |
|
|
|
|
return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.VersionLeafKey, isWrite) |
|
|
|
|
return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.VersionLeafKey, isWrite, false) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// BalanceGas adds the account's balance to the accessed data, and returns the
|
|
|
|
@ -288,8 +299,8 @@ func (ae *AccessEvents) VersionGas(addr common.Address, isWrite bool) uint64 { |
|
|
|
|
// in write mode. If false, the charged gas corresponds to an access in read mode.
|
|
|
|
|
// Note that an access in write mode implies an access in read mode, whereas an access in
|
|
|
|
|
// read mode does not imply an access in write mode.
|
|
|
|
|
func (ae *AccessEvents) BalanceGas(addr common.Address, isWrite bool) uint64 { |
|
|
|
|
return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BalanceLeafKey, isWrite) |
|
|
|
|
func (ae *AccessEvents) BalanceGas(addr common.Address, isWrite, isFill bool) uint64 { |
|
|
|
|
return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BalanceLeafKey, isWrite, isFill) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// NonceGas adds the account's nonce to the accessed data, and returns the
|
|
|
|
@ -298,7 +309,7 @@ func (ae *AccessEvents) BalanceGas(addr common.Address, isWrite bool) uint64 { |
|
|
|
|
// Note that an access in write mode implies an access in read mode, whereas an access in
|
|
|
|
|
// read mode does not imply an access in write mode.
|
|
|
|
|
func (ae *AccessEvents) NonceGas(addr common.Address, isWrite bool) uint64 { |
|
|
|
|
return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.NonceLeafKey, isWrite) |
|
|
|
|
return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.NonceLeafKey, isWrite, false) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// CodeSizeGas adds the account's code size to the accessed data, and returns the
|
|
|
|
@ -307,7 +318,7 @@ func (ae *AccessEvents) NonceGas(addr common.Address, isWrite bool) uint64 { |
|
|
|
|
// Note that an access in write mode implies an access in read mode, whereas an access in
|
|
|
|
|
// read mode does not imply an access in write mode.
|
|
|
|
|
func (ae *AccessEvents) CodeSizeGas(addr common.Address, isWrite bool) uint64 { |
|
|
|
|
return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeSizeLeafKey, isWrite) |
|
|
|
|
return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeSizeLeafKey, isWrite, false) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// CodeHashGas adds the account's code hash to the accessed data, and returns the
|
|
|
|
@ -316,5 +327,5 @@ func (ae *AccessEvents) CodeSizeGas(addr common.Address, isWrite bool) uint64 { |
|
|
|
|
// Note that an access in write mode implies an access in read mode, whereas an access in
|
|
|
|
|
// read mode does not imply an access in write mode.
|
|
|
|
|
func (ae *AccessEvents) CodeHashGas(addr common.Address, isWrite bool) uint64 { |
|
|
|
|
return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeKeccakLeafKey, isWrite) |
|
|
|
|
return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeKeccakLeafKey, isWrite, false) |
|
|
|
|
} |
|
|
|
|