github.com/ethereum/go-ethereum@v1.16.1/triedb/pathdb/history_test.go (about)

     1  // Copyright 2023 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package pathdb
    18  
    19  import (
    20  	"bytes"
    21  	"fmt"
    22  	"reflect"
    23  	"testing"
    24  
    25  	"github.com/ethereum/go-ethereum/common"
    26  	"github.com/ethereum/go-ethereum/core/rawdb"
    27  	"github.com/ethereum/go-ethereum/core/types"
    28  	"github.com/ethereum/go-ethereum/ethdb"
    29  	"github.com/ethereum/go-ethereum/internal/testrand"
    30  	"github.com/ethereum/go-ethereum/rlp"
    31  )
    32  
    33  // randomStateSet generates a random state change set.
    34  func randomStateSet(n int) (map[common.Address][]byte, map[common.Address]map[common.Hash][]byte) {
    35  	var (
    36  		accounts = make(map[common.Address][]byte)
    37  		storages = make(map[common.Address]map[common.Hash][]byte)
    38  	)
    39  	for i := 0; i < n; i++ {
    40  		addr := testrand.Address()
    41  		storages[addr] = make(map[common.Hash][]byte)
    42  		for j := 0; j < 3; j++ {
    43  			v, _ := rlp.EncodeToBytes(common.TrimLeftZeroes(testrand.Bytes(32)))
    44  			storages[addr][testrand.Hash()] = v
    45  		}
    46  		account := generateAccount(types.EmptyRootHash)
    47  		accounts[addr] = types.SlimAccountRLP(account)
    48  	}
    49  	return accounts, storages
    50  }
    51  
    52  func makeHistory(rawStorageKey bool) *history {
    53  	accounts, storages := randomStateSet(3)
    54  	return newHistory(testrand.Hash(), types.EmptyRootHash, 0, accounts, storages, rawStorageKey)
    55  }
    56  
    57  func makeHistories(n int) []*history {
    58  	var (
    59  		parent = types.EmptyRootHash
    60  		result []*history
    61  	)
    62  	for i := 0; i < n; i++ {
    63  		root := testrand.Hash()
    64  		accounts, storages := randomStateSet(3)
    65  		h := newHistory(root, parent, uint64(i), accounts, storages, false)
    66  		parent = root
    67  		result = append(result, h)
    68  	}
    69  	return result
    70  }
    71  
    72  func TestEncodeDecodeHistory(t *testing.T) {
    73  	testEncodeDecodeHistory(t, false)
    74  	testEncodeDecodeHistory(t, true)
    75  }
    76  
    77  func testEncodeDecodeHistory(t *testing.T, rawStorageKey bool) {
    78  	var (
    79  		m   meta
    80  		dec history
    81  		obj = makeHistory(rawStorageKey)
    82  	)
    83  	// check if meta data can be correctly encode/decode
    84  	blob := obj.meta.encode()
    85  	if err := m.decode(blob); err != nil {
    86  		t.Fatalf("Failed to decode %v", err)
    87  	}
    88  	if !reflect.DeepEqual(&m, obj.meta) {
    89  		t.Fatal("meta is mismatched")
    90  	}
    91  
    92  	// check if account/storage data can be correctly encode/decode
    93  	accountData, storageData, accountIndexes, storageIndexes := obj.encode()
    94  	if err := dec.decode(accountData, storageData, accountIndexes, storageIndexes); err != nil {
    95  		t.Fatalf("Failed to decode, err: %v", err)
    96  	}
    97  	if !compareSet(dec.accounts, obj.accounts) {
    98  		t.Fatal("account data is mismatched")
    99  	}
   100  	if !compareStorages(dec.storages, obj.storages) {
   101  		t.Fatal("storage data is mismatched")
   102  	}
   103  	if !compareList(dec.accountList, obj.accountList) {
   104  		t.Fatal("account list is mismatched")
   105  	}
   106  	if !compareStorageList(dec.storageList, obj.storageList) {
   107  		t.Fatal("storage list is mismatched")
   108  	}
   109  }
   110  
   111  func checkHistory(t *testing.T, db ethdb.KeyValueReader, freezer ethdb.AncientReader, id uint64, root common.Hash, exist bool) {
   112  	blob := rawdb.ReadStateHistoryMeta(freezer, id)
   113  	if exist && len(blob) == 0 {
   114  		t.Fatalf("Failed to load trie history, %d", id)
   115  	}
   116  	if !exist && len(blob) != 0 {
   117  		t.Fatalf("Unexpected trie history, %d", id)
   118  	}
   119  	if exist && rawdb.ReadStateID(db, root) == nil {
   120  		t.Fatalf("Root->ID mapping is not found, %d", id)
   121  	}
   122  	if !exist && rawdb.ReadStateID(db, root) != nil {
   123  		t.Fatalf("Unexpected root->ID mapping, %d", id)
   124  	}
   125  }
   126  
   127  func checkHistoriesInRange(t *testing.T, db ethdb.KeyValueReader, freezer ethdb.AncientReader, from, to uint64, roots []common.Hash, exist bool) {
   128  	for i, j := from, 0; i <= to; i, j = i+1, j+1 {
   129  		checkHistory(t, db, freezer, i, roots[j], exist)
   130  	}
   131  }
   132  
   133  func TestTruncateHeadHistory(t *testing.T) {
   134  	var (
   135  		roots      []common.Hash
   136  		hs         = makeHistories(10)
   137  		db         = rawdb.NewMemoryDatabase()
   138  		freezer, _ = rawdb.NewStateFreezer(t.TempDir(), false, false)
   139  	)
   140  	defer freezer.Close()
   141  
   142  	for i := 0; i < len(hs); i++ {
   143  		accountData, storageData, accountIndex, storageIndex := hs[i].encode()
   144  		rawdb.WriteStateHistory(freezer, uint64(i+1), hs[i].meta.encode(), accountIndex, storageIndex, accountData, storageData)
   145  		rawdb.WriteStateID(db, hs[i].meta.root, uint64(i+1))
   146  		roots = append(roots, hs[i].meta.root)
   147  	}
   148  	for size := len(hs); size > 0; size-- {
   149  		pruned, err := truncateFromHead(db, freezer, uint64(size-1))
   150  		if err != nil {
   151  			t.Fatalf("Failed to truncate from head %v", err)
   152  		}
   153  		if pruned != 1 {
   154  			t.Error("Unexpected pruned items", "want", 1, "got", pruned)
   155  		}
   156  		checkHistoriesInRange(t, db, freezer, uint64(size), uint64(10), roots[size-1:], false)
   157  		checkHistoriesInRange(t, db, freezer, uint64(1), uint64(size-1), roots[:size-1], true)
   158  	}
   159  }
   160  
   161  func TestTruncateTailHistory(t *testing.T) {
   162  	var (
   163  		roots      []common.Hash
   164  		hs         = makeHistories(10)
   165  		db         = rawdb.NewMemoryDatabase()
   166  		freezer, _ = rawdb.NewStateFreezer(t.TempDir(), false, false)
   167  	)
   168  	defer freezer.Close()
   169  
   170  	for i := 0; i < len(hs); i++ {
   171  		accountData, storageData, accountIndex, storageIndex := hs[i].encode()
   172  		rawdb.WriteStateHistory(freezer, uint64(i+1), hs[i].meta.encode(), accountIndex, storageIndex, accountData, storageData)
   173  		rawdb.WriteStateID(db, hs[i].meta.root, uint64(i+1))
   174  		roots = append(roots, hs[i].meta.root)
   175  	}
   176  	for newTail := 1; newTail < len(hs); newTail++ {
   177  		pruned, _ := truncateFromTail(db, freezer, uint64(newTail))
   178  		if pruned != 1 {
   179  			t.Error("Unexpected pruned items", "want", 1, "got", pruned)
   180  		}
   181  		checkHistoriesInRange(t, db, freezer, uint64(1), uint64(newTail), roots[:newTail], false)
   182  		checkHistoriesInRange(t, db, freezer, uint64(newTail+1), uint64(10), roots[newTail:], true)
   183  	}
   184  }
   185  
   186  func TestTruncateTailHistories(t *testing.T) {
   187  	var cases = []struct {
   188  		limit       uint64
   189  		expPruned   int
   190  		maxPruned   uint64
   191  		minUnpruned uint64
   192  		empty       bool
   193  	}{
   194  		{
   195  			1, 9, 9, 10, false,
   196  		},
   197  		{
   198  			0, 10, 10, 0 /* no meaning */, true,
   199  		},
   200  		{
   201  			10, 0, 0, 1, false,
   202  		},
   203  	}
   204  	for i, c := range cases {
   205  		var (
   206  			roots      []common.Hash
   207  			hs         = makeHistories(10)
   208  			db         = rawdb.NewMemoryDatabase()
   209  			freezer, _ = rawdb.NewStateFreezer(t.TempDir()+fmt.Sprintf("%d", i), false, false)
   210  		)
   211  		defer freezer.Close()
   212  
   213  		for i := 0; i < len(hs); i++ {
   214  			accountData, storageData, accountIndex, storageIndex := hs[i].encode()
   215  			rawdb.WriteStateHistory(freezer, uint64(i+1), hs[i].meta.encode(), accountIndex, storageIndex, accountData, storageData)
   216  			rawdb.WriteStateID(db, hs[i].meta.root, uint64(i+1))
   217  			roots = append(roots, hs[i].meta.root)
   218  		}
   219  		pruned, _ := truncateFromTail(db, freezer, uint64(10)-c.limit)
   220  		if pruned != c.expPruned {
   221  			t.Error("Unexpected pruned items", "want", c.expPruned, "got", pruned)
   222  		}
   223  		if c.empty {
   224  			checkHistoriesInRange(t, db, freezer, uint64(1), uint64(10), roots, false)
   225  		} else {
   226  			tail := 10 - int(c.limit)
   227  			checkHistoriesInRange(t, db, freezer, uint64(1), c.maxPruned, roots[:tail], false)
   228  			checkHistoriesInRange(t, db, freezer, c.minUnpruned, uint64(10), roots[tail:], true)
   229  		}
   230  	}
   231  }
   232  
   233  func TestTruncateOutOfRange(t *testing.T) {
   234  	var (
   235  		hs         = makeHistories(10)
   236  		db         = rawdb.NewMemoryDatabase()
   237  		freezer, _ = rawdb.NewStateFreezer(t.TempDir(), false, false)
   238  	)
   239  	defer freezer.Close()
   240  
   241  	for i := 0; i < len(hs); i++ {
   242  		accountData, storageData, accountIndex, storageIndex := hs[i].encode()
   243  		rawdb.WriteStateHistory(freezer, uint64(i+1), hs[i].meta.encode(), accountIndex, storageIndex, accountData, storageData)
   244  		rawdb.WriteStateID(db, hs[i].meta.root, uint64(i+1))
   245  	}
   246  	truncateFromTail(db, freezer, uint64(len(hs)/2))
   247  
   248  	// Ensure of-out-range truncations are rejected correctly.
   249  	head, _ := freezer.Ancients()
   250  	tail, _ := freezer.Tail()
   251  
   252  	cases := []struct {
   253  		mode   int
   254  		target uint64
   255  		expErr error
   256  	}{
   257  		{0, head, nil}, // nothing to delete
   258  		{0, head + 1, fmt.Errorf("out of range, tail: %d, head: %d, target: %d", tail, head, head+1)},
   259  		{0, tail - 1, fmt.Errorf("out of range, tail: %d, head: %d, target: %d", tail, head, tail-1)},
   260  		{1, tail, nil}, // nothing to delete
   261  		{1, head + 1, fmt.Errorf("out of range, tail: %d, head: %d, target: %d", tail, head, head+1)},
   262  		{1, tail - 1, fmt.Errorf("out of range, tail: %d, head: %d, target: %d", tail, head, tail-1)},
   263  	}
   264  	for _, c := range cases {
   265  		var gotErr error
   266  		if c.mode == 0 {
   267  			_, gotErr = truncateFromHead(db, freezer, c.target)
   268  		} else {
   269  			_, gotErr = truncateFromTail(db, freezer, c.target)
   270  		}
   271  		if !reflect.DeepEqual(gotErr, c.expErr) {
   272  			t.Errorf("Unexpected error, want: %v, got: %v", c.expErr, gotErr)
   273  		}
   274  	}
   275  }
   276  
   277  func compareSet[k comparable](a, b map[k][]byte) bool {
   278  	if len(a) != len(b) {
   279  		return false
   280  	}
   281  	for key, valA := range a {
   282  		valB, ok := b[key]
   283  		if !ok {
   284  			return false
   285  		}
   286  		if !bytes.Equal(valA, valB) {
   287  			return false
   288  		}
   289  	}
   290  	return true
   291  }
   292  
   293  func compareList[k comparable](a, b []k) bool {
   294  	if len(a) != len(b) {
   295  		return false
   296  	}
   297  	for i := 0; i < len(a); i++ {
   298  		if a[i] != b[i] {
   299  			return false
   300  		}
   301  	}
   302  	return true
   303  }
   304  
   305  func compareStorages(a, b map[common.Address]map[common.Hash][]byte) bool {
   306  	if len(a) != len(b) {
   307  		return false
   308  	}
   309  	for h, subA := range a {
   310  		subB, ok := b[h]
   311  		if !ok {
   312  			return false
   313  		}
   314  		if !compareSet(subA, subB) {
   315  			return false
   316  		}
   317  	}
   318  	return true
   319  }
   320  
   321  func compareStorageList(a, b map[common.Address][]common.Hash) bool {
   322  	if len(a) != len(b) {
   323  		return false
   324  	}
   325  	for h, la := range a {
   326  		lb, ok := b[h]
   327  		if !ok {
   328  			return false
   329  		}
   330  		if !compareList(la, lb) {
   331  			return false
   332  		}
   333  	}
   334  	return true
   335  }