mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-12 09:51:36 +00:00
Addresses review finding C2 (+ I5, S5, T2, T3, T12).
Before this commit, bintrieFlatCodec.ReadAccount returned the FULL
variable-length stem blob from disk while the in-memory diff-layer
buffer stored per-offset 32-byte values. The consumer,
bintrieFlatReader.Account, enforced len(basicBlob)!=32 → error, so
every disk-layer hit produced "bintrie BasicData leaf invalid length"
in production the moment the write buffer flushed. TestBintrieFlatReaderEndToEnd
did not catch this because it never forced a buffer → disk flush.
Fix: make bintrieFlatCodec.ReadAccount extract the offset from the
stem blob (mirroring ReadStorage), so the disk path and the buffer
path return the same 32-byte per-offset shape. Update
AccountCacheKey/StorageCacheKey to embed the full 32-byte key
(prefix + 31-byte stem + 1-byte offset), since caching under a
stem-only key would collapse BasicData and CodeHash into the same
slot and return the wrong value on the second hit. Update
Flush's cache-update loop to store per-offset entries from the
aggregated write set.
Design note: I considered the alternative of introducing a new
StemBlob(stem) interface method that returns the full blob synthesized
from a stem-level lookup index. Rejected because (a) the index is a
new data structure with its own consistency invariants, (b) the
per-offset approach is strictly local to the codec + reader, and (c)
the "1 Pebble read per Account" locality benefit is preserved at the
OS page cache level — both offsets at the same stem live in the same
Pebble block, so the second read is effectively free.
bintrieFlatReader.Account still does two AccountRLP lookups; the
torn-read hazard is gated by a new load-bearing invariant test,
TestBinaryHasherWritesBothBasicAndCodeHash, which asserts that
binaryHasher.updateAccount always emits both BasicData and CodeHash
leaves together. A future code-only update that broke this invariant
would fail the test.
Tests added:
* TestBintrieFlatReaderEndToEndAfterFlush — explicitly flushes via
tdb.Commit(root, false) and re-reads through a fresh StateReader.
This is the smoking-gun regression for C2.
* TestBintrieFlatReaderMultipleOffsetsPerStem — multiple offsets at
the same stem (BasicData, CodeHash, header storage slots) all
round-trip post-flush.
* TestBintrieCodecCrossFlushRMW — two Flush calls to the same stem
from different "blocks" correctly merge on disk, with prior
offsets preserved.
* TestBinaryHasherWritesBothBasicAndCodeHash — locks down the hasher
co-write invariant that bintrieFlatReader.Account relies on.
Existing tests updated to match the new per-offset ReadAccount
semantics:
* TestBintrieCodecAccountRoundTrip, TestBintrieCodecMultipleWritesSameStem,
TestBintrieCodecDeleteAccount — now read per-offset rather than
calling extractStemOffset on the raw blob.
* TestBintrieCodecCacheKeysDisjoint — additionally verifies two
offsets at the same stem produce distinct cache keys.
Error messages in bintrieFlatReader now include address and length
context (S5).
|
||
|---|---|---|
| .. | ||
| buffer.go | ||
| config.go | ||
| context.go | ||
| database.go | ||
| database_test.go | ||
| difflayer.go | ||
| difflayer_test.go | ||
| disklayer.go | ||
| errors.go | ||
| execute.go | ||
| fileutils_unix.go | ||
| fileutils_windows.go | ||
| flat_codec.go | ||
| flat_codec_bintrie.go | ||
| flat_codec_bintrie_test.go | ||
| flush.go | ||
| generate.go | ||
| generate_bintrie.go | ||
| generate_bintrie_test.go | ||
| generate_test.go | ||
| history.go | ||
| history_index.go | ||
| history_index_block.go | ||
| history_index_block_test.go | ||
| history_index_iterator.go | ||
| history_index_iterator_test.go | ||
| history_index_pruner.go | ||
| history_index_pruner_test.go | ||
| history_index_test.go | ||
| history_indexer.go | ||
| history_indexer_state.go | ||
| history_indexer_test.go | ||
| history_inspect.go | ||
| history_reader.go | ||
| history_reader_test.go | ||
| history_state.go | ||
| history_state_test.go | ||
| history_trienode.go | ||
| history_trienode_test.go | ||
| history_trienode_utils.go | ||
| history_trienode_utils_test.go | ||
| holdable_iterator.go | ||
| holdable_iterator_test.go | ||
| iterator.go | ||
| iterator_binary.go | ||
| iterator_fast.go | ||
| iterator_test.go | ||
| journal.go | ||
| layertree.go | ||
| layertree_test.go | ||
| lookup.go | ||
| metrics.go | ||
| nodes.go | ||
| nodes_test.go | ||
| reader.go | ||
| states.go | ||
| states_test.go | ||
| stem_blob.go | ||
| stem_blob_test.go | ||
| verifier.go | ||