github.com/klaytn/klaytn@v1.12.1/blockchain/state/iterator.go (about)

     1  // Copyright 2015 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  // This file is derived from ethdb/iterator.go (2020/05/20).
    18  // Modified and improved for the klaytn development.
    19  
    20  package state
    21  
    22  import (
    23  	"bytes"
    24  	"fmt"
    25  	"time"
    26  
    27  	"github.com/klaytn/klaytn/blockchain/types/account"
    28  	"github.com/klaytn/klaytn/common"
    29  	"github.com/klaytn/klaytn/log"
    30  	"github.com/klaytn/klaytn/rlp"
    31  	"github.com/klaytn/klaytn/storage/statedb"
    32  	"github.com/pkg/errors"
    33  )
    34  
    35  var (
    36  	errIterator   = errors.New("iterator error")
    37  	errStopByQuit = errors.New("stopByQuit")
    38  )
    39  
    40  // NodeIterator is an iterator to traverse the entire state trie post-order,
    41  // including all of the contract code and contract state tries.
    42  type NodeIterator struct {
    43  	state *StateDB // State being iterated
    44  
    45  	stateIt statedb.NodeIterator // Primary iterator for the global state trie
    46  	dataIt  statedb.NodeIterator // Secondary iterator for the data trie of a contract
    47  
    48  	accountHash common.Hash // Hash of the node containing the account
    49  	codeHash    common.Hash // Hash of the contract source code
    50  	Code        []byte      // Source code associated with a contract
    51  
    52  	Type   string
    53  	Hash   common.Hash // Hash of the current entry being iterated (nil if not standalone)
    54  	Parent common.Hash // Hash of the first full ancestor node (nil if current is the root)
    55  	Path   []byte      // the hex-encoded path to the current node.
    56  
    57  	Error error // Failure set in case of an internal error in the iteratord
    58  }
    59  
    60  // NewNodeIterator creates an post-order state node iterator.
    61  func NewNodeIterator(state *StateDB) *NodeIterator {
    62  	return &NodeIterator{
    63  		state: state,
    64  	}
    65  }
    66  
    67  // Next moves the iterator to the next node, returning whether there are any
    68  // further nodes. In case of an internal error this method returns false and
    69  // sets the Error field to the encountered failure.
    70  func (it *NodeIterator) Next() bool {
    71  	// If the iterator failed previously, don't do anything
    72  	if it.Error != nil {
    73  		return false
    74  	}
    75  	// Otherwise step forward with the iterator and report any errors
    76  	if err := it.step(); err != nil {
    77  		it.Error = err
    78  		return false
    79  	}
    80  	return it.retrieve()
    81  }
    82  
    83  // step moves the iterator to the next entry of the state trie.
    84  func (it *NodeIterator) step() error {
    85  	// Abort if we reached the end of the iteration
    86  	if it.state == nil {
    87  		return nil
    88  	}
    89  	// Initialize the iterator if we've just started
    90  	if it.stateIt == nil {
    91  		it.stateIt = it.state.trie.NodeIterator(nil)
    92  	}
    93  	// If we had data nodes previously, we surely have at least state nodes
    94  	if it.dataIt != nil {
    95  		if cont := it.dataIt.Next(true); !cont {
    96  			if it.dataIt.Error() != nil {
    97  				return it.dataIt.Error()
    98  			}
    99  			it.dataIt = nil
   100  		}
   101  		return nil
   102  	}
   103  	// If we had source code previously, discard that
   104  	if it.Code != nil {
   105  		it.Code = nil
   106  		return nil
   107  	}
   108  	// Step to the next state trie node, terminating if we're out of nodes
   109  	if cont := it.stateIt.Next(true); !cont {
   110  		if it.stateIt.Error() != nil {
   111  			return it.stateIt.Error()
   112  		}
   113  		it.state, it.stateIt = nil, nil
   114  		return nil
   115  	}
   116  	// If the state trie node is an internal entry, leave as is
   117  	if !it.stateIt.Leaf() {
   118  		return nil
   119  	}
   120  	// Otherwise we've reached an account node, initiate data iteration
   121  
   122  	serializer := account.NewAccountSerializer()
   123  	if err := rlp.Decode(bytes.NewReader(it.stateIt.LeafBlob()), serializer); err != nil {
   124  		return err
   125  	}
   126  	obj := serializer.GetAccount()
   127  
   128  	if pa := account.GetProgramAccount(obj); pa != nil {
   129  		dataTrie, err := it.state.db.OpenStorageTrie(pa.GetStorageRoot(), nil)
   130  		if err != nil {
   131  			return err
   132  		}
   133  		it.dataIt = dataTrie.NodeIterator(nil)
   134  		if !it.dataIt.Next(true) {
   135  			it.dataIt = nil
   136  		}
   137  
   138  		if codeHash := pa.GetCodeHash(); !bytes.Equal(codeHash, emptyCodeHash) {
   139  			it.codeHash = common.BytesToHash(codeHash)
   140  			// addrHash := common.BytesToHash(it.stateIt.LeafKey())
   141  			it.Code, err = it.state.db.ContractCode(common.BytesToHash(codeHash))
   142  			if err != nil {
   143  				return fmt.Errorf("code %x: %v", codeHash, err)
   144  			}
   145  		}
   146  	}
   147  	it.accountHash = it.stateIt.Parent()
   148  	return nil
   149  }
   150  
   151  // retrieve pulls and caches the current state entry the iterator is traversing.
   152  // The method returns whether there are any more data left for inspection.
   153  func (it *NodeIterator) retrieve() bool {
   154  	// Clear out any previously set values
   155  	it.Hash = common.Hash{}
   156  	it.Path = []byte{}
   157  
   158  	// If the iteration's done, return no available data
   159  	if it.state == nil {
   160  		return false
   161  	}
   162  	// Otherwise retrieve the current entry
   163  	switch {
   164  	case it.dataIt != nil:
   165  		it.Type = "storage"
   166  		if it.dataIt.Leaf() {
   167  			it.Type = "storage_leaf"
   168  		}
   169  
   170  		it.Hash, it.Parent, it.Path = it.dataIt.Hash(), it.dataIt.Parent(), it.dataIt.Path()
   171  
   172  		if it.Parent == (common.Hash{}) {
   173  			it.Parent = it.accountHash
   174  		}
   175  	case it.Code != nil:
   176  		it.Type = "code"
   177  		it.Hash, it.Parent = it.codeHash, it.accountHash
   178  	case it.stateIt != nil:
   179  		it.Type = "state"
   180  		if it.stateIt.Leaf() {
   181  			it.Type = "state_leaf"
   182  		}
   183  
   184  		it.Hash, it.Parent, it.Path = it.stateIt.Hash(), it.stateIt.Parent(), it.stateIt.Path()
   185  	}
   186  	return true
   187  }
   188  
   189  // CheckStateConsistencyParallel checks the consistency of all state/storage trie of given two state databases in parallel.
   190  func CheckStateConsistencyParallel(oldDB Database, newDB Database, root common.Hash, quitCh chan struct{}) error {
   191  	// Check if the tries can be called
   192  	_, err := oldDB.OpenTrie(root, nil)
   193  	if err != nil {
   194  		return errors.WithMessage(err, "can not open oldDB trie")
   195  	}
   196  	_, err = newDB.OpenTrie(root, nil)
   197  	if err != nil {
   198  		return errors.WithMessage(err, "can not open newDB trie")
   199  	}
   200  	// get children hash
   201  	children, err := oldDB.TrieDB().NodeChildren(root.ExtendZero()) // does not work with Live Pruning
   202  	if err != nil {
   203  		logger.Error("cannot start CheckStateConsistencyParallel", "err", err)
   204  		return errors.WithMessage(err, "cannot get children before consistency check")
   205  	}
   206  
   207  	logger.Info("CheckStateConsistencyParallel is started", "root", root.String(), "len(children)", len(children))
   208  
   209  	iteratorQuitCh := make(chan struct{})
   210  	resultHashCh := make(chan struct{}, 10000)
   211  	resultErrCh := make(chan error)
   212  
   213  	// checks the consistency for each child in parallel
   214  	for _, child := range children {
   215  		go concurrentIterator(oldDB, newDB, child, iteratorQuitCh, resultHashCh, resultErrCh)
   216  	}
   217  
   218  	var resultErr error
   219  	cnt := 0
   220  	logged := time.Now()
   221  	for finishCnt := 0; finishCnt < len(children); {
   222  		select {
   223  		case <-resultHashCh:
   224  			cnt++
   225  			if time.Since(logged) > log.StatsReportLimit {
   226  				logged = time.Now()
   227  				logger.Info("CheckStateConsistencyParallel progress", "cnt", cnt)
   228  			}
   229  		case err := <-resultErrCh:
   230  			if err != nil {
   231  				logger.Warn("CheckStateConsistencyParallel got an error", "err", err)
   232  				close(iteratorQuitCh)
   233  				return err
   234  			}
   235  
   236  			finishCnt++
   237  			logger.Debug("CheckStateConsistencyParallel is being finished", "finishCnt", finishCnt, "err", err)
   238  		case <-quitCh:
   239  			close(iteratorQuitCh)
   240  			return nil
   241  		}
   242  	}
   243  	logger.Info("CheckStateConsistencyParallel is done", "cnt", cnt, "err", resultErr)
   244  
   245  	return resultErr
   246  }
   247  
   248  // concurrentIterator checks the consistency of all state/storage trie of given two state database
   249  // and pass the result via the channel. The 'root' here can be a subtrie root.
   250  // Does not work with Live Pruning.
   251  func concurrentIterator(oldDB Database, newDB Database, root common.ExtHash, quit chan struct{}, resultCh chan struct{}, finishCh chan error) (resultErr error) {
   252  	defer func() {
   253  		finishCh <- resultErr
   254  	}()
   255  
   256  	// Create and iterate a state trie rooted in a sub-node
   257  	oldState, err := New(root.Unextend(), oldDB, nil, nil)
   258  	if err != nil {
   259  		return errors.WithMessage(err, "can not open oldDB trie")
   260  	}
   261  
   262  	newState, err := New(root.Unextend(), newDB, nil, nil)
   263  	if err != nil {
   264  		return errors.WithMessage(err, "can not open newDB trie")
   265  	}
   266  
   267  	oldIt := NewNodeIterator(oldState)
   268  	newIt := NewNodeIterator(newState)
   269  
   270  	for oldIt.Next() {
   271  		if !newIt.Next() {
   272  			return fmt.Errorf("newDB iterator finished earlier : oldIt.Hash(%v) oldIt.Parent(%v) newIt.Error(%v)",
   273  				oldIt.Hash.String(), oldIt.Parent.String(), newIt.Error)
   274  		}
   275  
   276  		if oldIt.Hash != newIt.Hash {
   277  			return fmt.Errorf("mismatched hash oldIt.Hash : oldIt.Hash(%v) newIt.Hash(%v)", oldIt.Hash.String(), newIt.Hash.String())
   278  		}
   279  
   280  		if oldIt.Parent != newIt.Parent {
   281  			return fmt.Errorf("mismatched parent hash : oldIt.Parent(%v) newIt.Parent(%v)", oldIt.Parent.String(), newIt.Parent.String())
   282  		}
   283  
   284  		if !bytes.Equal(oldIt.Path, newIt.Path) {
   285  			return fmt.Errorf("mismatched path : oldIt.path(%v) newIt.path(%v)",
   286  				statedb.HexPathToString(oldIt.Path), statedb.HexPathToString(newIt.Path))
   287  		}
   288  
   289  		if oldIt.Code != nil {
   290  			if newIt.Code != nil {
   291  				if !bytes.Equal(oldIt.Code, newIt.Code) {
   292  					return fmt.Errorf("mismatched code : oldIt.Code(%v) newIt.Code(%v)", string(oldIt.Code), string(newIt.Code))
   293  				}
   294  			} else {
   295  				return fmt.Errorf("mismatched code : oldIt.Code(%v) newIt.Code(nil)", string(oldIt.Code))
   296  			}
   297  		} else {
   298  			if newIt.Code != nil {
   299  				return fmt.Errorf("mismatched code : oldIt.Code(nil) newIt.Code(%v)", string(newIt.Code))
   300  			}
   301  		}
   302  
   303  		resultCh <- struct{}{}
   304  
   305  		if quit != nil {
   306  			select {
   307  			case <-quit:
   308  				logger.Warn("CheckStateConsistency is stop")
   309  				return errStopByQuit
   310  			default:
   311  			}
   312  		}
   313  	}
   314  
   315  	if newIt.Next() {
   316  		return fmt.Errorf("oldDB iterator finished earlier  : newIt.Hash(%v) newIt.Parent(%v) oldIt.Error(%v)",
   317  			newIt.Hash, newIt.Parent, oldIt.Error)
   318  	}
   319  
   320  	if oldIt.Error != nil || newIt.Error != nil {
   321  		return fmt.Errorf("%w : oldIt.Error(%v), newIt.Error(%v)", errIterator, oldIt.Error, newIt.Error)
   322  	}
   323  
   324  	return nil
   325  }
   326  
   327  // CheckStateConsistency checks the consistency of all state/storage trie of given two state database.
   328  func CheckStateConsistency(oldDB Database, newDB Database, root common.Hash, mapSize int, quit chan struct{}) error {
   329  	// Create and iterate a state trie rooted in a sub-node
   330  	oldState, err := New(root, oldDB, nil, nil)
   331  	if err != nil {
   332  		return err
   333  	}
   334  
   335  	newState, err := New(root, newDB, nil, nil)
   336  	if err != nil {
   337  		return err
   338  	}
   339  
   340  	oldIt := NewNodeIterator(oldState)
   341  	newIt := NewNodeIterator(newState)
   342  
   343  	cnt := 0
   344  	nodes := make(map[common.Hash]struct{}, mapSize)
   345  
   346  	lastTime := time.Now()
   347  	report := func() {
   348  		elapsed := time.Since(lastTime)
   349  		if elapsed > log.StatsReportLimit {
   350  			logger.Info("CheckStateConsistency next",
   351  				"idx", cnt,
   352  				"type", oldIt.Type,
   353  				"hash", oldIt.Hash.String(),
   354  				"parent", oldIt.Parent.String(),
   355  				"path", statedb.HexPathToString(oldIt.Path))
   356  			lastTime = time.Now()
   357  		}
   358  	}
   359  
   360  	for oldIt.Next() {
   361  		cnt++
   362  		report()
   363  
   364  		if !newIt.Next() {
   365  			return fmt.Errorf("newDB iterator finished earlier : oldIt.Hash(%v) oldIt.Parent(%v) newIt.Error(%v)",
   366  				oldIt.Hash.String(), oldIt.Parent.String(), newIt.Error)
   367  		}
   368  
   369  		if oldIt.Hash != newIt.Hash {
   370  			return fmt.Errorf("mismatched hash oldIt.Hash : oldIt.Hash(%v) newIt.Hash(%v)", oldIt.Hash.String(), newIt.Hash.String())
   371  		}
   372  
   373  		if oldIt.Parent != newIt.Parent {
   374  			return fmt.Errorf("mismatched parent hash : oldIt.Parent(%v) newIt.Parent(%v)", oldIt.Parent.String(), newIt.Parent.String())
   375  		}
   376  
   377  		if !bytes.Equal(oldIt.Path, newIt.Path) {
   378  			return fmt.Errorf("mismatched path : oldIt.path(%v) newIt.path(%v)",
   379  				statedb.HexPathToString(oldIt.Path), statedb.HexPathToString(newIt.Path))
   380  		}
   381  
   382  		if oldIt.Code != nil {
   383  			if newIt.Code != nil {
   384  				if !bytes.Equal(oldIt.Code, newIt.Code) {
   385  					return fmt.Errorf("mismatched code : oldIt.Code(%v) newIt.Code(%v)", string(oldIt.Code), string(newIt.Code))
   386  				}
   387  			} else {
   388  				return fmt.Errorf("mismatched code : oldIt.Code(%v) newIt.Code(nil)", string(oldIt.Code))
   389  			}
   390  		} else {
   391  			if newIt.Code != nil {
   392  				return fmt.Errorf("mismatched code : oldIt.Code(nil) newIt.Code(%v)", string(newIt.Code))
   393  			}
   394  		}
   395  
   396  		if !common.EmptyHash(oldIt.Hash) {
   397  			nodes[oldIt.Hash] = struct{}{}
   398  		}
   399  
   400  		if quit != nil {
   401  			select {
   402  			case <-quit:
   403  				logger.Warn("CheckStateConsistency is stop", "cnt", cnt, "cnt without duplication", len(nodes))
   404  				return errStopByQuit
   405  			default:
   406  			}
   407  		}
   408  	}
   409  
   410  	if newIt.Next() {
   411  		return fmt.Errorf("oldDB iterator finished earlier  : newIt.Hash(%v) newIt.Parent(%v) oldIt.Error(%v)",
   412  			newIt.Hash, newIt.Parent, oldIt.Error)
   413  	}
   414  
   415  	if oldIt.Error != nil || newIt.Error != nil {
   416  		return fmt.Errorf("%w : oldIt.Error(%v), newIt.Error(%v)", errIterator, oldIt.Error, newIt.Error)
   417  	}
   418  
   419  	logger.Info("CheckStateConsistency is completed", "cnt", cnt, "cnt without duplication", len(nodes))
   420  	return nil
   421  }