github.com/theQRL/go-zond@v0.1.1/trie/triedb/pathdb/history_test.go (about)

     1  // Copyright 2022 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/theQRL/go-zond/common"
    26  	"github.com/theQRL/go-zond/core/rawdb"
    27  	"github.com/theQRL/go-zond/core/types"
    28  	"github.com/theQRL/go-zond/zonddb"
    29  	"github.com/theQRL/go-zond/rlp"
    30  	"github.com/theQRL/go-zond/trie/testutil"
    31  	"github.com/theQRL/go-zond/trie/triestate"
    32  )
    33  
    34  // randomStateSet generates a random state change set.
    35  func randomStateSet(n int) *triestate.Set {
    36  	var (
    37  		accounts = make(map[common.Address][]byte)
    38  		storages = make(map[common.Address]map[common.Hash][]byte)
    39  	)
    40  	for i := 0; i < n; i++ {
    41  		addr := testutil.RandomAddress()
    42  		storages[addr] = make(map[common.Hash][]byte)
    43  		for j := 0; j < 3; j++ {
    44  			v, _ := rlp.EncodeToBytes(common.TrimLeftZeroes(testutil.RandBytes(32)))
    45  			storages[addr][testutil.RandomHash()] = v
    46  		}
    47  		account := generateAccount(types.EmptyRootHash)
    48  		accounts[addr] = types.SlimAccountRLP(account)
    49  	}
    50  	return triestate.New(accounts, storages, nil)
    51  }
    52  
    53  func makeHistory() *history {
    54  	return newHistory(testutil.RandomHash(), types.EmptyRootHash, 0, randomStateSet(3))
    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 := testutil.RandomHash()
    64  		h := newHistory(root, parent, uint64(i), randomStateSet(3))
    65  		parent = root
    66  		result = append(result, h)
    67  	}
    68  	return result
    69  }
    70  
    71  func TestEncodeDecodeHistory(t *testing.T) {
    72  	var (
    73  		m   meta
    74  		dec history
    75  		obj = makeHistory()
    76  	)
    77  	// check if meta data can be correctly encode/decode
    78  	blob := obj.meta.encode()
    79  	if err := m.decode(blob); err != nil {
    80  		t.Fatalf("Failed to decode %v", err)
    81  	}
    82  	if !reflect.DeepEqual(&m, obj.meta) {
    83  		t.Fatal("meta is mismatched")
    84  	}
    85  
    86  	// check if account/storage data can be correctly encode/decode
    87  	accountData, storageData, accountIndexes, storageIndexes := obj.encode()
    88  	if err := dec.decode(accountData, storageData, accountIndexes, storageIndexes); err != nil {
    89  		t.Fatalf("Failed to decode, err: %v", err)
    90  	}
    91  	if !compareSet(dec.accounts, obj.accounts) {
    92  		t.Fatal("account data is mismatched")
    93  	}
    94  	if !compareStorages(dec.storages, obj.storages) {
    95  		t.Fatal("storage data is mismatched")
    96  	}
    97  	if !compareList(dec.accountList, obj.accountList) {
    98  		t.Fatal("account list is mismatched")
    99  	}
   100  	if !compareStorageList(dec.storageList, obj.storageList) {
   101  		t.Fatal("storage list is mismatched")
   102  	}
   103  }
   104  
   105  func checkHistory(t *testing.T, db zonddb.KeyValueReader, freezer *rawdb.ResettableFreezer, id uint64, root common.Hash, exist bool) {
   106  	blob := rawdb.ReadStateHistoryMeta(freezer, id)
   107  	if exist && len(blob) == 0 {
   108  		t.Fatalf("Failed to load trie history, %d", id)
   109  	}
   110  	if !exist && len(blob) != 0 {
   111  		t.Fatalf("Unexpected trie history, %d", id)
   112  	}
   113  	if exist && rawdb.ReadStateID(db, root) == nil {
   114  		t.Fatalf("Root->ID mapping is not found, %d", id)
   115  	}
   116  	if !exist && rawdb.ReadStateID(db, root) != nil {
   117  		t.Fatalf("Unexpected root->ID mapping, %d", id)
   118  	}
   119  }
   120  
   121  func checkHistoriesInRange(t *testing.T, db zonddb.KeyValueReader, freezer *rawdb.ResettableFreezer, from, to uint64, roots []common.Hash, exist bool) {
   122  	for i, j := from, 0; i <= to; i, j = i+1, j+1 {
   123  		checkHistory(t, db, freezer, i, roots[j], exist)
   124  	}
   125  }
   126  
   127  func TestTruncateHeadHistory(t *testing.T) {
   128  	var (
   129  		roots      []common.Hash
   130  		hs         = makeHistories(10)
   131  		db         = rawdb.NewMemoryDatabase()
   132  		freezer, _ = openFreezer(t.TempDir(), false)
   133  	)
   134  	defer freezer.Close()
   135  
   136  	for i := 0; i < len(hs); i++ {
   137  		accountData, storageData, accountIndex, storageIndex := hs[i].encode()
   138  		rawdb.WriteStateHistory(freezer, uint64(i+1), hs[i].meta.encode(), accountIndex, storageIndex, accountData, storageData)
   139  		rawdb.WriteStateID(db, hs[i].meta.root, uint64(i+1))
   140  		roots = append(roots, hs[i].meta.root)
   141  	}
   142  	for size := len(hs); size > 0; size-- {
   143  		pruned, err := truncateFromHead(db, freezer, uint64(size-1))
   144  		if err != nil {
   145  			t.Fatalf("Failed to truncate from head %v", err)
   146  		}
   147  		if pruned != 1 {
   148  			t.Error("Unexpected pruned items", "want", 1, "got", pruned)
   149  		}
   150  		checkHistoriesInRange(t, db, freezer, uint64(size), uint64(10), roots[size-1:], false)
   151  		checkHistoriesInRange(t, db, freezer, uint64(1), uint64(size-1), roots[:size-1], true)
   152  	}
   153  }
   154  
   155  func TestTruncateTailHistory(t *testing.T) {
   156  	var (
   157  		roots      []common.Hash
   158  		hs         = makeHistories(10)
   159  		db         = rawdb.NewMemoryDatabase()
   160  		freezer, _ = openFreezer(t.TempDir(), false)
   161  	)
   162  	defer freezer.Close()
   163  
   164  	for i := 0; i < len(hs); i++ {
   165  		accountData, storageData, accountIndex, storageIndex := hs[i].encode()
   166  		rawdb.WriteStateHistory(freezer, uint64(i+1), hs[i].meta.encode(), accountIndex, storageIndex, accountData, storageData)
   167  		rawdb.WriteStateID(db, hs[i].meta.root, uint64(i+1))
   168  		roots = append(roots, hs[i].meta.root)
   169  	}
   170  	for newTail := 1; newTail < len(hs); newTail++ {
   171  		pruned, _ := truncateFromTail(db, freezer, uint64(newTail))
   172  		if pruned != 1 {
   173  			t.Error("Unexpected pruned items", "want", 1, "got", pruned)
   174  		}
   175  		checkHistoriesInRange(t, db, freezer, uint64(1), uint64(newTail), roots[:newTail], false)
   176  		checkHistoriesInRange(t, db, freezer, uint64(newTail+1), uint64(10), roots[newTail:], true)
   177  	}
   178  }
   179  
   180  func TestTruncateTailHistories(t *testing.T) {
   181  	var cases = []struct {
   182  		limit       uint64
   183  		expPruned   int
   184  		maxPruned   uint64
   185  		minUnpruned uint64
   186  		empty       bool
   187  	}{
   188  		{
   189  			1, 9, 9, 10, false,
   190  		},
   191  		{
   192  			0, 10, 10, 0 /* no meaning */, true,
   193  		},
   194  		{
   195  			10, 0, 0, 1, false,
   196  		},
   197  	}
   198  	for i, c := range cases {
   199  		var (
   200  			roots      []common.Hash
   201  			hs         = makeHistories(10)
   202  			db         = rawdb.NewMemoryDatabase()
   203  			freezer, _ = openFreezer(t.TempDir()+fmt.Sprintf("%d", i), false)
   204  		)
   205  		defer freezer.Close()
   206  
   207  		for i := 0; i < len(hs); i++ {
   208  			accountData, storageData, accountIndex, storageIndex := hs[i].encode()
   209  			rawdb.WriteStateHistory(freezer, uint64(i+1), hs[i].meta.encode(), accountIndex, storageIndex, accountData, storageData)
   210  			rawdb.WriteStateID(db, hs[i].meta.root, uint64(i+1))
   211  			roots = append(roots, hs[i].meta.root)
   212  		}
   213  		pruned, _ := truncateFromTail(db, freezer, uint64(10)-c.limit)
   214  		if pruned != c.expPruned {
   215  			t.Error("Unexpected pruned items", "want", c.expPruned, "got", pruned)
   216  		}
   217  		if c.empty {
   218  			checkHistoriesInRange(t, db, freezer, uint64(1), uint64(10), roots, false)
   219  		} else {
   220  			tail := 10 - int(c.limit)
   221  			checkHistoriesInRange(t, db, freezer, uint64(1), c.maxPruned, roots[:tail], false)
   222  			checkHistoriesInRange(t, db, freezer, c.minUnpruned, uint64(10), roots[tail:], true)
   223  		}
   224  	}
   225  }
   226  
   227  // openFreezer initializes the freezer instance for storing state histories.
   228  func openFreezer(datadir string, readOnly bool) (*rawdb.ResettableFreezer, error) {
   229  	return rawdb.NewStateFreezer(datadir, readOnly)
   230  }
   231  
   232  func compareSet[k comparable](a, b map[k][]byte) bool {
   233  	if len(a) != len(b) {
   234  		return false
   235  	}
   236  	for key, valA := range a {
   237  		valB, ok := b[key]
   238  		if !ok {
   239  			return false
   240  		}
   241  		if !bytes.Equal(valA, valB) {
   242  			return false
   243  		}
   244  	}
   245  	return true
   246  }
   247  
   248  func compareList[k comparable](a, b []k) bool {
   249  	if len(a) != len(b) {
   250  		return false
   251  	}
   252  	for i := 0; i < len(a); i++ {
   253  		if a[i] != b[i] {
   254  			return false
   255  		}
   256  	}
   257  	return true
   258  }
   259  
   260  func compareStorages(a, b map[common.Address]map[common.Hash][]byte) bool {
   261  	if len(a) != len(b) {
   262  		return false
   263  	}
   264  	for h, subA := range a {
   265  		subB, ok := b[h]
   266  		if !ok {
   267  			return false
   268  		}
   269  		if !compareSet(subA, subB) {
   270  			return false
   271  		}
   272  	}
   273  	return true
   274  }
   275  
   276  func compareStorageList(a, b map[common.Address][]common.Hash) bool {
   277  	if len(a) != len(b) {
   278  		return false
   279  	}
   280  	for h, la := range a {
   281  		lb, ok := b[h]
   282  		if !ok {
   283  			return false
   284  		}
   285  		if !compareList(la, lb) {
   286  			return false
   287  		}
   288  	}
   289  	return true
   290  }