-
Notifications
You must be signed in to change notification settings - Fork 886
balances and bools #4093
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
balances and bools #4093
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -391,7 +391,7 @@ func (s *memoryStoreSnapshot) AccountExists(address gigastore.Address) bool { | |
| return balance != nil && balance.Sign() != 0 || s.store.base.GetNonce(address) != 0 || len(s.store.base.GetCode(address)) != 0 | ||
| } | ||
|
|
||
| func (s *memoryStoreSnapshot) GetStorage(address gigastore.Address, slot gigastore.Hash) gigastore.Hash { | ||
| func (s *memoryStoreSnapshot) GetStorage(address gigastore.Address, slot gigastore.Hash) (gigastore.Hash, bool) { | ||
| s.requireOpen() | ||
| key := memoryStoreStorageKey{address: address, slot: slot} | ||
| s.store.mu.RLock() | ||
|
|
@@ -400,70 +400,83 @@ func (s *memoryStoreSnapshot) GetStorage(address gigastore.Address, slot gigasto | |
| s.store.mu.RUnlock() | ||
| if valueOK && (!clearOK || value.height >= clearHeight) { | ||
| if value.delete { | ||
| return gigastore.Hash{} | ||
| return gigastore.Hash{}, false | ||
| } | ||
| return value.value | ||
| return value.value, true | ||
| } | ||
| if clearOK { | ||
| return gigastore.Hash{} | ||
| return gigastore.Hash{}, false | ||
| } | ||
| return s.store.base.GetState(address, slot) | ||
| baseValue := s.store.base.GetState(address, slot) | ||
| // The base reader reports no presence of its own, so an unset slot is indistinguishable from one | ||
| // holding zero. | ||
| return baseValue, baseValue != (gigastore.Hash{}) | ||
| } | ||
|
|
||
| func (s *memoryStoreSnapshot) GetBalance(address gigastore.Address) gigastore.Hash { | ||
| func (s *memoryStoreSnapshot) GetBalance(address gigastore.Address) (gigastore.Hash, bool) { | ||
| s.requireOpen() | ||
| s.store.mu.RLock() | ||
| value, ok := latestMemoryStoreValue(s.store.balances[address], s.height) | ||
| s.store.mu.RUnlock() | ||
| if ok { | ||
| return value.value | ||
| return value.value, true | ||
| } | ||
| var balance common.Hash | ||
| baseBalance := s.store.base.GetBalance(address) | ||
| if baseBalance != nil { | ||
| if err := validateMemoryStoreBalance(baseBalance); err != nil { | ||
| panic(err) | ||
| } | ||
| baseBalance.FillBytes(balance[:]) | ||
| if baseBalance == nil { | ||
| return gigastore.Hash{}, false | ||
| } | ||
| return balance | ||
| if err := validateMemoryStoreBalance(baseBalance); err != nil { | ||
| panic(err) | ||
| } | ||
| var balance common.Hash | ||
| baseBalance.FillBytes(balance[:]) | ||
| // The base reader reports no presence of its own, so an account with no balance is | ||
| // indistinguishable from one holding zero. | ||
| return balance, baseBalance.Sign() != 0 | ||
| } | ||
|
|
||
| func (s *memoryStoreSnapshot) GetNonce(address gigastore.Address) uint64 { | ||
| func (s *memoryStoreSnapshot) GetNonce(address gigastore.Address) (uint64, bool) { | ||
| s.requireOpen() | ||
| s.store.mu.RLock() | ||
| value, ok := latestMemoryStoreValue(s.store.nonces[address], s.height) | ||
| s.store.mu.RUnlock() | ||
| if ok { | ||
| return value.value | ||
| return value.value, true | ||
| } | ||
| return s.store.base.GetNonce(address) | ||
| baseNonce := s.store.base.GetNonce(address) | ||
| // The base reader reports no presence of its own, so a missing account is indistinguishable from | ||
| // one whose nonce is zero. | ||
| return baseNonce, baseNonce != 0 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [suggestion] This contradicts the contract the PR just wrote for
The mirror-image case is |
||
| } | ||
|
|
||
| func (s *memoryStoreSnapshot) GetCodeSize(address gigastore.Address) int { | ||
| return len(s.GetCode(address)) | ||
| func (s *memoryStoreSnapshot) GetCodeSize(address gigastore.Address) (int, bool) { | ||
| code, ok := s.GetCode(address) | ||
| return len(code), ok | ||
| } | ||
|
|
||
| func (s *memoryStoreSnapshot) GetCodeHash(address gigastore.Address) gigastore.Hash { | ||
| s.requireOpen() | ||
| if !s.AccountExists(address) { | ||
| return gigastore.Hash{} | ||
| func (s *memoryStoreSnapshot) GetCodeHash(address gigastore.Address) (gigastore.Hash, bool) { | ||
| code, ok := s.GetCode(address) | ||
| if !ok { | ||
| return gigastore.Hash{}, false | ||
| } | ||
| return crypto.Keccak256Hash(s.GetCode(address)) | ||
| return crypto.Keccak256Hash(code), true | ||
| } | ||
|
|
||
| func (s *memoryStoreSnapshot) GetCode(address gigastore.Address) []byte { | ||
| func (s *memoryStoreSnapshot) GetCode(address gigastore.Address) ([]byte, bool) { | ||
| s.requireOpen() | ||
| s.store.mu.RLock() | ||
| value, ok := latestMemoryStoreValue(s.store.code[address], s.height) | ||
| s.store.mu.RUnlock() | ||
| if ok { | ||
| if value.delete { | ||
| return nil | ||
| return nil, false | ||
| } | ||
| return cloneBytes(value.value) | ||
| return cloneBytes(value.value), true | ||
| } | ||
| return cloneBytes(s.store.base.GetCode(address)) | ||
| baseCode := s.store.base.GetCode(address) | ||
| // The base reader reports no presence of its own, so an account with no code is indistinguishable | ||
| // from one holding empty code. | ||
| return cloneBytes(baseCode), len(baseCode) != 0 | ||
| } | ||
|
|
||
| func (s *memoryStoreSnapshot) GetBlockHeight() int64 { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -25,6 +25,7 @@ var ( | |
| codeKeyPrefix = []byte{0x07} | ||
| codeHashKeyPrefix = []byte{0x08} | ||
| nonceKeyPrefix = []byte{0x0a} | ||
| balanceKeyPrefix = []byte{0x21} | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [suggestion] This is the second declaration of
|
||
| ) | ||
|
|
||
| // StateKeyPrefix returns the storage state key prefix (0x03). | ||
|
|
@@ -34,18 +35,21 @@ func StateKeyPrefix() []byte { return stateKeyPrefix } | |
| // EVMKeyKind identifies an EVM key family. | ||
| type EVMKeyKind uint8 | ||
|
|
||
| // These values are in-memory routing tags, renumbered whenever a kind is added. Writing one into a | ||
| // key, a value, or any other stored or wire format is forbidden. | ||
| const ( | ||
| EVMKeyEmpty EVMKeyKind = iota // Returned only for zero-length keys | ||
| EVMKeyNonce // Stripped key: 20-byte address | ||
| EVMKeyCodeHash // Stripped key: 20-byte address | ||
| EVMKeyBalance // Stripped key: 20-byte address | ||
| EVMKeyCode // Stripped key: 20-byte address | ||
| EVMKeyStorage // Stripped key: addr||slot (20+32 bytes) | ||
| EVMKeyMisc // Full original key preserved (address mappings, codesize, etc.) | ||
| ) | ||
|
|
||
| // ParseEVMKey parses an EVM key from the x/evm store keyspace. | ||
| // | ||
| // For optimized keys (nonce, code, codehash, storage), keyBytes is the stripped key. | ||
| // For optimized keys (nonce, code, codehash, storage, balance), keyBytes is the stripped key. | ||
| // For misc keys (all other EVM data including codesize), keyBytes is the full original key. | ||
| // Only returns EVMKeyEmpty for zero-length keys. | ||
| func ParseEVMKey(key []byte) (kind EVMKeyKind, keyBytes []byte) { | ||
|
|
@@ -77,6 +81,12 @@ func ParseEVMKey(key []byte) (kind EVMKeyKind, keyBytes []byte) { | |
| return EVMKeyMisc, key | ||
| } | ||
| return EVMKeyStorage, key[len(stateKeyPrefix):] | ||
|
|
||
| case bytes.HasPrefix(key, balanceKeyPrefix): | ||
| if len(key) != len(balanceKeyPrefix)+AddressLen { | ||
| return EVMKeyMisc, key | ||
| } | ||
| return EVMKeyBalance, key[len(balanceKeyPrefix):] | ||
| } | ||
|
|
||
| // All other EVM keys go to the misc store (address mappings, codesize, etc.) | ||
|
|
@@ -95,6 +105,8 @@ func EVMKeyPrefixByte(kind EVMKeyKind) (byte, bool) { | |
| return codeHashKeyPrefix[0], true | ||
| case EVMKeyCode: | ||
| return codeKeyPrefix[0], true | ||
| case EVMKeyBalance: | ||
| return balanceKeyPrefix[0], true | ||
| default: | ||
| return 0, false | ||
| } | ||
|
|
@@ -123,7 +135,7 @@ func InternalKeyLen(kind EVMKeyKind) int { | |
| switch kind { | ||
| case EVMKeyStorage: | ||
| return AddressLen + slotLen // 52 bytes | ||
| case EVMKeyNonce, EVMKeyCodeHash, EVMKeyCode: | ||
| case EVMKeyNonce, EVMKeyCodeHash, EVMKeyCode, EVMKeyBalance: | ||
| return AddressLen // 20 bytes | ||
| default: | ||
| return 0 | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Zero overlay balance reported present
Medium Severity
The memory-store overlay reports a balance as present for any latest write, including a zeroed or deleted value. FlatKV and the new read contract treat a zero balance as not stored, so
GetBalance'sokdisagrees across implementations when an account is drained or a balance is removed.Reviewed by Cursor Bugbot for commit 792b4af. Configure here.