2020-12-08 09:44:29 +00:00
package shards
import (
"bytes"
"container/heap"
"fmt"
2023-10-21 23:17:18 +00:00
"github.com/ledgerwatch/erigon-lib/metrics"
2020-12-08 09:44:29 +00:00
"unsafe"
2021-02-21 18:41:59 +00:00
"github.com/c2h5oh/datasize"
2020-12-08 09:44:29 +00:00
"github.com/google/btree"
"github.com/holiman/uint256"
2023-01-13 18:12:18 +00:00
libcommon "github.com/ledgerwatch/erigon-lib/common"
2021-05-20 18:25:53 +00:00
"github.com/ledgerwatch/erigon/core/types/accounts"
2020-12-08 09:44:29 +00:00
)
// LRU state cache consists of two structures - B-Tree and binary heap
// Every element is marked either as Read, Updated, or Deleted via flags
2021-02-21 18:41:59 +00:00
// Metrics
var (
2021-07-29 10:27:46 +00:00
AccRead = metrics . GetOrCreateCounter ( ` cache_total { target="acc_read"} ` )
StRead = metrics . GetOrCreateCounter ( ` cache_total { target="st_read"} ` )
WritesRead = metrics . GetOrCreateCounter ( ` cache_total { target="write"} ` )
2021-02-21 18:41:59 +00:00
)
2020-12-08 09:44:29 +00:00
const (
2021-02-21 18:41:59 +00:00
ModifiedFlag uint16 = 1 // Set when the item is different seek what is last committed to the database
2020-12-08 09:44:29 +00:00
AbsentFlag uint16 = 2 // Set when the item is absent in the state
DeletedFlag uint16 = 4 // Set when the item is marked for deletion, even though it might have the value in it
UnprocessedFlag uint16 = 8 // Set when there is a modification in the item that invalidates merkle root calculated previously
)
// Sizes of B-tree items for the purposes of keeping track of the size of reads and writes
// The sizes of the nodes of the B-tree are not accounted for, because their are private to the `btree` package
2021-02-21 18:41:59 +00:00
// +16 means - each item has 2 words overhead: 1 for interface, 1 for pointer to item.
2020-12-08 09:44:29 +00:00
const (
2021-02-21 18:41:59 +00:00
accountItemSize = int ( unsafe . Sizeof ( AccountItem { } ) + 16 )
accountWriteItemSize = int ( unsafe . Sizeof ( AccountWriteItem { } ) + 16 ) + accountItemSize
storageItemSize = int ( unsafe . Sizeof ( StorageItem { } ) + 16 )
storageWriteItemSize = int ( unsafe . Sizeof ( StorageWriteItem { } ) + 16 ) + storageItemSize
codeItemSize = int ( unsafe . Sizeof ( CodeItem { } ) + 16 )
codeWriteItemSize = int ( unsafe . Sizeof ( CodeWriteItem { } ) + 16 ) + codeItemSize
2020-12-08 09:44:29 +00:00
)
2021-02-21 18:41:59 +00:00
// AccountSeek allows to traverse sub-tree
type AccountSeek struct {
seek [ ] byte
}
// StorageSeek allows to traverse sub-tree
type StorageSeek struct {
2023-01-13 18:12:18 +00:00
addrHash libcommon . Hash
2021-02-21 18:41:59 +00:00
incarnation uint64
seek [ ] byte
}
// AccountItem is an element in the `readWrites` B-tree representing an Ethereum account. It can mean either value
// just read seek the database and cache (read), or value that is different seek what the last committed value
// in the DB is (write). Reads can be removed or evicted seek the B-tree at any time, because this
// does not hurt the consistency. Writes cannot be removed or evicted one by one, therefore they can
// either be deleted all together, or committed all together and turned into reads.
2020-12-08 09:44:29 +00:00
type AccountItem struct {
sequence int
queuePos int
flags uint16
2023-01-13 18:12:18 +00:00
addrHash libcommon . Hash
2020-12-08 09:44:29 +00:00
account accounts . Account
}
2021-02-21 18:41:59 +00:00
// AccountWriteItem is an item in the `writes` B-tree. As can be seen, it always references a corresponding
// `AccountItem`. There can be `AccountItem` without corresponding `AccountWriteItem` (in that case `AccountItem`
// represents a cached read), but there cannot be `AccountWriteItem` without a corresponding `AccountItem`.
// Such pair represents an account that has been modified in the cache, but the modification has not been committed
// to the database yet. The correspondence of an `ai AccountItem` and an `awi AccountWriteItem` implies that
2020-12-08 09:44:29 +00:00
// `keccak(awi.address) == ai.addrHash`.
type AccountWriteItem struct {
2023-01-13 18:12:18 +00:00
address libcommon . Address
2020-12-08 09:44:29 +00:00
ai * AccountItem
}
type StorageItem struct {
sequence int
queuePos int
flags uint16
2023-01-13 18:12:18 +00:00
addrHash libcommon . Hash
2020-12-08 09:44:29 +00:00
incarnation uint64
2023-01-13 18:12:18 +00:00
locHash libcommon . Hash
2020-12-08 09:44:29 +00:00
value uint256 . Int
}
type StorageWriteItem struct {
2023-01-13 18:12:18 +00:00
address libcommon . Address
location libcommon . Hash
2020-12-08 09:44:29 +00:00
si * StorageItem
}
type CodeItem struct {
sequence int
queuePos int
flags uint16
2023-01-13 18:12:18 +00:00
addrHash libcommon . Hash
2020-12-08 09:44:29 +00:00
incarnation uint64
code [ ] byte
}
type CodeWriteItem struct {
2023-01-13 18:12:18 +00:00
address libcommon . Address
2020-12-08 09:44:29 +00:00
ci * CodeItem
}
type CacheItem interface {
btree . Item
GetSequence ( ) int
SetSequence ( sequence int )
GetSize ( ) int
GetQueuePos ( ) int
SetQueuePos ( pos int )
HasFlag ( flag uint16 ) bool // Check if specified flag is set
SetFlags ( flags uint16 ) // Set specified flags, but leaves other flags alone
ClearFlags ( flags uint16 ) // Clear specified flags, but laves other flags alone
2021-02-21 18:41:59 +00:00
CopyValueFrom ( item CacheItem ) // Copy value (not key) seek given item
2020-12-08 09:44:29 +00:00
HasPrefix ( prefix CacheItem ) bool // Whether this item has specified item as a prefix
}
type CacheWriteItem interface {
btree . Item
GetCacheItem ( ) CacheItem
SetCacheItem ( item CacheItem )
GetSize ( ) int
}
func compare_code_code ( i1 * CodeItem , i2 * CodeItem ) int {
c := bytes . Compare ( i1 . addrHash . Bytes ( ) , i2 . addrHash . Bytes ( ) )
if c != 0 {
return c
}
if i1 . incarnation == i2 . incarnation {
return 0
}
if i1 . incarnation < i2 . incarnation {
return - 1
}
return 1
}
2021-02-21 18:41:59 +00:00
func ( r * AccountSeek ) Less ( than btree . Item ) bool {
2020-12-08 09:44:29 +00:00
switch i := than . ( type ) {
case * AccountItem :
2021-02-21 18:41:59 +00:00
return bytes . Compare ( r . seek , i . addrHash . Bytes ( ) ) < 0
case * AccountWriteItem :
return bytes . Compare ( r . seek , i . ai . addrHash . Bytes ( ) ) < 0
2020-12-08 09:44:29 +00:00
default :
2021-02-21 18:41:59 +00:00
panic ( fmt . Sprintf ( "unexpected type: %T" , than ) )
2020-12-08 09:44:29 +00:00
}
}
2021-02-21 18:41:59 +00:00
func ( r * StorageSeek ) Less ( than btree . Item ) bool {
2020-12-08 09:44:29 +00:00
switch i := than . ( type ) {
2021-02-21 18:41:59 +00:00
case * StorageItem :
c := bytes . Compare ( r . addrHash . Bytes ( ) , i . addrHash . Bytes ( ) )
if c != 0 {
return c < 0
}
if r . incarnation < i . incarnation {
2020-12-08 09:44:29 +00:00
return true
}
2021-02-21 18:41:59 +00:00
return bytes . Compare ( r . seek , i . locHash . Bytes ( ) ) < 0
case * StorageWriteItem :
c := bytes . Compare ( r . addrHash . Bytes ( ) , i . si . addrHash . Bytes ( ) )
if c != 0 {
return c < 0
}
if r . incarnation < i . si . incarnation {
2020-12-08 09:44:29 +00:00
return true
}
2021-02-21 18:41:59 +00:00
return bytes . Compare ( r . seek , i . si . locHash . Bytes ( ) ) < 0
2020-12-08 09:44:29 +00:00
default :
2021-02-21 18:41:59 +00:00
panic ( fmt . Sprintf ( "unexpected type: %T" , than ) )
2020-12-08 09:44:29 +00:00
}
}
2021-02-21 18:41:59 +00:00
func ( ai * AccountItem ) Less ( than btree . Item ) bool {
switch i := than . ( type ) {
case * AccountItem :
return bytes . Compare ( ai . addrHash . Bytes ( ) , i . addrHash . Bytes ( ) ) < 0
case * AccountWriteItem :
return bytes . Compare ( ai . addrHash . Bytes ( ) , i . ai . addrHash . Bytes ( ) ) < 0
case * AccountSeek :
return bytes . Compare ( ai . addrHash . Bytes ( ) , i . seek ) < 0
default :
panic ( fmt . Sprintf ( "unexpected type: %T" , than ) )
}
2020-12-08 09:44:29 +00:00
}
2021-02-21 18:41:59 +00:00
func ( awi * AccountWriteItem ) GetCacheItem ( ) CacheItem { return awi . ai }
func ( awi * AccountWriteItem ) SetCacheItem ( item CacheItem ) { awi . ai = item . ( * AccountItem ) }
func ( awi * AccountWriteItem ) GetSize ( ) int { return accountWriteItemSize }
func ( awi * AccountWriteItem ) Less ( than btree . Item ) bool {
return awi . ai . Less ( than )
2020-12-08 09:44:29 +00:00
}
2021-02-21 18:41:59 +00:00
func ( ai * AccountItem ) GetSequence ( ) int { return ai . sequence }
func ( ai * AccountItem ) SetSequence ( sequence int ) { ai . sequence = sequence }
func ( ai * AccountItem ) GetSize ( ) int { return accountItemSize }
func ( ai * AccountItem ) GetQueuePos ( ) int { return ai . queuePos }
func ( ai * AccountItem ) SetQueuePos ( pos int ) { ai . queuePos = pos }
func ( ai * AccountItem ) HasFlag ( flag uint16 ) bool { return ai . flags & flag != 0 }
func ( ai * AccountItem ) SetFlags ( flags uint16 ) { ai . flags |= flags }
func ( ai * AccountItem ) ClearFlags ( flags uint16 ) { ai . flags &^= flags }
func ( ai * AccountItem ) String ( ) string { return fmt . Sprintf ( "AccountItem(addrHash=%x)" , ai . addrHash ) }
2020-12-08 09:44:29 +00:00
func ( ai * AccountItem ) CopyValueFrom ( item CacheItem ) {
otherAi , ok := item . ( * AccountItem )
if ! ok {
panic ( fmt . Sprintf ( "expected AccountItem, got %T" , item ) )
}
ai . account . Copy ( & otherAi . account )
}
2021-02-21 18:41:59 +00:00
func ( swi * StorageWriteItem ) Less ( than btree . Item ) bool {
return swi . si . Less ( than . ( * StorageWriteItem ) . si )
2020-12-08 09:44:29 +00:00
}
2021-02-21 18:41:59 +00:00
func ( swi * StorageWriteItem ) GetCacheItem ( ) CacheItem { return swi . si }
func ( swi * StorageWriteItem ) SetCacheItem ( item CacheItem ) { swi . si = item . ( * StorageItem ) }
func ( swi * StorageWriteItem ) GetSize ( ) int { return storageWriteItemSize }
2020-12-08 09:44:29 +00:00
2021-02-21 18:41:59 +00:00
func ( si * StorageItem ) Less ( than btree . Item ) bool {
2020-12-08 09:44:29 +00:00
switch i := than . ( type ) {
2021-02-21 18:41:59 +00:00
case * StorageItem :
c := bytes . Compare ( si . addrHash . Bytes ( ) , i . addrHash . Bytes ( ) )
if c != 0 {
return c < 0
}
if si . incarnation < i . incarnation {
return true
2020-12-08 09:44:29 +00:00
}
2021-02-21 18:41:59 +00:00
return bytes . Compare ( si . locHash . Bytes ( ) , i . locHash . Bytes ( ) ) < 0
2020-12-08 09:44:29 +00:00
case * StorageWriteItem :
2021-02-21 18:41:59 +00:00
c := bytes . Compare ( si . addrHash . Bytes ( ) , i . si . addrHash . Bytes ( ) )
if c != 0 {
return c < 0
2020-12-08 09:44:29 +00:00
}
2021-02-21 18:41:59 +00:00
if si . incarnation < i . si . incarnation {
return true
2020-12-08 09:44:29 +00:00
}
2021-02-21 18:41:59 +00:00
return bytes . Compare ( si . locHash . Bytes ( ) , i . si . locHash . Bytes ( ) ) < 0
case * StorageSeek :
c := bytes . Compare ( si . addrHash . Bytes ( ) , i . addrHash . Bytes ( ) )
if c != 0 {
return c < 0
}
if si . incarnation < i . incarnation {
return true
}
return bytes . Compare ( si . locHash . Bytes ( ) , i . seek ) < 0
2020-12-08 09:44:29 +00:00
default :
2021-02-21 18:41:59 +00:00
panic ( fmt . Sprintf ( "unexpected type: %T" , than ) )
2020-12-08 09:44:29 +00:00
}
}
2021-02-21 18:41:59 +00:00
func ( si * StorageItem ) GetSequence ( ) int { return si . sequence }
func ( si * StorageItem ) SetSequence ( sequence int ) { si . sequence = sequence }
func ( si * StorageItem ) GetSize ( ) int { return storageItemSize }
func ( si * StorageItem ) GetQueuePos ( ) int { return si . queuePos }
func ( si * StorageItem ) SetQueuePos ( pos int ) { si . queuePos = pos }
func ( si * StorageItem ) HasFlag ( flag uint16 ) bool { return si . flags & flag != 0 }
func ( si * StorageItem ) SetFlags ( flags uint16 ) { si . flags |= flags }
func ( si * StorageItem ) ClearFlags ( flags uint16 ) { si . flags &^= flags }
2020-12-08 09:44:29 +00:00
func ( si * StorageItem ) String ( ) string {
return fmt . Sprintf ( "StorageItem(addrHash=%x,incarnation=%d,locHash=%x)" , si . addrHash , si . incarnation , si . locHash )
}
func ( si * StorageItem ) CopyValueFrom ( item CacheItem ) {
otherSi , ok := item . ( * StorageItem )
if ! ok {
panic ( fmt . Sprintf ( "expected StorageCacheItem, got %T" , item ) )
}
si . value . Set ( & otherSi . value )
}
func ( ci * CodeItem ) Less ( than btree . Item ) bool {
2021-02-21 18:41:59 +00:00
return compare_code_code ( ci , than . ( * CodeItem ) ) < 0
2020-12-08 09:44:29 +00:00
}
func ( cwi * CodeWriteItem ) Less ( than btree . Item ) bool {
2021-02-21 18:41:59 +00:00
i := than . ( * CodeWriteItem )
c := bytes . Compare ( cwi . address . Bytes ( ) , i . address . Bytes ( ) )
if c == 0 {
return cwi . ci . incarnation < i . ci . incarnation
2020-12-08 09:44:29 +00:00
}
2021-02-21 18:41:59 +00:00
return c < 0
}
func ( cwi * CodeWriteItem ) GetCacheItem ( ) CacheItem { return cwi . ci }
func ( cwi * CodeWriteItem ) SetCacheItem ( item CacheItem ) { cwi . ci = item . ( * CodeItem ) }
func ( cwi * CodeWriteItem ) GetSize ( ) int { return codeWriteItemSize + len ( cwi . ci . code ) }
func ( ci * CodeItem ) GetSequence ( ) int { return ci . sequence }
func ( ci * CodeItem ) SetSequence ( sequence int ) { ci . sequence = sequence }
func ( ci * CodeItem ) GetSize ( ) int { return codeItemSize + len ( ci . code ) }
func ( ci * CodeItem ) GetQueuePos ( ) int { return ci . queuePos }
func ( ci * CodeItem ) SetQueuePos ( pos int ) { ci . queuePos = pos }
func ( ci * CodeItem ) HasFlag ( flag uint16 ) bool { return ci . flags & flag != 0 }
func ( ci * CodeItem ) SetFlags ( flags uint16 ) { ci . flags |= flags }
func ( ci * CodeItem ) ClearFlags ( flags uint16 ) { ci . flags &^= flags }
2020-12-08 09:44:29 +00:00
func ( ci * CodeItem ) String ( ) string {
return fmt . Sprintf ( "CodeItem(addrHash=%x,incarnation=%d)" , ci . addrHash , ci . incarnation )
}
func ( ci * CodeItem ) CopyValueFrom ( item CacheItem ) {
otherCi , ok := item . ( * CodeItem )
if ! ok {
panic ( fmt . Sprintf ( "expected CodeCacheItem, got %T" , item ) )
}
ci . code = make ( [ ] byte , len ( otherCi . code ) )
copy ( ci . code , otherCi . code )
}
// Heap for reads
type ReadHeap struct {
items [ ] CacheItem
}
2021-02-21 18:41:59 +00:00
func ( rh ReadHeap ) Len ( ) int { return len ( rh . items ) }
func ( rh ReadHeap ) Less ( i , j int ) bool { return rh . items [ i ] . GetSequence ( ) < rh . items [ j ] . GetSequence ( ) }
2020-12-08 09:44:29 +00:00
func ( rh ReadHeap ) Swap ( i , j int ) {
// Swap queue positions in the B-tree leaves too
rh . items [ i ] . SetQueuePos ( j )
rh . items [ j ] . SetQueuePos ( i )
rh . items [ i ] , rh . items [ j ] = rh . items [ j ] , rh . items [ i ]
}
func ( rh * ReadHeap ) Push ( x interface { } ) {
// Push and Pop use pointer receivers because they modify the slice's length,
// not just its contents.
cacheItem := x . ( CacheItem )
cacheItem . SetQueuePos ( len ( rh . items ) )
rh . items = append ( rh . items , cacheItem )
}
func ( rh * ReadHeap ) Pop ( ) interface { } {
cacheItem := rh . items [ len ( rh . items ) - 1 ]
rh . items = rh . items [ : len ( rh . items ) - 1 ]
return cacheItem
}
// StateCache is the structure containing B-trees and priority queues for the state cache
type StateCache struct {
2021-02-21 18:41:59 +00:00
readWrites [ 5 ] * btree . BTree // Mixed reads and writes
writes [ 5 ] * btree . BTree // Only writes for the effective iteration
readQueue [ 5 ] ReadHeap // Priority queue of read elements eligible for eviction (sorted by sequence)
limit datasize . ByteSize // Total size of the readQueue (if new item causes the size to go over the limit, some existing items are evicted)
2020-12-08 09:44:29 +00:00
readSize int
writeSize int
2021-02-21 18:41:59 +00:00
sequence int // Current sequence assigned to any item that has been "touched" (created, deleted, read). Incremented after every touch
unprocQueue [ 5 ] UnprocessedHeap // Priority queue of items appeared since last root calculation processing (sorted by the keys - addrHash, incarnation, locHash)
}
func id ( a interface { } ) uint8 {
switch a . ( type ) {
case * AccountItem , * AccountWriteItem , * AccountSeek :
return 0
case * StorageItem , * StorageWriteItem , * StorageSeek :
return 1
case * CodeItem , * CodeWriteItem :
return 2
case * AccountHashItem , * AccountHashWriteItem :
return 3
case * StorageHashItem , * StorageHashWriteItem :
return 4
default :
panic ( fmt . Sprintf ( "unexpected type: %T" , a ) )
}
2020-12-08 09:44:29 +00:00
}
// NewStateCache create a new state cache based on the B-trees of specific degree. The second and the third parameters are the limit on the number of reads and writes to cache, respectively
2021-02-21 18:41:59 +00:00
func NewStateCache ( degree int , limit datasize . ByteSize ) * StateCache {
2020-12-08 09:44:29 +00:00
var sc StateCache
sc . limit = limit
2021-02-21 18:41:59 +00:00
for i := 0 ; i < len ( sc . readWrites ) ; i ++ {
sc . readWrites [ i ] = btree . New ( degree )
}
for i := 0 ; i < len ( sc . writes ) ; i ++ {
sc . writes [ i ] = btree . New ( degree )
}
for i := 0 ; i < len ( sc . readQueue ) ; i ++ {
heap . Init ( & sc . readQueue [ i ] )
}
for i := 0 ; i < len ( sc . unprocQueue ) ; i ++ {
heap . Init ( & sc . unprocQueue [ i ] )
}
2020-12-08 09:44:29 +00:00
return & sc
}
2021-02-10 17:04:47 +00:00
// Clone creates a clone cache which can be modified independently, but it shares the parts of the cache that are common
func ( sc * StateCache ) Clone ( ) * StateCache {
var clone StateCache
2021-02-21 18:41:59 +00:00
for i := range clone . readWrites {
clone . readWrites [ i ] = sc . readWrites [ i ] . Clone ( )
clone . writes [ i ] = sc . writes [ i ] . Clone ( )
clone . limit = sc . limit
heap . Init ( & clone . readQueue [ i ] )
heap . Init ( & clone . unprocQueue [ i ] )
}
2021-02-10 17:04:47 +00:00
return & clone
}
2020-12-08 09:44:29 +00:00
func ( sc * StateCache ) get ( key btree . Item ) ( CacheItem , bool ) {
2021-07-29 10:27:46 +00:00
WritesRead . Inc ( )
2021-02-21 18:41:59 +00:00
item := sc . readWrites [ id ( key ) ] . Get ( key )
2020-12-08 09:44:29 +00:00
if item == nil {
return nil , false
}
cacheItem := item . ( CacheItem )
if cacheItem . HasFlag ( DeletedFlag ) || cacheItem . HasFlag ( AbsentFlag ) {
return nil , true
}
return cacheItem , true
}
// GetAccount searches and account with given address, without modifying any structures
// Second return value is true if such account is found
func ( sc * StateCache ) GetAccount ( address [ ] byte ) ( * accounts . Account , bool ) {
2021-07-29 10:27:46 +00:00
AccRead . Inc ( )
2020-12-08 09:44:29 +00:00
var key AccountItem
2023-10-21 23:17:18 +00:00
h := libcommon . NewHasher ( )
defer libcommon . ReturnHasherToPool ( h )
2020-12-08 09:44:29 +00:00
//nolint:errcheck
h . Sha . Write ( address )
//nolint:errcheck
h . Sha . Read ( key . addrHash [ : ] )
if item , ok := sc . get ( & key ) ; ok {
if item != nil {
return & item . ( * AccountItem ) . account , true
}
return nil , true
}
return nil , false
}
2021-02-21 18:41:59 +00:00
func ( sc * StateCache ) HasAccountWithInPrefix ( addrHashPrefix [ ] byte ) bool {
2021-07-29 10:27:46 +00:00
AccRead . Inc ( )
2021-02-21 18:41:59 +00:00
seek := & AccountSeek { seek : addrHashPrefix }
var found bool
sc . readWrites [ id ( seek ) ] . AscendGreaterOrEqual ( seek , func ( i btree . Item ) bool {
found = bytes . HasPrefix ( i . ( * AccountItem ) . addrHash . Bytes ( ) , addrHashPrefix )
return false
} )
return found
}
2020-12-08 09:44:29 +00:00
// GetDeletedAccount attempts to retrieve the last version of account before it was deleted
func ( sc * StateCache ) GetDeletedAccount ( address [ ] byte ) * accounts . Account {
2021-02-21 18:41:59 +00:00
key := & AccountItem { }
2023-10-21 23:17:18 +00:00
h := libcommon . NewHasher ( )
defer libcommon . ReturnHasherToPool ( h )
2020-12-08 09:44:29 +00:00
//nolint:errcheck
h . Sha . Write ( address )
//nolint:errcheck
h . Sha . Read ( key . addrHash [ : ] )
2021-02-21 18:41:59 +00:00
item := sc . readWrites [ id ( key ) ] . Get ( key )
2020-12-08 09:44:29 +00:00
if item == nil {
return nil
}
ai := item . ( * AccountItem )
if ! ai . HasFlag ( DeletedFlag ) {
return nil
}
return & ai . account
}
// GetStorage searches storage item with given address, incarnation, and location, without modifying any structures
// Second return value is true if such item is found
func ( sc * StateCache ) GetStorage ( address [ ] byte , incarnation uint64 , location [ ] byte ) ( [ ] byte , bool ) {
2021-07-29 10:27:46 +00:00
StRead . Inc ( )
2020-12-08 09:44:29 +00:00
var key StorageItem
2023-10-21 23:17:18 +00:00
h := libcommon . NewHasher ( )
defer libcommon . ReturnHasherToPool ( h )
2020-12-08 09:44:29 +00:00
//nolint:errcheck
h . Sha . Write ( address )
//nolint:errcheck
h . Sha . Read ( key . addrHash [ : ] )
key . incarnation = incarnation
h . Sha . Reset ( )
//nolint:errcheck
h . Sha . Write ( location )
//nolint:errcheck
h . Sha . Read ( key . locHash [ : ] )
if item , ok := sc . get ( & key ) ; ok {
if item != nil {
return item . ( * StorageItem ) . value . Bytes ( ) , true
}
return nil , true
}
return nil , false
}
// GetCode searches contract code with given address, without modifying any structures
// Second return value is true if such item is found
func ( sc * StateCache ) GetCode ( address [ ] byte , incarnation uint64 ) ( [ ] byte , bool ) {
var key CodeItem
2023-10-21 23:17:18 +00:00
h := libcommon . NewHasher ( )
defer libcommon . ReturnHasherToPool ( h )
2020-12-08 09:44:29 +00:00
//nolint:errcheck
h . Sha . Write ( address )
//nolint:errcheck
h . Sha . Read ( key . addrHash [ : ] )
key . incarnation = incarnation
if item , ok := sc . get ( & key ) ; ok {
if item != nil {
return item . ( * CodeItem ) . code , true
}
return nil , true
}
return nil , false
}
func ( sc * StateCache ) setRead ( item CacheItem , absent bool ) {
2021-02-21 18:41:59 +00:00
id := id ( item )
if sc . readWrites [ id ] . Get ( item ) != nil {
2020-12-08 09:44:29 +00:00
panic ( fmt . Sprintf ( "item must not be present in the cache before doing setRead: %s" , item ) )
}
item . SetSequence ( sc . sequence )
sc . sequence ++
item . ClearFlags ( ModifiedFlag | DeletedFlag )
if absent {
item . SetFlags ( AbsentFlag )
} else {
item . ClearFlags ( AbsentFlag )
}
2021-02-21 18:41:59 +00:00
if sc . limit != 0 && sc . readSize + item . GetSize ( ) > int ( sc . limit ) {
2021-04-11 04:23:39 +00:00
for sc . readQueue [ id ] . Len ( ) > 0 && sc . readSize + item . GetSize ( ) > int ( sc . limit ) {
2020-12-08 09:44:29 +00:00
// Read queue cannot grow anymore, need to evict one element
2021-02-21 18:41:59 +00:00
cacheItem := heap . Pop ( & sc . readQueue [ id ] ) . ( CacheItem )
2020-12-08 09:44:29 +00:00
sc . readSize -= cacheItem . GetSize ( )
2021-02-21 18:41:59 +00:00
sc . readWrites [ id ] . Delete ( cacheItem )
2020-12-08 09:44:29 +00:00
}
}
// Push new element on the read queue
2021-02-21 18:41:59 +00:00
heap . Push ( & sc . readQueue [ id ] , item )
sc . readWrites [ id ] . ReplaceOrInsert ( item )
2020-12-08 09:44:29 +00:00
sc . readSize += item . GetSize ( )
}
2021-02-21 18:41:59 +00:00
func ( sc * StateCache ) readQueuesLen ( ) ( res int ) {
for i := 0 ; i < len ( sc . readQueue ) ; i ++ {
res += sc . readQueue [ i ] . Len ( )
}
return
}
2020-12-08 09:44:29 +00:00
// SetAccountRead adds given account to the cache, marking it as a read (not written)
func ( sc * StateCache ) SetAccountRead ( address [ ] byte , account * accounts . Account ) {
var ai AccountItem
2023-10-21 23:17:18 +00:00
h := libcommon . NewHasher ( )
defer libcommon . ReturnHasherToPool ( h )
2020-12-08 09:44:29 +00:00
//nolint:errcheck
h . Sha . Write ( address )
//nolint:errcheck
h . Sha . Read ( ai . addrHash [ : ] )
ai . account . Copy ( account )
sc . setRead ( & ai , false /* absent */ )
}
2021-02-21 18:41:59 +00:00
// hack to set hashed addr - we don't have another one in trie stage
2023-01-13 18:12:18 +00:00
func ( sc * StateCache ) DeprecatedSetAccountRead ( addrHash libcommon . Hash , account * accounts . Account ) {
2021-02-21 18:41:59 +00:00
var ai AccountItem
ai . addrHash . SetBytes ( addrHash . Bytes ( ) )
ai . account . Copy ( account )
sc . setRead ( & ai , false /* absent */ )
}
2023-01-13 18:12:18 +00:00
func ( sc * StateCache ) GetAccountByHashedAddress ( addrHash libcommon . Hash ) ( * accounts . Account , bool ) {
2021-02-21 18:41:59 +00:00
var key AccountItem
key . addrHash . SetBytes ( addrHash . Bytes ( ) )
if item , ok := sc . get ( & key ) ; ok {
if item != nil {
return & item . ( * AccountItem ) . account , true
}
return nil , true
}
return nil , false
}
2023-01-13 18:12:18 +00:00
func ( sc * StateCache ) GetStorageByHashedAddress ( addrHash libcommon . Hash , incarnation uint64 , locHash libcommon . Hash ) ( [ ] byte , bool ) {
2021-02-21 18:41:59 +00:00
key := StorageItem {
addrHash : addrHash ,
incarnation : incarnation ,
locHash : locHash ,
}
if item , ok := sc . get ( & key ) ; ok {
if item != nil {
return item . ( * StorageItem ) . value . Bytes ( ) , true
}
return nil , true
}
return nil , false
}
2020-12-08 09:44:29 +00:00
// SetAccountRead adds given account address to the cache, marking it as a absent
func ( sc * StateCache ) SetAccountAbsent ( address [ ] byte ) {
var ai AccountItem
2023-10-21 23:17:18 +00:00
h := libcommon . NewHasher ( )
defer libcommon . ReturnHasherToPool ( h )
2020-12-08 09:44:29 +00:00
//nolint:errcheck
h . Sha . Write ( address )
//nolint:errcheck
h . Sha . Read ( ai . addrHash [ : ] )
sc . setRead ( & ai , true /* absent */ )
}
2023-03-25 05:13:27 +00:00
func ( sc * StateCache ) setWrite ( item CacheItem , writeItem CacheWriteItem , del bool ) {
2021-02-21 18:41:59 +00:00
id := id ( item )
2020-12-08 09:44:29 +00:00
// Check if this is going to be modification of the existing entry
2021-02-21 18:41:59 +00:00
if existing := sc . writes [ id ] . Get ( writeItem ) ; existing != nil {
2020-12-08 09:44:29 +00:00
cacheWriteItem := existing . ( CacheWriteItem )
cacheItem := cacheWriteItem . GetCacheItem ( )
sc . readSize += item . GetSize ( )
sc . readSize -= cacheItem . GetSize ( )
sc . writeSize += writeItem . GetSize ( )
sc . writeSize -= cacheWriteItem . GetSize ( )
2023-03-25 05:13:27 +00:00
if del {
2020-12-08 09:44:29 +00:00
cacheItem . SetFlags ( DeletedFlag )
} else {
cacheItem . CopyValueFrom ( item )
cacheItem . ClearFlags ( DeletedFlag )
}
cacheItem . SetSequence ( sc . sequence )
sc . sequence ++
return
}
// Now see if there is such item in the readWrite B-tree - then we replace read entry with write entry
2021-02-21 18:41:59 +00:00
if existing := sc . readWrites [ id ] . Get ( item ) ; existing != nil {
2020-12-08 09:44:29 +00:00
cacheItem := existing . ( CacheItem )
2021-02-21 18:41:59 +00:00
// Remove seek the reads queue
if sc . readQueue [ id ] . Len ( ) > 0 {
heap . Remove ( & sc . readQueue [ id ] , cacheItem . GetQueuePos ( ) )
}
2020-12-08 09:44:29 +00:00
sc . readSize += item . GetSize ( )
sc . readSize -= cacheItem . GetSize ( )
cacheItem . SetFlags ( ModifiedFlag )
cacheItem . ClearFlags ( AbsentFlag )
2023-03-25 05:13:27 +00:00
if del {
2020-12-08 09:44:29 +00:00
cacheItem . SetFlags ( DeletedFlag )
} else {
cacheItem . CopyValueFrom ( item )
cacheItem . ClearFlags ( DeletedFlag )
}
cacheItem . SetSequence ( sc . sequence )
sc . sequence ++
writeItem . SetCacheItem ( cacheItem )
2021-02-21 18:41:59 +00:00
sc . writes [ id ] . ReplaceOrInsert ( writeItem )
2020-12-08 09:44:29 +00:00
sc . writeSize += writeItem . GetSize ( )
return
}
2021-02-21 18:41:59 +00:00
if sc . limit != 0 && sc . readSize + item . GetSize ( ) > int ( sc . limit ) {
2021-04-11 04:23:39 +00:00
for sc . readQueue [ id ] . Len ( ) > 0 && sc . readSize + item . GetSize ( ) > int ( sc . limit ) {
2020-12-08 09:44:29 +00:00
// There is no space available, need to evict one read element
2021-02-21 18:41:59 +00:00
cacheItem := heap . Pop ( & sc . readQueue [ id ] ) . ( CacheItem )
sc . readWrites [ id ] . Delete ( cacheItem )
2020-12-08 09:44:29 +00:00
sc . readSize -= cacheItem . GetSize ( )
}
}
item . SetSequence ( sc . sequence )
sc . sequence ++
item . SetFlags ( ModifiedFlag )
item . ClearFlags ( AbsentFlag )
2023-03-25 05:13:27 +00:00
if del {
2020-12-08 09:44:29 +00:00
item . SetFlags ( DeletedFlag )
} else {
item . ClearFlags ( DeletedFlag )
}
2021-02-21 18:41:59 +00:00
sc . readWrites [ id ] . ReplaceOrInsert ( item )
2020-12-08 09:44:29 +00:00
sc . readSize += item . GetSize ( )
writeItem . SetCacheItem ( item )
2021-02-21 18:41:59 +00:00
sc . writes [ id ] . ReplaceOrInsert ( writeItem )
2020-12-08 09:44:29 +00:00
sc . writeSize += writeItem . GetSize ( )
}
// SetAccountWrite adds given account to the cache, marking it as written (cannot be evicted)
func ( sc * StateCache ) SetAccountWrite ( address [ ] byte , account * accounts . Account ) {
var ai AccountItem
2023-10-21 23:17:18 +00:00
h := libcommon . NewHasher ( )
defer libcommon . ReturnHasherToPool ( h )
2020-12-08 09:44:29 +00:00
//nolint:errcheck
h . Sha . Write ( address )
//nolint:errcheck
h . Sha . Read ( ai . addrHash [ : ] )
ai . account . Copy ( account )
var awi AccountWriteItem
copy ( awi . address [ : ] , address )
awi . ai = & ai
sc . setWrite ( & ai , & awi , false /* delete */ )
}
// SetAccountDelete is very similar to SetAccountWrite with the difference that there no set value
func ( sc * StateCache ) SetAccountDelete ( address [ ] byte ) {
var ai AccountItem
2023-10-21 23:17:18 +00:00
h := libcommon . NewHasher ( )
defer libcommon . ReturnHasherToPool ( h )
2020-12-08 09:44:29 +00:00
//nolint:errcheck
h . Sha . Write ( address )
//nolint:errcheck
h . Sha . Read ( ai . addrHash [ : ] )
var awi AccountWriteItem
copy ( awi . address [ : ] , address )
awi . ai = & ai
sc . setWrite ( & ai , & awi , true /* delete */ )
}
func ( sc * StateCache ) SetStorageRead ( address [ ] byte , incarnation uint64 , location [ ] byte , value [ ] byte ) {
var si StorageItem
2023-10-21 23:17:18 +00:00
h := libcommon . NewHasher ( )
defer libcommon . ReturnHasherToPool ( h )
2020-12-08 09:44:29 +00:00
//nolint:errcheck
h . Sha . Write ( address )
//nolint:errcheck
h . Sha . Read ( si . addrHash [ : ] )
si . incarnation = incarnation
h . Sha . Reset ( )
//nolint:errcheck
h . Sha . Write ( location )
//nolint:errcheck
h . Sha . Read ( si . locHash [ : ] )
si . value . SetBytes ( value )
sc . setRead ( & si , false /* absent */ )
}
2021-02-21 18:41:59 +00:00
// hack to set hashed addr - we don't have another one in trie stage
2023-01-13 18:12:18 +00:00
func ( sc * StateCache ) DeprecatedSetStorageRead ( addrHash libcommon . Hash , incarnation uint64 , locHash libcommon . Hash , val [ ] byte ) {
2021-02-21 18:41:59 +00:00
var i StorageItem
2023-10-21 23:17:18 +00:00
h := libcommon . NewHasher ( )
defer libcommon . ReturnHasherToPool ( h )
2021-02-21 18:41:59 +00:00
copy ( i . addrHash [ : ] , addrHash . Bytes ( ) )
i . incarnation = incarnation
i . locHash . SetBytes ( locHash . Bytes ( ) )
i . value . SetBytes ( val )
sc . setRead ( & i , false /* absent */ )
}
// hack to set hashed addr - we don't have another one in trie stage
2023-01-13 18:12:18 +00:00
func ( sc * StateCache ) DeprecatedSetAccountWrite ( addrHash libcommon . Hash , account * accounts . Account ) {
2021-02-21 18:41:59 +00:00
var ai AccountItem
copy ( ai . addrHash [ : ] , addrHash . Bytes ( ) )
ai . account . Copy ( account )
var awi AccountWriteItem
awi . ai = & ai
sc . setWrite ( & ai , & awi , false /* delete */ )
}
// hack to set hashed addr - we don't have another one in trie stage
2023-01-13 18:12:18 +00:00
func ( sc * StateCache ) DeprecatedSetAccountDelete ( addrHash libcommon . Hash ) {
2021-02-21 18:41:59 +00:00
var ai AccountItem
copy ( ai . addrHash [ : ] , addrHash . Bytes ( ) )
var awi AccountWriteItem
awi . ai = & ai
sc . setWrite ( & ai , & awi , true /* delete */ )
}
// hack to set hashed addr - we don't have another one in trie stage
2023-01-13 18:12:18 +00:00
func ( sc * StateCache ) DeprecatedSetStorageDelete ( addrHash libcommon . Hash , incarnation uint64 , locHash libcommon . Hash ) {
2021-02-21 18:41:59 +00:00
var si StorageItem
copy ( si . addrHash [ : ] , addrHash . Bytes ( ) )
si . incarnation = incarnation
copy ( si . locHash [ : ] , locHash . Bytes ( ) )
var swi StorageWriteItem
swi . si = & si
sc . setWrite ( & si , & swi , true /* delete */ )
}
// hack to set hashed addr - we don't have another one in trie stage
2023-01-13 18:12:18 +00:00
func ( sc * StateCache ) DeprecatedSetStorageWrite ( addrHash libcommon . Hash , incarnation uint64 , locHash libcommon . Hash , v [ ] byte ) {
2021-02-21 18:41:59 +00:00
var si StorageItem
copy ( si . addrHash [ : ] , addrHash . Bytes ( ) )
si . incarnation = incarnation
copy ( si . locHash [ : ] , locHash . Bytes ( ) )
si . value . SetBytes ( v )
var swi StorageWriteItem
swi . si = & si
sc . setWrite ( & si , & swi , false /* delete */ )
}
2020-12-08 09:44:29 +00:00
func ( sc * StateCache ) SetStorageAbsent ( address [ ] byte , incarnation uint64 , location [ ] byte ) {
var si StorageItem
2023-10-21 23:17:18 +00:00
h := libcommon . NewHasher ( )
defer libcommon . ReturnHasherToPool ( h )
2020-12-08 09:44:29 +00:00
//nolint:errcheck
h . Sha . Write ( address )
//nolint:errcheck
h . Sha . Read ( si . addrHash [ : ] )
si . incarnation = incarnation
h . Sha . Reset ( )
//nolint:errcheck
h . Sha . Write ( location )
//nolint:errcheck
h . Sha . Read ( si . locHash [ : ] )
sc . setRead ( & si , true /* absent */ )
}
func ( sc * StateCache ) SetStorageWrite ( address [ ] byte , incarnation uint64 , location [ ] byte , value [ ] byte ) {
var si StorageItem
2023-10-21 23:17:18 +00:00
h := libcommon . NewHasher ( )
defer libcommon . ReturnHasherToPool ( h )
2020-12-08 09:44:29 +00:00
//nolint:errcheck
h . Sha . Write ( address )
//nolint:errcheck
h . Sha . Read ( si . addrHash [ : ] )
si . incarnation = incarnation
h . Sha . Reset ( )
//nolint:errcheck
h . Sha . Write ( location )
//nolint:errcheck
h . Sha . Read ( si . locHash [ : ] )
si . value . SetBytes ( value )
var swi StorageWriteItem
copy ( swi . address [ : ] , address )
copy ( swi . location [ : ] , location )
swi . si = & si
sc . setWrite ( & si , & swi , false /* delete */ )
}
func ( sc * StateCache ) SetStorageDelete ( address [ ] byte , incarnation uint64 , location [ ] byte ) {
var si StorageItem
2023-10-21 23:17:18 +00:00
h := libcommon . NewHasher ( )
defer libcommon . ReturnHasherToPool ( h )
2020-12-08 09:44:29 +00:00
//nolint:errcheck
h . Sha . Write ( address )
//nolint:errcheck
h . Sha . Read ( si . addrHash [ : ] )
si . incarnation = incarnation
h . Sha . Reset ( )
//nolint:errcheck
h . Sha . Write ( location )
//nolint:errcheck
h . Sha . Read ( si . locHash [ : ] )
var swi StorageWriteItem
copy ( swi . address [ : ] , address )
copy ( swi . location [ : ] , location )
swi . si = & si
sc . setWrite ( & si , & swi , true /* delete */ )
}
func ( sc * StateCache ) SetCodeRead ( address [ ] byte , incarnation uint64 , code [ ] byte ) {
var ci CodeItem
2023-10-21 23:17:18 +00:00
h := libcommon . NewHasher ( )
defer libcommon . ReturnHasherToPool ( h )
2020-12-08 09:44:29 +00:00
//nolint:errcheck
h . Sha . Write ( address )
//nolint:errcheck
h . Sha . Read ( ci . addrHash [ : ] )
ci . incarnation = incarnation
ci . code = make ( [ ] byte , len ( code ) )
copy ( ci . code , code )
sc . setRead ( & ci , false /* absent */ )
}
func ( sc * StateCache ) SetCodeAbsent ( address [ ] byte , incarnation uint64 ) {
var ci CodeItem
2023-10-21 23:17:18 +00:00
h := libcommon . NewHasher ( )
defer libcommon . ReturnHasherToPool ( h )
2020-12-08 09:44:29 +00:00
//nolint:errcheck
h . Sha . Write ( address )
//nolint:errcheck
h . Sha . Read ( ci . addrHash [ : ] )
ci . incarnation = incarnation
sc . setRead ( & ci , true /* absent */ )
}
func ( sc * StateCache ) SetCodeWrite ( address [ ] byte , incarnation uint64 , code [ ] byte ) {
// Check if this is going to be modification of the existing entry
var ci CodeItem
2023-10-21 23:17:18 +00:00
h := libcommon . NewHasher ( )
defer libcommon . ReturnHasherToPool ( h )
2020-12-08 09:44:29 +00:00
//nolint:errcheck
h . Sha . Write ( address )
//nolint:errcheck
h . Sha . Read ( ci . addrHash [ : ] )
ci . incarnation = incarnation
ci . code = make ( [ ] byte , len ( code ) )
copy ( ci . code , code )
var cwi CodeWriteItem
copy ( cwi . address [ : ] , address )
cwi . ci = & ci
sc . setWrite ( & ci , & cwi , false /* delete */ )
}
func ( sc * StateCache ) SetCodeDelete ( address [ ] byte , incarnation uint64 ) {
// Check if this is going to be modification of the existing entry
var ci CodeItem
2023-10-21 23:17:18 +00:00
h := libcommon . NewHasher ( )
defer libcommon . ReturnHasherToPool ( h )
2020-12-08 09:44:29 +00:00
//nolint:errcheck
h . Sha . Write ( address )
//nolint:errcheck
h . Sha . Read ( ci . addrHash [ : ] )
ci . incarnation = incarnation
ci . code = nil
var cwi CodeWriteItem
copy ( cwi . address [ : ] , address )
cwi . ci = & ci
sc . setWrite ( & ci , & cwi , true /* delete */ )
}
2021-02-21 18:41:59 +00:00
func ( sc * StateCache ) PrepareWrites ( ) [ 5 ] * btree . BTree {
var writes [ 5 ] * btree . BTree
for i := 0 ; i < len ( sc . writes ) ; i ++ {
sc . writes [ i ] . Ascend ( func ( i btree . Item ) bool {
writeItem := i . ( CacheWriteItem )
cacheItem := writeItem . GetCacheItem ( )
cacheItem . ClearFlags ( ModifiedFlag )
if cacheItem . HasFlag ( DeletedFlag ) {
cacheItem . ClearFlags ( DeletedFlag )
cacheItem . SetFlags ( AbsentFlag )
}
return true
} )
writes [ i ] = sc . writes [ i ] . Clone ( )
sc . writes [ i ] . Clear ( true /* addNodesToFreeList */ )
sc . writeSize = 0
}
2020-12-08 09:44:29 +00:00
return writes
}
func WalkWrites (
2021-02-21 18:41:59 +00:00
writes [ 5 ] * btree . BTree ,
2020-12-08 09:44:29 +00:00
accountWrite func ( address [ ] byte , account * accounts . Account ) error ,
accountDelete func ( address [ ] byte , original * accounts . Account ) error ,
storageWrite func ( address [ ] byte , incarnation uint64 , location [ ] byte , value [ ] byte ) error ,
storageDelete func ( address [ ] byte , incarnation uint64 , location [ ] byte ) error ,
codeWrite func ( address [ ] byte , incarnation uint64 , code [ ] byte ) error ,
codeDelete func ( address [ ] byte , incarnation uint64 ) error ,
) error {
var err error
2021-02-21 18:41:59 +00:00
for i := 0 ; i < len ( writes ) ; i ++ {
writes [ i ] . Ascend ( func ( i btree . Item ) bool {
switch it := i . ( type ) {
case * AccountWriteItem :
if it . ai . flags & AbsentFlag != 0 {
if err = accountDelete ( it . address . Bytes ( ) , & it . ai . account ) ; err != nil {
return false
}
} else {
if err = accountWrite ( it . address . Bytes ( ) , & it . ai . account ) ; err != nil {
return false
}
2020-12-08 09:44:29 +00:00
}
2021-02-21 18:41:59 +00:00
case * StorageWriteItem :
if it . si . flags & AbsentFlag != 0 {
if err = storageDelete ( it . address . Bytes ( ) , it . si . incarnation , it . location . Bytes ( ) ) ; err != nil {
return false
}
} else {
if err = storageWrite ( it . address . Bytes ( ) , it . si . incarnation , it . location . Bytes ( ) , it . si . value . Bytes ( ) ) ; err != nil {
return false
}
2020-12-08 09:44:29 +00:00
}
2021-02-21 18:41:59 +00:00
case * CodeWriteItem :
if it . ci . flags & AbsentFlag != 0 {
if err = codeDelete ( it . address . Bytes ( ) , it . ci . incarnation ) ; err != nil {
return false
}
} else {
if err = codeWrite ( it . address . Bytes ( ) , it . ci . incarnation , it . ci . code ) ; err != nil {
return false
}
2020-12-08 09:44:29 +00:00
}
}
2021-02-21 18:41:59 +00:00
return true
} )
}
2020-12-08 09:44:29 +00:00
2021-02-21 18:41:59 +00:00
return err
2020-12-08 09:44:29 +00:00
}
2021-02-21 18:41:59 +00:00
func ( sc * StateCache ) TurnWritesToReads ( writes [ 5 ] * btree . BTree ) {
for i := 0 ; i < len ( writes ) ; i ++ {
readQueue := & sc . readQueue [ i ]
writes [ i ] . Ascend ( func ( it btree . Item ) bool {
cacheWriteItem := it . ( CacheWriteItem )
cacheItem := cacheWriteItem . GetCacheItem ( )
if ! cacheItem . HasFlag ( ModifiedFlag ) {
// Cannot touch items that have been modified since we have taken away the writes
heap . Push ( readQueue , cacheItem )
}
return true
} )
}
2020-12-08 09:44:29 +00:00
}
2021-02-21 18:41:59 +00:00
func ( sc * StateCache ) TotalCount ( ) ( res int ) {
for i := 0 ; i < len ( sc . readWrites ) ; i ++ {
res += sc . readWrites [ i ] . Len ( )
}
return
2020-12-08 09:44:29 +00:00
}
2021-02-21 18:41:59 +00:00
func ( sc * StateCache ) WriteCount ( ) ( res int ) {
for i := 0 ; i < len ( sc . readWrites ) ; i ++ {
res += sc . writes [ i ] . Len ( )
}
return
2020-12-08 09:44:29 +00:00
}
2021-02-21 18:41:59 +00:00
func ( sc * StateCache ) WriteSize ( ) int { return sc . writeSize }
func ( sc * StateCache ) ReadSize ( ) int { return sc . readSize }