Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions execution/state/versionedio.go
Original file line number Diff line number Diff line change
Expand Up @@ -1336,7 +1336,8 @@ func (vr *versionedStateReader) TracePrefix() string {
}

func (vr *versionedStateReader) ReadAccountData(address accounts.Address) (*accounts.Account, error) {
if r, ok := vr.reads.GetAddress(address); ok && r.Val != nil && !r.Val.IsNil() {
r, recorded := vr.reads.GetAddress(address)
if recorded && r.Val != nil && !r.Val.IsNil() {
account := r.Val.Account()
updated := vr.applyVersionedUpdates(address, *account)
return &updated, nil
Expand Down Expand Up @@ -1387,7 +1388,9 @@ func (vr *versionedStateReader) ReadAccountData(address accounts.Address) (*acco
}
}

if vr.stateReader != nil {
// A recorded AddressPath read with no account is the tx's own conclusion that
// the address holds nothing; the domain cannot say otherwise.
if vr.stateReader != nil && !recorded {
account, err := vr.stateReader.ReadAccountData(address)

if err != nil {
Expand Down
53 changes: 53 additions & 0 deletions execution/state/versionedio_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package state

import (
"errors"
"fmt"
"sort"
"testing"
Expand Down Expand Up @@ -1596,6 +1597,58 @@ func TestVersionedUpdates_EstimateCellConsumed(t *testing.T) {
require.Equal(t, newStorage, storageGot, "Estimate-cell storage must be consumed, not stale")
}

// countingStateReader records how many account reads reached the domain.
type countingStateReader struct {
minimalStateReader
acc *accounts.Account
accountReads int
failOnAccounts bool
}

func (r *countingStateReader) ReadAccountData(addr accounts.Address) (*accounts.Account, error) {
r.accountReads++
if r.failOnAccounts {
return nil, errors.New("domain must not be consulted")
}
return r.acc, nil
}

// A tx that read an address and found no account records the AddressPath entry
// header-only; that entry is the answer, not a gap to fill from the domain.
func TestVersionedStateReader_RecordedAbsentSkipsDomain(t *testing.T) {
t.Parallel()

addr := accounts.InternAddress(common.HexToAddress("0xab5e17"))
reads := ReadSet{}
reads.SetAddress(addr, VersionedRead[AccountView]{
ReadHeader: ReadHeader{Source: StorageRead, Version: UnknownVersion},
})

reader := &countingStateReader{failOnAccounts: true}
vr := NewVersionedStateReader(3, reads, NewVersionMap(nil), reader)

got, err := vr.ReadAccountData(addr)
require.NoError(t, err)
require.Nil(t, got)
require.Zero(t, reader.accountReads, "recorded-absent read must not reach the domain")
}

func TestVersionedStateReader_UnrecordedAddressReadsDomain(t *testing.T) {
t.Parallel()

addr := accounts.InternAddress(common.HexToAddress("0xc01d"))
domainAcc := accounts.NewAccount()
domainAcc.Nonce = 9
reader := &countingStateReader{acc: &domainAcc}
vr := NewVersionedStateReader(3, ReadSet{}, NewVersionMap(nil), reader)

got, err := vr.ReadAccountData(addr)
require.NoError(t, err)
require.NotNil(t, got)
require.Equal(t, uint64(9), got.Nonce)
require.Equal(t, 1, reader.accountReads)
}

// writeSetFixture builds a WriteSet covering every path, multiple addresses and
// storage keys — the input for the iteration closed-loop tests.
func writeSetFixture() (*WriteSet, []string) {
Expand Down
Loading