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

     1  // Copyright 2025 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  	"testing"
    23  	"time"
    24  
    25  	"github.com/ethereum/go-ethereum/common"
    26  	"github.com/ethereum/go-ethereum/core/rawdb"
    27  )
    28  
    29  func waitIndexing(db *Database) {
    30  	for {
    31  		metadata := loadIndexMetadata(db.diskdb)
    32  		if metadata != nil && metadata.Last >= db.tree.bottom().stateID() {
    33  			return
    34  		}
    35  		time.Sleep(100 * time.Millisecond)
    36  	}
    37  }
    38  
    39  func checkHistoricState(env *tester, root common.Hash, hr *historyReader) error {
    40  	// Short circuit if the historical state is no longer available
    41  	if rawdb.ReadStateID(env.db.diskdb, root) == nil {
    42  		return nil
    43  	}
    44  	var (
    45  		dl       = env.db.tree.bottom()
    46  		stateID  = rawdb.ReadStateID(env.db.diskdb, root)
    47  		accounts = env.snapAccounts[root]
    48  		storages = env.snapStorages[root]
    49  	)
    50  	for addrHash, accountData := range accounts {
    51  		latest, _ := dl.account(addrHash, 0)
    52  		blob, err := hr.read(newAccountIdentQuery(env.accountPreimage(addrHash), addrHash), *stateID, dl.stateID(), latest)
    53  		if err != nil {
    54  			return err
    55  		}
    56  		if !bytes.Equal(accountData, blob) {
    57  			return fmt.Errorf("wrong account data, expected %x, got %x", accountData, blob)
    58  		}
    59  	}
    60  	for i := 0; i < len(env.roots); i++ {
    61  		if env.roots[i] == root {
    62  			break
    63  		}
    64  		// Find all accounts deleted in the past, ensure the associated data is null
    65  		for addrHash := range env.snapAccounts[env.roots[i]] {
    66  			if _, ok := accounts[addrHash]; !ok {
    67  				latest, _ := dl.account(addrHash, 0)
    68  				blob, err := hr.read(newAccountIdentQuery(env.accountPreimage(addrHash), addrHash), *stateID, dl.stateID(), latest)
    69  				if err != nil {
    70  					return err
    71  				}
    72  				if len(blob) != 0 {
    73  					return fmt.Errorf("wrong account data, expected null, got %x", blob)
    74  				}
    75  			}
    76  		}
    77  	}
    78  	for addrHash, slots := range storages {
    79  		for slotHash, slotData := range slots {
    80  			latest, _ := dl.storage(addrHash, slotHash, 0)
    81  			blob, err := hr.read(newStorageIdentQuery(env.accountPreimage(addrHash), addrHash, env.hashPreimage(slotHash), slotHash), *stateID, dl.stateID(), latest)
    82  			if err != nil {
    83  				return err
    84  			}
    85  			if !bytes.Equal(slotData, blob) {
    86  				return fmt.Errorf("wrong storage data, expected %x, got %x", slotData, blob)
    87  			}
    88  		}
    89  	}
    90  	for i := 0; i < len(env.roots); i++ {
    91  		if env.roots[i] == root {
    92  			break
    93  		}
    94  		// Find all storage slots deleted in the past, ensure the associated data is null
    95  		for addrHash, slots := range env.snapStorages[env.roots[i]] {
    96  			for slotHash := range slots {
    97  				_, ok := storages[addrHash]
    98  				if ok {
    99  					_, ok = storages[addrHash][slotHash]
   100  				}
   101  				if !ok {
   102  					latest, _ := dl.storage(addrHash, slotHash, 0)
   103  					blob, err := hr.read(newStorageIdentQuery(env.accountPreimage(addrHash), addrHash, env.hashPreimage(slotHash), slotHash), *stateID, dl.stateID(), latest)
   104  					if err != nil {
   105  						return err
   106  					}
   107  					if len(blob) != 0 {
   108  						return fmt.Errorf("wrong storage data, expected null, got %x", blob)
   109  					}
   110  				}
   111  			}
   112  		}
   113  	}
   114  	return nil
   115  }
   116  
   117  func TestHistoryReader(t *testing.T) {
   118  	testHistoryReader(t, 0)  // with all histories reserved
   119  	testHistoryReader(t, 10) // with latest 10 histories reserved
   120  }
   121  
   122  func testHistoryReader(t *testing.T, historyLimit uint64) {
   123  	maxDiffLayers = 4
   124  	defer func() {
   125  		maxDiffLayers = 128
   126  	}()
   127  	//log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelDebug, true)))
   128  
   129  	env := newTester(t, historyLimit, false, 64, true)
   130  	defer env.release()
   131  	waitIndexing(env.db)
   132  
   133  	var (
   134  		roots = env.roots
   135  		dRoot = env.db.tree.bottom().rootHash()
   136  		hr    = newHistoryReader(env.db.diskdb, env.db.freezer)
   137  	)
   138  	for _, root := range roots {
   139  		if root == dRoot {
   140  			break
   141  		}
   142  		if err := checkHistoricState(env, root, hr); err != nil {
   143  			t.Fatal(err)
   144  		}
   145  	}
   146  
   147  	// Pile up more histories on top, ensuring the historic reader is not affected
   148  	env.extend(4)
   149  	waitIndexing(env.db)
   150  
   151  	for _, root := range roots {
   152  		if root == dRoot {
   153  			break
   154  		}
   155  		if err := checkHistoricState(env, root, hr); err != nil {
   156  			t.Fatal(err)
   157  		}
   158  	}
   159  }