mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-23 05:06:43 +00:00
checkpoint commit
This commit is contained in:
parent
02a626614e
commit
9444281eab
2 changed files with 192 additions and 125 deletions
|
|
@ -53,15 +53,128 @@ TLDR:
|
||||||
|
|
||||||
// TODO: maybe rename this to StateDiffBuilder (?)
|
// TODO: maybe rename this to StateDiffBuilder (?)
|
||||||
type AccessListBuilder struct {
|
type AccessListBuilder struct {
|
||||||
accessesStack []map[common.Address]constructionAccountAccess
|
// stores the tx-prestate values of any account/storage values which were modified
|
||||||
|
//
|
||||||
|
// TODO: it's a bit unfortunate that the prestate.StorageWrites is reused here to
|
||||||
|
// represent the prestate values of keys which were written and it would be nice to
|
||||||
|
// somehow make it clear that the field can contain both the mutated values or the
|
||||||
|
// prestate depending on the context (or find a cleaner solution entirely, instead
|
||||||
|
// of reusing the field here)
|
||||||
|
prestates map[common.Address]*AccountState
|
||||||
|
|
||||||
|
accessesStack []map[common.Address]*constructionAccountAccess
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *AccessListBuilder) StorageRead(key common.Hash) {
|
||||||
|
c.curIdxChanges.StorageRead(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *AccessListBuilder) StorageWrite(address common.Address, key, prevVal, newVal common.Hash) {
|
||||||
|
if _, ok := c.prestates[address]; !ok {
|
||||||
|
c.prestates[address] = &AccountState{}
|
||||||
|
}
|
||||||
|
if c.prestates[address].StorageWrites == nil {
|
||||||
|
c.prestates[address].StorageWrites = make(map[common.Hash]common.Hash)
|
||||||
|
}
|
||||||
|
if _, ok := c.prestates[address].StorageWrites[key]; !ok {
|
||||||
|
c.prestates[address].StorageWrites[key] = prevVal
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := c.accessesStack[len(c.accessesStack)-1][address]; !ok {
|
||||||
|
c.accessesStack[len(c.accessesStack)-1][address] = &constructionAccountAccess{}
|
||||||
|
}
|
||||||
|
acctAccesses := c.accessesStack[len(c.accessesStack)-1][address]
|
||||||
|
acctAccesses.StorageWrite(key, prevVal, newVal)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *AccessListBuilder) BalanceChange(prev, cur *uint256.Int) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *AccessListBuilder) CodeChange(prev, cur []byte) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *AccessListBuilder) NonceChange(prev, cur uint64) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *AccessListBuilder) EnterScope() {
|
||||||
|
c.accessesStack = append(c.accessesStack, make(map[common.Address]*constructionAccountAccess))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *AccessListBuilder) ExitScope(reverted bool) {
|
||||||
|
// all storage writes in the child scope are converted into reads
|
||||||
|
// if there were no storage writes, the account is reported in the BAL as a read (if it wasn't already in the BAL and/or mutated previously)
|
||||||
|
childAccessList := c.accessesStack[len(c.accessesStack)-1]
|
||||||
|
parentAccessList := c.accessesStack[len(c.accessesStack)-2]
|
||||||
|
|
||||||
|
for addr, childAccess := range childAccessList {
|
||||||
|
if _, ok := parentAccessList[addr]; ok {
|
||||||
|
} else {
|
||||||
|
parentAccessList[addr] = &constructionAccountAccess{}
|
||||||
|
}
|
||||||
|
if reverted {
|
||||||
|
parentAccessList[addr].MergeReads(childAccess)
|
||||||
|
} else {
|
||||||
|
parentAccessList[addr].Merge(childAccess)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c.accessesStack = c.accessesStack[:len(c.accessesStack)-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *AccessListBuilder) Finalise() (*StateDiff, StateAccesses) {
|
func (a *AccessListBuilder) Finalise() (*StateDiff, StateAccesses) {
|
||||||
|
diff := &StateDiff{make(map[common.Address]*AccountState)}
|
||||||
|
stateAccesses := make(StateAccesses)
|
||||||
|
|
||||||
|
for addr, access := range a.accessesStack[0] {
|
||||||
|
// remove any mutations from the access list with no net difference vs the tx prestate value
|
||||||
|
if access.nonce != nil && *a.prestates[addr].Nonce == *access.nonce {
|
||||||
|
access.nonce = nil
|
||||||
|
}
|
||||||
|
if access.balance != nil && a.prestates[addr].Balance.Eq(access.balance) {
|
||||||
|
access.balance = nil
|
||||||
|
}
|
||||||
|
if access.code != nil && bytes.Equal(access.code, a.prestates[addr].Code) {
|
||||||
|
access.code = nil
|
||||||
|
}
|
||||||
|
// TODO: if the account was created/deleted in the same transaction
|
||||||
|
// it could register storage mutations, but they should be included
|
||||||
|
// in the BAL as reads
|
||||||
|
|
||||||
|
// two scenarios where an account can become non-existent:
|
||||||
|
// account was created/deleted in the same transaction
|
||||||
|
// account only had balance set (prefunded), was target of create2 initcode which called SENDALL leaving the account empty
|
||||||
|
if access.storageMutations != nil {
|
||||||
|
for key, val := range access.storageMutations {
|
||||||
|
if a.prestates[addr].StorageWrites[key] == val {
|
||||||
|
delete(access.storageMutations, key)
|
||||||
|
access.storageReads[key] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stateAccesses[addr] = access.storageReads
|
||||||
|
diff.Mutations[addr] = &AccountState{
|
||||||
|
Balance: access.balance,
|
||||||
|
Nonce: access.nonce,
|
||||||
|
Code: access.code,
|
||||||
|
StorageWrites: access.storageMutations,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return diff, stateAccesses
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ConstructionBlockAccessList) Apply(idx uint16, diff *StateDiff, accesses StateAccesses) {
|
func (c *ConstructionBlockAccessList) Apply(idx uint16, diff *StateDiff, accesses StateAccesses) {
|
||||||
|
for addr, stateDiff := range diff.Mutations {
|
||||||
|
acctChanges, ok := c.Accounts[addr]
|
||||||
|
if !ok {
|
||||||
|
acctChanges = &ConstructionAccountAccesses{}
|
||||||
|
c.Accounts[addr] = acctChanges
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: the BalReader Validation method should accept the computed values as
|
// TODO: the BalReader Validation method should accept the computed values as
|
||||||
|
|
@ -80,7 +193,8 @@ type CodeChange struct {
|
||||||
Code []byte `json:"code,omitempty"`
|
Code []byte `json:"code,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var IgnoredBALAddresses map[common.Address]struct{} = map[common.Address]struct{}{
|
// TODO: make use of this
|
||||||
|
var IgnoredBALAddresses = map[common.Address]struct{}{
|
||||||
params.SystemAddress: {},
|
params.SystemAddress: {},
|
||||||
common.BytesToAddress([]byte{0x01}): {},
|
common.BytesToAddress([]byte{0x01}): {},
|
||||||
common.BytesToAddress([]byte{0x02}): {},
|
common.BytesToAddress([]byte{0x02}): {},
|
||||||
|
|
@ -126,78 +240,9 @@ type ConstructionAccountAccesses struct {
|
||||||
NonceChanges map[uint16]uint64
|
NonceChanges map[uint16]uint64
|
||||||
|
|
||||||
CodeChanges map[uint16]CodeChange
|
CodeChanges map[uint16]CodeChange
|
||||||
|
|
||||||
curIdx uint16
|
|
||||||
// changes are first accumulated in here, and "flushed" to the maps above
|
|
||||||
// via invoking Finalise.
|
|
||||||
curIdxChanges constructionAccountAccess
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ConstructionAccountAccesses) StorageRead(key common.Hash) {
|
|
||||||
c.curIdxChanges.StorageRead(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ConstructionAccountAccesses) StorageWrite(key, prevVal, newVal common.Hash) {
|
|
||||||
c.curIdxChanges.StorageWrite(key, prevVal, newVal)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ConstructionAccountAccesses) BalanceChange(prev, cur *uint256.Int) {
|
|
||||||
c.curIdxChanges.BalanceChange(prev, cur)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ConstructionAccountAccesses) CodeChange(prev, cur []byte) {
|
|
||||||
c.curIdxChanges.CodeChange(prev, cur)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ConstructionAccountAccesses) NonceChange(prev, cur uint64) {
|
|
||||||
c.curIdxChanges.NonceChange(prev, cur)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Called at tx boundaries
|
|
||||||
func (c *ConstructionAccountAccesses) Finalise() {
|
|
||||||
panic("see TODO below")
|
|
||||||
// TODO: Finalise should determine if the account became non-existent. two scenarios for deletion:
|
|
||||||
// 1) account was pre-funded (but no preexisting code), some initcode ran at it and executed a SENDALL clearing the account.
|
|
||||||
// 2) account was created and deleted in the same transaction:
|
|
||||||
// * every account field started empty and ended empty, with the exception of possible net storage changes.
|
|
||||||
// * all keys that were storage writes should become storage reads
|
|
||||||
c.curIdxChanges.Finalise()
|
|
||||||
if c.curIdxChanges.code != nil {
|
|
||||||
c.CodeChanges[c.curIdx] = CodeChange{c.curIdx, c.curIdxChanges.code}
|
|
||||||
}
|
|
||||||
if c.curIdxChanges.nonce != nil {
|
|
||||||
c.NonceChanges[c.curIdx] = *c.curIdxChanges.nonce
|
|
||||||
}
|
|
||||||
if c.curIdxChanges.balance != nil {
|
|
||||||
c.BalanceChanges[c.curIdx] = c.curIdxChanges.balance
|
|
||||||
}
|
|
||||||
if c.curIdxChanges.storageMutations != nil {
|
|
||||||
for key, val := range c.curIdxChanges.storageMutations {
|
|
||||||
if _, ok := c.StorageWrites[key]; !ok {
|
|
||||||
c.StorageWrites[key] = make(map[uint16]common.Hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.StorageWrites[key][c.curIdx] = val
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if c.curIdxChanges.storageReads != nil {
|
|
||||||
for key := range c.StorageReads {
|
|
||||||
c.StorageReads[key] = struct{}{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c.curIdxChanges = constructionAccountAccess{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type constructionAccountAccess struct {
|
type constructionAccountAccess struct {
|
||||||
// stores the tx-prestate values of any account/storage values which were modified
|
|
||||||
//
|
|
||||||
// TODO: it's a bit unfortunate that the prestate.StorageWrites is reused here to
|
|
||||||
// represent the prestate values of keys which were written and it would be nice to
|
|
||||||
// somehow make it clear that the field can contain both the mutated values or the
|
|
||||||
// prestate depending on the context (or find a cleaner solution entirely, instead
|
|
||||||
// of reusing the field here)
|
|
||||||
prestate *AccountState
|
|
||||||
|
|
||||||
code []byte
|
code []byte
|
||||||
nonce *uint64
|
nonce *uint64
|
||||||
balance *uint256.Int
|
balance *uint256.Int
|
||||||
|
|
@ -206,6 +251,60 @@ type constructionAccountAccess struct {
|
||||||
storageReads map[common.Hash]struct{}
|
storageReads map[common.Hash]struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *constructionAccountAccess) Merge(other *constructionAccountAccess) {
|
||||||
|
if other.code != nil {
|
||||||
|
c.code = other.code
|
||||||
|
}
|
||||||
|
if other.nonce != nil {
|
||||||
|
c.nonce = other.nonce
|
||||||
|
}
|
||||||
|
if other.balance != nil {
|
||||||
|
c.balance = other.balance
|
||||||
|
}
|
||||||
|
if other.storageMutations != nil {
|
||||||
|
if c.storageMutations == nil {
|
||||||
|
c.storageMutations = make(map[common.Hash]common.Hash)
|
||||||
|
}
|
||||||
|
for key, val := range other.storageMutations {
|
||||||
|
c.storageMutations[key] = val
|
||||||
|
delete(c.storageReads, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if other.storageReads != nil {
|
||||||
|
for key, val := range other.storageReads {
|
||||||
|
c.storageReads[key] = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MergeReads merges accesses from a reverted execution from:
|
||||||
|
// * any reads/writes from the reverted frame which weren't mutated
|
||||||
|
// in the current frame, are merged into the current frame as reads.
|
||||||
|
func (c *constructionAccountAccess) MergeReads(other *constructionAccountAccess) {
|
||||||
|
if other.storageMutations != nil {
|
||||||
|
if c.storageReads == nil {
|
||||||
|
c.storageReads = make(map[common.Hash]struct{})
|
||||||
|
}
|
||||||
|
for key, _ := range other.storageMutations {
|
||||||
|
if _, ok := c.storageMutations[key]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
c.storageReads[key] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if other.storageReads != nil {
|
||||||
|
if c.storageReads == nil {
|
||||||
|
c.storageReads = make(map[common.Hash]struct{})
|
||||||
|
}
|
||||||
|
for key := range other.storageReads {
|
||||||
|
if _, ok := c.storageMutations[key]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
c.storageReads[key] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (c *constructionAccountAccess) StorageRead(key common.Hash) {
|
func (c *constructionAccountAccess) StorageRead(key common.Hash) {
|
||||||
if c.storageReads == nil {
|
if c.storageReads == nil {
|
||||||
c.storageReads = make(map[common.Hash]struct{})
|
c.storageReads = make(map[common.Hash]struct{})
|
||||||
|
|
@ -253,37 +352,14 @@ func (c *constructionAccountAccess) NonceChange(prev, cur uint64) {
|
||||||
c.nonce = &cur
|
c.nonce = &cur
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finalise clears any fields which had no net mutations compared to their
|
|
||||||
// prestate values.
|
|
||||||
func (c *constructionAccountAccess) Finalise() {
|
|
||||||
if c.nonce != nil && *c.prestate.Nonce == *c.nonce {
|
|
||||||
c.nonce = nil
|
|
||||||
}
|
|
||||||
if c.balance != nil && c.prestate.Balance.Eq(c.balance) {
|
|
||||||
c.balance = nil
|
|
||||||
}
|
|
||||||
if c.code != nil && bytes.Equal(c.code, c.prestate.Code) {
|
|
||||||
c.code = nil
|
|
||||||
}
|
|
||||||
if c.storageMutations != nil {
|
|
||||||
for key, val := range c.storageMutations {
|
|
||||||
if c.prestate.StorageWrites[key] == val {
|
|
||||||
delete(c.storageMutations, key)
|
|
||||||
c.storageReads[key] = struct{}{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewConstructionAccountAccesses initializes the account access object.
|
// NewConstructionAccountAccesses initializes the account access object.
|
||||||
func NewConstructionAccountAccesses(idx uint16) *ConstructionAccountAccesses {
|
func NewConstructionAccountAccesses() *ConstructionAccountAccesses {
|
||||||
return &ConstructionAccountAccesses{
|
return &ConstructionAccountAccesses{
|
||||||
StorageWrites: make(map[common.Hash]map[uint16]common.Hash),
|
StorageWrites: make(map[common.Hash]map[uint16]common.Hash),
|
||||||
StorageReads: make(map[common.Hash]struct{}),
|
StorageReads: make(map[common.Hash]struct{}),
|
||||||
BalanceChanges: make(map[uint16]*uint256.Int),
|
BalanceChanges: make(map[uint16]*uint256.Int),
|
||||||
NonceChanges: make(map[uint16]uint64),
|
NonceChanges: make(map[uint16]uint64),
|
||||||
CodeChanges: make(map[uint16]CodeChange),
|
CodeChanges: make(map[uint16]CodeChange),
|
||||||
curIdx: idx,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -302,23 +378,7 @@ func NewConstructionBlockAccessList() *ConstructionBlockAccessList {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// EnterCallScope should be invoked when the EVM execution enters a new call fram
|
// move this to access list builder
|
||||||
func (c *ConstructionBlockAccessList) EnterCallScope() {
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExitCallScope is invoked when the EVM execution exits a call frame.
|
|
||||||
// If the call frame reverted, all state changes that occurred within that
|
|
||||||
// scope (or any nested calls) are discarded. Storage reads performed in the
|
|
||||||
// nested scope(s) are retained and any storage writes that occurred are denoted
|
|
||||||
// as storage reads in the BAL.
|
|
||||||
func (c *ConstructionBlockAccessList) ExitCallScope(reverted bool) {
|
|
||||||
if reverted {
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ConstructionBlockAccessList) DiffAt(i int) *StateDiff {
|
func (c *ConstructionBlockAccessList) DiffAt(i int) *StateDiff {
|
||||||
res := &StateDiff{make(map[common.Address]*AccountState)}
|
res := &StateDiff{make(map[common.Address]*AccountState)}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -471,25 +471,32 @@ func (evm *EVM) StaticCall(caller common.Address, addr common.Address, input []b
|
||||||
|
|
||||||
// create creates a new contract using code as deployment code.
|
// create creates a new contract using code as deployment code.
|
||||||
func (evm *EVM) create(caller common.Address, code []byte, gas uint64, value *uint256.Int, address common.Address, typ OpCode) (ret []byte, createAddress common.Address, leftOverGas uint64, err error) {
|
func (evm *EVM) create(caller common.Address, code []byte, gas uint64, value *uint256.Int, address common.Address, typ OpCode) (ret []byte, createAddress common.Address, leftOverGas uint64, err error) {
|
||||||
|
// Depth check execution. Fail if we're trying to execute above the
|
||||||
|
// limit.
|
||||||
|
var nonce uint64
|
||||||
|
if evm.depth > int(params.CallCreateDepth) {
|
||||||
|
err = ErrDepth
|
||||||
|
} else if !evm.Context.CanTransfer(evm.StateDB, caller, value) {
|
||||||
|
err = ErrInsufficientBalance
|
||||||
|
} else {
|
||||||
|
nonce = evm.StateDB.GetNonce(caller)
|
||||||
|
if nonce+1 < nonce {
|
||||||
|
err = ErrNonceUintOverflow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
evm.StateDB.SetNonce(caller, nonce+1, tracing.NonceChangeContractCreator)
|
||||||
|
}
|
||||||
if evm.Config.Tracer != nil {
|
if evm.Config.Tracer != nil {
|
||||||
evm.captureBegin(evm.depth, typ, caller, address, code, gas, value.ToBig())
|
evm.captureBegin(evm.depth, typ, caller, address, code, gas, value.ToBig())
|
||||||
defer func(startGas uint64) {
|
defer func(startGas uint64) {
|
||||||
evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err)
|
evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err)
|
||||||
}(gas)
|
}(gas)
|
||||||
}
|
}
|
||||||
// Depth check execution. Fail if we're trying to execute above the
|
if err != nil {
|
||||||
// limit.
|
return nil, common.Address{}, gas, err
|
||||||
if evm.depth > int(params.CallCreateDepth) {
|
|
||||||
return nil, common.Address{}, gas, ErrDepth
|
|
||||||
}
|
}
|
||||||
if !evm.Context.CanTransfer(evm.StateDB, caller, value) {
|
|
||||||
return nil, common.Address{}, gas, ErrInsufficientBalance
|
|
||||||
}
|
|
||||||
nonce := evm.StateDB.GetNonce(caller)
|
|
||||||
if nonce+1 < nonce {
|
|
||||||
return nil, common.Address{}, gas, ErrNonceUintOverflow
|
|
||||||
}
|
|
||||||
evm.StateDB.SetNonce(caller, nonce+1, tracing.NonceChangeContractCreator)
|
|
||||||
|
|
||||||
// Charge the contract creation init gas in verkle mode
|
// Charge the contract creation init gas in verkle mode
|
||||||
if evm.chainRules.IsEIP4762 {
|
if evm.chainRules.IsEIP4762 {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue