github.com/alanchchen/go-ethereum@v1.6.6-0.20170601190819-6171d01b1195/trie/sync.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  package trie
    18  
    19  import (
    20  	"errors"
    21  	"fmt"
    22  
    23  	"github.com/ethereum/go-ethereum/common"
    24  	"gopkg.in/karalabe/cookiejar.v2/collections/prque"
    25  )
    26  
    27  // ErrNotRequested is returned by the trie sync when it's requested to process a
    28  // node it did not request.
    29  var ErrNotRequested = errors.New("not requested")
    30  
    31  // request represents a scheduled or already in-flight state retrieval request.
    32  type request struct {
    33  	hash common.Hash // Hash of the node data content to retrieve
    34  	data []byte      // Data content of the node, cached until all subtrees complete
    35  	raw  bool        // Whether this is a raw entry (code) or a trie node
    36  
    37  	parents []*request // Parent state nodes referencing this entry (notify all upon completion)
    38  	depth   int        // Depth level within the trie the node is located to prioritise DFS
    39  	deps    int        // Number of dependencies before allowed to commit this node
    40  
    41  	callback TrieSyncLeafCallback // Callback to invoke if a leaf node it reached on this branch
    42  }
    43  
    44  // SyncResult is a simple list to return missing nodes along with their request
    45  // hashes.
    46  type SyncResult struct {
    47  	Hash common.Hash // Hash of the originally unknown trie node
    48  	Data []byte      // Data content of the retrieved node
    49  }
    50  
    51  // TrieSyncLeafCallback is a callback type invoked when a trie sync reaches a
    52  // leaf node. It's used by state syncing to check if the leaf node requires some
    53  // further data syncing.
    54  type TrieSyncLeafCallback func(leaf []byte, parent common.Hash) error
    55  
    56  // TrieSync is the main state trie synchronisation scheduler, which provides yet
    57  // unknown trie hashes to retrieve, accepts node data associated with said hashes
    58  // and reconstructs the trie step by step until all is done.
    59  type TrieSync struct {
    60  	database DatabaseReader
    61  	requests map[common.Hash]*request // Pending requests pertaining to a key hash
    62  	queue    *prque.Prque             // Priority queue with the pending requests
    63  }
    64  
    65  // NewTrieSync creates a new trie data download scheduler.
    66  func NewTrieSync(root common.Hash, database DatabaseReader, callback TrieSyncLeafCallback) *TrieSync {
    67  	ts := &TrieSync{
    68  		database: database,
    69  		requests: make(map[common.Hash]*request),
    70  		queue:    prque.New(),
    71  	}
    72  	ts.AddSubTrie(root, 0, common.Hash{}, callback)
    73  	return ts
    74  }
    75  
    76  // AddSubTrie registers a new trie to the sync code, rooted at the designated parent.
    77  func (s *TrieSync) AddSubTrie(root common.Hash, depth int, parent common.Hash, callback TrieSyncLeafCallback) {
    78  	// Short circuit if the trie is empty or already known
    79  	if root == emptyRoot {
    80  		return
    81  	}
    82  	key := root.Bytes()
    83  	blob, _ := s.database.Get(key)
    84  	if local, err := decodeNode(key, blob, 0); local != nil && err == nil {
    85  		return
    86  	}
    87  	// Assemble the new sub-trie sync request
    88  	req := &request{
    89  		hash:     root,
    90  		depth:    depth,
    91  		callback: callback,
    92  	}
    93  	// If this sub-trie has a designated parent, link them together
    94  	if parent != (common.Hash{}) {
    95  		ancestor := s.requests[parent]
    96  		if ancestor == nil {
    97  			panic(fmt.Sprintf("sub-trie ancestor not found: %x", parent))
    98  		}
    99  		ancestor.deps++
   100  		req.parents = append(req.parents, ancestor)
   101  	}
   102  	s.schedule(req)
   103  }
   104  
   105  // AddRawEntry schedules the direct retrieval of a state entry that should not be
   106  // interpreted as a trie node, but rather accepted and stored into the database
   107  // as is. This method's goal is to support misc state metadata retrievals (e.g.
   108  // contract code).
   109  func (s *TrieSync) AddRawEntry(hash common.Hash, depth int, parent common.Hash) {
   110  	// Short circuit if the entry is empty or already known
   111  	if hash == emptyState {
   112  		return
   113  	}
   114  	if blob, _ := s.database.Get(hash.Bytes()); blob != nil {
   115  		return
   116  	}
   117  	// Assemble the new sub-trie sync request
   118  	req := &request{
   119  		hash:  hash,
   120  		raw:   true,
   121  		depth: depth,
   122  	}
   123  	// If this sub-trie has a designated parent, link them together
   124  	if parent != (common.Hash{}) {
   125  		ancestor := s.requests[parent]
   126  		if ancestor == nil {
   127  			panic(fmt.Sprintf("raw-entry ancestor not found: %x", parent))
   128  		}
   129  		ancestor.deps++
   130  		req.parents = append(req.parents, ancestor)
   131  	}
   132  	s.schedule(req)
   133  }
   134  
   135  // Missing retrieves the known missing nodes from the trie for retrieval.
   136  func (s *TrieSync) Missing(max int) []common.Hash {
   137  	requests := []common.Hash{}
   138  	for !s.queue.Empty() && (max == 0 || len(requests) < max) {
   139  		requests = append(requests, s.queue.PopItem().(common.Hash))
   140  	}
   141  	return requests
   142  }
   143  
   144  // Process injects a batch of retrieved trie nodes data, returning if something
   145  // was committed to the database and also the index of an entry if processing of
   146  // it failed.
   147  func (s *TrieSync) Process(results []SyncResult, dbw DatabaseWriter) (bool, int, error) {
   148  	committed := false
   149  
   150  	for i, item := range results {
   151  		// If the item was not requested, bail out
   152  		request := s.requests[item.Hash]
   153  		if request == nil {
   154  			return committed, i, ErrNotRequested
   155  		}
   156  		// If the item is a raw entry request, commit directly
   157  		if request.raw {
   158  			request.data = item.Data
   159  			s.commit(request, dbw)
   160  			committed = true
   161  			continue
   162  		}
   163  		// Decode the node data content and update the request
   164  		node, err := decodeNode(item.Hash[:], item.Data, 0)
   165  		if err != nil {
   166  			return committed, i, err
   167  		}
   168  		request.data = item.Data
   169  
   170  		// Create and schedule a request for all the children nodes
   171  		requests, err := s.children(request, node)
   172  		if err != nil {
   173  			return committed, i, err
   174  		}
   175  		if len(requests) == 0 && request.deps == 0 {
   176  			s.commit(request, dbw)
   177  			committed = true
   178  			continue
   179  		}
   180  		request.deps += len(requests)
   181  		for _, child := range requests {
   182  			s.schedule(child)
   183  		}
   184  	}
   185  	return committed, 0, nil
   186  }
   187  
   188  // Pending returns the number of state entries currently pending for download.
   189  func (s *TrieSync) Pending() int {
   190  	return len(s.requests)
   191  }
   192  
   193  // schedule inserts a new state retrieval request into the fetch queue. If there
   194  // is already a pending request for this node, the new request will be discarded
   195  // and only a parent reference added to the old one.
   196  func (s *TrieSync) schedule(req *request) {
   197  	// If we're already requesting this node, add a new reference and stop
   198  	if old, ok := s.requests[req.hash]; ok {
   199  		old.parents = append(old.parents, req.parents...)
   200  		return
   201  	}
   202  	// Schedule the request for future retrieval
   203  	s.queue.Push(req.hash, float32(req.depth))
   204  	s.requests[req.hash] = req
   205  }
   206  
   207  // children retrieves all the missing children of a state trie entry for future
   208  // retrieval scheduling.
   209  func (s *TrieSync) children(req *request, object node) ([]*request, error) {
   210  	// Gather all the children of the node, irrelevant whether known or not
   211  	type child struct {
   212  		node  node
   213  		depth int
   214  	}
   215  	children := []child{}
   216  
   217  	switch node := (object).(type) {
   218  	case *shortNode:
   219  		children = []child{{
   220  			node:  node.Val,
   221  			depth: req.depth + len(node.Key),
   222  		}}
   223  	case *fullNode:
   224  		for i := 0; i < 17; i++ {
   225  			if node.Children[i] != nil {
   226  				children = append(children, child{
   227  					node:  node.Children[i],
   228  					depth: req.depth + 1,
   229  				})
   230  			}
   231  		}
   232  	default:
   233  		panic(fmt.Sprintf("unknown node: %+v", node))
   234  	}
   235  	// Iterate over the children, and request all unknown ones
   236  	requests := make([]*request, 0, len(children))
   237  	for _, child := range children {
   238  		// Notify any external watcher of a new key/value node
   239  		if req.callback != nil {
   240  			if node, ok := (child.node).(valueNode); ok {
   241  				if err := req.callback(node, req.hash); err != nil {
   242  					return nil, err
   243  				}
   244  			}
   245  		}
   246  		// If the child references another node, resolve or schedule
   247  		if node, ok := (child.node).(hashNode); ok {
   248  			// Try to resolve the node from the local database
   249  			blob, _ := s.database.Get(node)
   250  			if local, err := decodeNode(node[:], blob, 0); local != nil && err == nil {
   251  				continue
   252  			}
   253  			// Locally unknown node, schedule for retrieval
   254  			requests = append(requests, &request{
   255  				hash:     common.BytesToHash(node),
   256  				parents:  []*request{req},
   257  				depth:    child.depth,
   258  				callback: req.callback,
   259  			})
   260  		}
   261  	}
   262  	return requests, nil
   263  }
   264  
   265  // commit finalizes a retrieval request and stores it into the database. If any
   266  // of the referencing parent requests complete due to this commit, they are also
   267  // committed themselves.
   268  func (s *TrieSync) commit(req *request, dbw DatabaseWriter) (err error) {
   269  	// Write the node content to disk
   270  	if err := dbw.Put(req.hash[:], req.data); err != nil {
   271  		return err
   272  	}
   273  	delete(s.requests, req.hash)
   274  
   275  	// Check all parents for completion
   276  	for _, parent := range req.parents {
   277  		parent.deps--
   278  		if parent.deps == 0 {
   279  			if err := s.commit(parent, dbw); err != nil {
   280  				return err
   281  			}
   282  		}
   283  	}
   284  	return nil
   285  }