erigon-pulse/turbo/shards/state_cache_test.go

568 lines
16 KiB
Go

package shards
import (
"bytes"
"fmt"
"testing"
"github.com/c2h5oh/datasize"
libcommon "github.com/ledgerwatch/erigon-lib/common"
"github.com/stretchr/testify/assert"
"golang.org/x/crypto/sha3"
"github.com/ledgerwatch/erigon/core/types/accounts"
)
func TestCacheBtreeOrderAccountStorage2(t *testing.T) {
sc := NewStateCache(32, datasize.ByteSize(128*accountItemSize))
var a1 libcommon.Address
a1[0] = 1
sc.SetAccountRead(a1.Bytes(), &accounts.Account{})
sc.SetAccountWrite(a1.Bytes(), &accounts.Account{Incarnation: 2})
x, ok := sc.GetAccount(a1.Bytes())
fmt.Printf("%+v,%t\n", x.Incarnation, ok)
}
func TestCacheBtreeOrderAccountStorage(t *testing.T) {
sc := NewStateCache(32, datasize.ByteSize(128*accountItemSize))
var a1, a2 libcommon.Address
a1[0] = 1
a2[0] = 2
sc.SetAccountWrite(a2.Bytes(), &accounts.Account{})
sc.SetAccountWrite(a1.Bytes(), &accounts.Account{})
lastK := make([]byte, 0, 128)
curK := make([]byte, 0, 128)
if err := sc.WalkAccounts([]byte{}, func(addrHash libcommon.Hash, account *accounts.Account) (bool, error) {
curK = append(curK[:0], addrHash.Bytes()...)
assert.True(t, bytes.Compare(lastK, curK) < 0)
lastK = append(lastK[:0], curK...)
return true, nil
}); err != nil {
t.Fatal(err)
}
var l1, l2, l3 libcommon.Hash
l1[0] = 42
l2[0] = 2
l3[0] = 3
sc.SetStorageWrite(a1.Bytes(), 1, l1.Bytes(), nil)
sc.SetStorageWrite(a1.Bytes(), 1, l2.Bytes(), nil)
sc.SetStorageWrite(a2.Bytes(), 1, l3.Bytes(), nil)
lastK = lastK[:0]
if err := sc.WalkStorage(libcommon.BytesToHash(sha3.NewLegacyKeccak256().Sum(a1.Bytes())), 1, nil, func(locHash libcommon.Hash, val []byte) error {
curK = append(curK[:0], locHash.Bytes()...)
assert.True(t, bytes.Compare(lastK, curK) < 0)
lastK = append(lastK[:0], curK...)
return nil
}); err != nil {
t.Fatal(err)
}
sc.SetCodeWrite(a1.Bytes(), 1, []byte{1})
sc.SetCodeWrite(a2.Bytes(), 1, []byte{2})
lastK = lastK[:0]
//if err := WalkWrites(sc.PrepareWrites(), nil, nil, nil, nil, func(address []byte, incarnation uint64, code []byte) error {
// i++
// if i == 1 {
// assert.Equal(t, a1.Bytes(), address)
// assert.Equal(t, 1, incarnation)
// assert.Equal(t, []byte{1}, code)
// }
// if i == 2 {
// assert.Equal(t, a2.Bytes(), address)
// assert.Equal(t, 1, incarnation)
// assert.Equal(t, []byte{2}, code)
// }
// return nil
//}, nil); err != nil {
// t.Fatal(err)
//}
}
func TestAccountReads(t *testing.T) {
sc := NewStateCache(32, datasize.ByteSize(4*accountItemSize))
var account1 accounts.Account
account1.Balance.SetUint64(1)
var addr1 libcommon.Address
addr1[0] = 1
sc.SetAccountRead(addr1.Bytes(), &account1)
if _, ok := sc.GetAccount(addr1.Bytes()); !ok {
t.Fatalf("Expected to find account with addr1")
}
var addr2 libcommon.Address
addr2[0] = 2
if _, ok := sc.GetAccount(addr2.Bytes()); ok {
t.Fatalf("Did not expect account with addr2")
}
var addr3 libcommon.Address
addr3[0] = 3
sc.SetAccountAbsent(addr3.Bytes())
if a, ok := sc.GetAccount(addr3.Bytes()); !ok || a != nil {
t.Fatalf("Expected account with addr3 to be absent")
}
for i := 4; i <= 6; i++ {
var account accounts.Account
account.Balance.SetUint64(uint64(i))
var addr libcommon.Address
addr[0] = byte(i)
sc.SetAccountRead(addr.Bytes(), &account)
}
// Out of 6 addresses, one was not associated with an account or absence record. So 5 records would be in the cache
// But since the limit is 4, the first addr will be evicted
if _, ok := sc.GetAccount(addr1.Bytes()); ok {
t.Fatalf("Expected addr1 to be evicted")
}
for i := 4; i <= 6; i++ {
var account accounts.Account
account.Balance.SetUint64(uint64(i))
var addr libcommon.Address
addr[0] = byte(i)
if _, ok := sc.GetAccount(addr.Bytes()); !ok {
t.Fatalf("Expected to find account with addr %x", addr)
}
}
}
func TestAccountReadWrites(t *testing.T) {
sc := NewStateCache(32, datasize.ByteSize(9*accountItemSize))
var account1 accounts.Account
account1.Balance.SetUint64(1)
var addr1 libcommon.Address
addr1[0] = 1
sc.SetAccountWrite(addr1.Bytes(), &account1)
if _, ok := sc.GetAccount(addr1.Bytes()); !ok {
t.Fatalf("Expected to find account with addr1")
}
if sc.WriteCount() != 1 {
t.Fatalf("Write queue is expected to have 1 elements, got: %d", sc.WriteCount())
}
// Replace the existing value
var account11 accounts.Account
account11.Balance.SetUint64(11)
sc.SetAccountWrite(addr1.Bytes(), &account11)
if a, ok := sc.GetAccount(addr1.Bytes()); !ok {
t.Fatalf("Expected to find account with addr1")
} else {
if a.Balance.Uint64() != 11 {
t.Fatalf("Expected account balance 11, got %d", a.Balance.Uint64())
}
}
if sc.WriteCount() != 1 {
t.Fatalf("Write queue is expected to have 1 elements, got: %d", sc.WriteCount())
}
// Add read and then replace it with the write
var account2 accounts.Account
account2.Balance.SetUint64(2)
var addr2 libcommon.Address
addr2[0] = 2
sc.SetAccountRead(addr2.Bytes(), &account2)
// Check that readQueue is empty
if sc.readQueuesLen() != 1 {
t.Fatalf("Read queue is expected to be 1 element")
}
var account22 accounts.Account
account22.Balance.SetUint64(22)
sc.SetAccountWrite(addr2.Bytes(), &account22)
if a, ok := sc.GetAccount(addr2.Bytes()); !ok {
t.Fatalf("Expected to find account with addr2")
} else {
if a.Balance.Uint64() != 22 {
t.Fatalf("Expected account balance 22, got %d", a.Balance.Uint64())
}
}
if sc.WriteCount() != 2 {
t.Fatalf("Write queue is expected to have 2 elements, got: %d", sc.WriteCount())
}
// Check that readQueue is empty
if sc.readQueuesLen() != 0 {
t.Fatalf("Read queue is expected to be empty")
}
// Deleting written account
var account3 accounts.Account
account3.Balance.SetUint64(3)
var addr3 libcommon.Address
addr3[0] = 3
sc.SetAccountWrite(addr3.Bytes(), &account3)
sc.SetAccountDelete(addr3.Bytes())
if a, ok := sc.GetAccount(addr3.Bytes()); !ok || a != nil {
t.Fatalf("Expected account addr3 to be deleted")
}
if sc.WriteCount() != 3 {
t.Fatalf("Write queue is expected to have 3 elements, got: %d", sc.WriteCount())
}
// Deleting read account
var account4 accounts.Account
account4.Balance.SetUint64(4)
var addr4 libcommon.Address
addr4[0] = 4
sc.SetAccountRead(addr4.Bytes(), &account4)
sc.SetAccountDelete(addr4.Bytes())
if a, ok := sc.GetAccount(addr4.Bytes()); !ok || a != nil {
t.Fatalf("Expected account addr4 to be deleted")
}
if sc.WriteCount() != 4 {
t.Fatalf("Write queue is expected to have 4 elements, got: %d", sc.WriteCount())
}
// Check that readQueue is empty
if sc.readQueuesLen() != 0 {
t.Fatalf("Read queue is expected to be empty")
}
// Deleting account not seen before
var addr5 libcommon.Address
addr5[0] = 5
sc.SetAccountDelete(addr5.Bytes())
if a, ok := sc.GetAccount(addr5.Bytes()); !ok || a != nil {
t.Fatalf("Expected account addr5 to be deleted")
}
if sc.WriteCount() != 5 {
t.Fatalf("Write queue is expected to have 5 elements, got: %d", sc.WriteCount())
}
}
func TestReplaceAccountReadsWithWrites(t *testing.T) {
sc := NewStateCache(32, datasize.ByteSize(6*accountItemSize))
for i := 1; i <= 4; i++ {
var addr libcommon.Address
addr[0] = byte(i)
var account accounts.Account
account.Balance.SetUint64(uint64(i))
sc.SetAccountWrite(addr.Bytes(), &account)
}
writes := sc.PrepareWrites()
sc.TurnWritesToReads(writes)
if sc.WriteCount() != 0 {
t.Fatalf("Write queue is expected to be empty, got: %d", sc.WriteCount())
}
if sc.readQueuesLen() != 4 {
t.Fatalf("Read queue is expected to have 4 elements, got: %d", sc.readQueuesLen())
}
// Do 4 more delets
for i := 5; i <= 8; i++ {
var addr libcommon.Address
addr[0] = byte(i)
var account accounts.Account
account.Balance.SetUint64(uint64(i))
sc.SetAccountWrite(addr.Bytes(), &account)
}
if sc.WriteCount() != 4 {
t.Fatalf("Write queue is expected to have 4 elements, got: %d", sc.WriteCount())
}
if sc.readQueuesLen() != 2 {
t.Fatalf("Read queue is expected to have 2 elements, got: %d", sc.readQueuesLen())
}
// Check that the first two address are evicted
for i := 1; i <= 2; i++ {
var addr libcommon.Address
addr[0] = byte(i)
if _, ok := sc.GetAccount(addr.Bytes()); ok {
t.Fatalf("Expected not to find address %d", i)
}
}
// Check that the other 6 addresses are there
for i := 3; i <= 8; i++ {
var addr libcommon.Address
addr[0] = byte(i)
if _, ok := sc.GetAccount(addr.Bytes()); !ok {
t.Errorf("Expected to find address %d", i)
}
}
}
func TestReadAccountExisting(t *testing.T) {
sc := NewStateCache(32, datasize.ByteSize(2*accountItemSize))
var account1 accounts.Account
account1.Balance.SetUint64(1)
var addr1 libcommon.Address
sc.SetAccountRead(addr1.Bytes(), &account1)
defer func() {
//nolint:staticcheck
if r := recover(); r != nil {
}
}()
sc.SetAccountRead(addr1.Bytes(), &account1)
t.Fatalf("Expected to panic")
}
func TestWriteAccountExceedLimit(t *testing.T) {
sc := NewStateCache(32, datasize.ByteSize(2*accountItemSize))
defer func() {
//nolint:staticcheck
if r := recover(); r != nil {
}
}()
for i := 1; i <= 3; i++ {
var addr libcommon.Address
addr[0] = byte(i)
var account accounts.Account
account.Balance.SetUint64(uint64(i))
sc.SetAccountWrite(addr.Bytes(), &account)
}
}
func TestGetDeletedAccount(t *testing.T) {
sc := NewStateCache(32, datasize.ByteSize(4*accountItemSize))
var account1 accounts.Account
account1.Balance.SetUint64(1)
account1.Incarnation = 1
var addr1 libcommon.Address
addr1[0] = 1
sc.SetAccountRead(addr1.Bytes(), &account1)
var account11 accounts.Account
account11.Incarnation = 2
sc.SetAccountWrite(addr1.Bytes(), &account11)
acc := sc.GetDeletedAccount(addr1.Bytes())
if acc != nil {
t.Fatalf("Did not expect to find deleted account before deletion")
}
sc.SetAccountDelete(addr1.Bytes())
acc = sc.GetDeletedAccount(addr1.Bytes())
if acc == nil {
t.Fatalf("Expected to find deleted account")
}
if acc.Incarnation != 2 {
t.Fatalf("Expected to find deleted account with incarnation 2, got %d", acc.Incarnation)
}
}
func TestReadWriteAbsentDeleteStorage(t *testing.T) {
sc := NewStateCache(32, datasize.ByteSize(4*storageItemSize))
// Add absents
for i := 1; i <= 4; i++ {
var addr libcommon.Address
addr[0] = byte(i)
var loc libcommon.Hash
loc[1] = byte(i)
sc.SetStorageAbsent(addr.Bytes(), 1, loc.Bytes())
}
if sc.readQueuesLen() != 4 {
t.Fatalf("expected 4 reads got: %d", sc.readQueuesLen())
}
for i := 1; i <= 4; i++ {
var addr libcommon.Address
addr[0] = byte(i)
var loc libcommon.Hash
loc[1] = byte(i)
if s, ok := sc.GetStorage(addr.Bytes(), 1, loc.Bytes()); !ok || s != nil {
t.Fatalf("expected entry with %x,1,%x not to exist", addr, loc)
}
}
// Add reads for incarnation 2 (instead of 1), 6 records instead of 4, so only last 4 will stay
for i := 1; i <= 6; i++ {
var addr libcommon.Address
addr[0] = byte(i)
var loc libcommon.Hash
loc[1] = byte(i)
var val libcommon.Hash
val[2] = byte(i)
sc.SetStorageRead(addr.Bytes(), 2, loc.Bytes(), val.Bytes())
}
if sc.readQueuesLen() != 4 {
t.Fatalf("expected 4 reads got: %d", sc.readQueuesLen())
}
// Check that first two records were evicted
for i := 1; i <= 2; i++ {
var addr libcommon.Address
addr[0] = byte(i)
var loc libcommon.Hash
loc[1] = byte(i)
if _, ok := sc.GetStorage(addr.Bytes(), 2, loc.Bytes()); ok {
t.Fatalf("expected entry with %x,2,%x to be evicted", addr, loc)
}
}
// Check that last 4 records stayed
for i := 3; i <= 6; i++ {
var addr libcommon.Address
addr[0] = byte(i)
var loc libcommon.Hash
loc[1] = byte(i)
if _, ok := sc.GetStorage(addr.Bytes(), 2, loc.Bytes()); !ok {
t.Errorf("expected entry with %x,2,%x to exist", addr, loc)
}
}
// Replace all 4 entries with deletes
for i := 1; i <= 4; i++ {
var addr libcommon.Address
addr[0] = byte(i)
var loc libcommon.Hash
loc[1] = byte(i)
sc.SetStorageDelete(addr.Bytes(), 1, loc.Bytes())
}
if sc.readQueuesLen() != 0 {
t.Fatalf("expected 0 reads got: %d", sc.readQueuesLen())
}
for i := 1; i <= 4; i++ {
var addr libcommon.Address
addr[0] = byte(i)
var loc libcommon.Hash
loc[1] = byte(i)
if s, ok := sc.GetStorage(addr.Bytes(), 1, loc.Bytes()); !ok || s != nil {
t.Fatalf("expected entry with %x,1,%x not to exist", addr, loc)
}
}
// Replace all 4 deletes with writes
for i := 1; i <= 4; i++ {
var addr libcommon.Address
addr[0] = byte(i)
var loc libcommon.Hash
loc[1] = byte(i)
var val libcommon.Hash
val[2] = byte(i)
sc.SetStorageWrite(addr.Bytes(), 1, loc.Bytes(), val.Bytes())
}
if sc.WriteCount() != 4 {
t.Fatalf("expected 4 writes, got %d", sc.WriteCount())
}
for i := 1; i <= 4; i++ {
var addr libcommon.Address
addr[0] = byte(i)
var loc libcommon.Hash
loc[1] = byte(i)
if _, ok := sc.GetStorage(addr.Bytes(), 1, loc.Bytes()); !ok {
t.Fatalf("expected entry with %x,1,%x to exist", addr, loc)
}
}
}
func TestReadStorageExisting(t *testing.T) {
sc := NewStateCache(32, datasize.ByteSize(2*storageItemSize))
var addr1 libcommon.Address
addr1[0] = 1
var loc1 libcommon.Hash
var val1 libcommon.Hash
val1[2] = 1
sc.SetStorageRead(addr1.Bytes(), 1, loc1.Bytes(), val1.Bytes())
defer func() {
//nolint:staticcheck
if r := recover(); r != nil {
}
}()
sc.SetStorageRead(addr1.Bytes(), 1, loc1.Bytes(), val1.Bytes())
t.Fatalf("Expected to panic")
}
func TestWriteStorageExceedLimit(t *testing.T) {
sc := NewStateCache(32, datasize.ByteSize(2*storageItemSize))
defer func() {
//nolint:staticcheck
if r := recover(); r != nil {
}
}()
for i := 1; i <= 3; i++ {
var addr libcommon.Address
addr[0] = byte(i)
var loc libcommon.Hash
loc[1] = byte(i)
var val libcommon.Hash
val[2] = byte(i)
sc.SetStorageWrite(addr.Bytes(), 1, loc.Bytes(), val.Bytes())
}
}
func TestCodeReadWriteAbsentDelete(t *testing.T) {
sc := NewStateCache(32, datasize.ByteSize(4*(codeItemSize+3)))
// Add absents
for i := 1; i <= 4; i++ {
var addr libcommon.Address
addr[0] = byte(i)
sc.SetCodeAbsent(addr.Bytes(), 1)
}
if sc.readQueuesLen() != 4 {
t.Fatalf("expected 4 reads got: %d", sc.readQueuesLen())
}
for i := 1; i <= 4; i++ {
var addr libcommon.Address
addr[0] = byte(i)
if c, ok := sc.GetCode(addr.Bytes(), 1); !ok || c != nil {
t.Fatalf("expected entry with %x,1 not to exist", addr)
}
}
// Add reads for incarnation 2 (instead of 1), 6 records instead of 4, so only last 4 will stay
for i := 1; i <= 6; i++ {
var addr libcommon.Address
addr[0] = byte(i)
var code = []byte{byte(i), 2, 3}
sc.SetCodeRead(addr.Bytes(), 2, code)
}
if sc.readQueuesLen() != 4 {
t.Fatalf("expected 4 reads got: %d", sc.readQueuesLen())
}
// Check that first two records were evicted
for i := 1; i <= 2; i++ {
var addr libcommon.Address
addr[0] = byte(i)
if _, ok := sc.GetCode(addr.Bytes(), 2); ok {
t.Fatalf("expected entry with %x,2 to be evicted", addr)
}
}
// Check that last 4 records stayed
for i := 3; i <= 6; i++ {
var addr libcommon.Address
addr[0] = byte(i)
if _, ok := sc.GetCode(addr.Bytes(), 2); !ok {
t.Errorf("expected entry with %x,2 to exist", addr)
}
}
// Replace all 4 entries with deletes
for i := 1; i <= 4; i++ {
var addr libcommon.Address
addr[0] = byte(i)
sc.SetCodeDelete(addr.Bytes(), 1)
}
if sc.readQueuesLen() != 0 {
t.Fatalf("expected 0 reads got: %d", sc.readQueuesLen())
}
for i := 1; i <= 4; i++ {
var addr libcommon.Address
addr[0] = byte(i)
if c, ok := sc.GetCode(addr.Bytes(), 1); !ok || c != nil {
t.Fatalf("expected entry with %x,1 not to exist", addr)
}
}
// Replace all 4 deletes with writes
for i := 1; i <= 4; i++ {
var addr libcommon.Address
addr[0] = byte(i)
var code = []byte{byte(i), 2, 3}
sc.SetCodeWrite(addr.Bytes(), 1, code)
}
if sc.WriteCount() != 4 {
t.Fatalf("expected 4 writes, got %d", sc.WriteCount())
}
for i := 1; i <= 4; i++ {
var addr libcommon.Address
addr[0] = byte(i)
if _, ok := sc.GetCode(addr.Bytes(), 1); !ok {
t.Fatalf("expected entry with %x,1 to exist", addr)
}
}
}
func TestReadCodeExisting(t *testing.T) {
sc := NewStateCache(32, datasize.ByteSize(2*(codeItemSize+3)))
var addr1 libcommon.Address
addr1[0] = 1
code1 := []byte{1, 2, 3}
sc.SetCodeRead(addr1.Bytes(), 1, code1)
defer func() {
//nolint:staticcheck
if r := recover(); r != nil {
}
}()
sc.SetCodeRead(addr1.Bytes(), 1, code1)
t.Fatalf("Expected to panic")
}
func TestWriteCodeExceedLimit(t *testing.T) {
sc := NewStateCache(32, datasize.ByteSize(2*(codeItemSize+3)))
defer func() {
//nolint:staticcheck
if r := recover(); r != nil {
}
}()
for i := 1; i <= 3; i++ {
var addr libcommon.Address
addr[0] = byte(i)
code := []byte{byte(i), 2, 3}
sc.SetCodeWrite(addr.Bytes(), 1, code)
}
}