github.com/flexpool/go-ethereum@v1.9.7/p2p/dnsdisc/sync.go (about)

     1  // Copyright 2019 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 dnsdisc
    18  
    19  import (
    20  	"context"
    21  	"crypto/ecdsa"
    22  	"math/rand"
    23  	"time"
    24  
    25  	"github.com/ethereum/go-ethereum/common/mclock"
    26  	"github.com/ethereum/go-ethereum/p2p/enode"
    27  )
    28  
    29  // clientTree is a full tree being synced.
    30  type clientTree struct {
    31  	c             *Client
    32  	loc           *linkEntry
    33  	root          *rootEntry
    34  	lastRootCheck mclock.AbsTime // last revalidation of root
    35  	enrs          *subtreeSync
    36  	links         *subtreeSync
    37  	linkCache     linkCache
    38  }
    39  
    40  func newClientTree(c *Client, loc *linkEntry) *clientTree {
    41  	ct := &clientTree{c: c, loc: loc}
    42  	ct.linkCache.self = ct
    43  	return ct
    44  }
    45  
    46  func (ct *clientTree) matchPubkey(key *ecdsa.PublicKey) bool {
    47  	return keysEqual(ct.loc.pubkey, key)
    48  }
    49  
    50  func keysEqual(k1, k2 *ecdsa.PublicKey) bool {
    51  	return k1.Curve == k2.Curve && k1.X.Cmp(k2.X) == 0 && k1.Y.Cmp(k2.Y) == 0
    52  }
    53  
    54  // syncAll retrieves all entries of the tree.
    55  func (ct *clientTree) syncAll(dest map[string]entry) error {
    56  	if err := ct.updateRoot(); err != nil {
    57  		return err
    58  	}
    59  	if err := ct.links.resolveAll(dest); err != nil {
    60  		return err
    61  	}
    62  	if err := ct.enrs.resolveAll(dest); err != nil {
    63  		return err
    64  	}
    65  	return nil
    66  }
    67  
    68  // syncRandom retrieves a single entry of the tree. The Node return value
    69  // is non-nil if the entry was a node.
    70  func (ct *clientTree) syncRandom(ctx context.Context) (*enode.Node, error) {
    71  	if ct.rootUpdateDue() {
    72  		if err := ct.updateRoot(); err != nil {
    73  			return nil, err
    74  		}
    75  	}
    76  	// Link tree sync has priority, run it to completion before syncing ENRs.
    77  	if !ct.links.done() {
    78  		err := ct.syncNextLink(ctx)
    79  		return nil, err
    80  	}
    81  
    82  	// Sync next random entry in ENR tree. Once every node has been visited, we simply
    83  	// start over. This is fine because entries are cached.
    84  	if ct.enrs.done() {
    85  		ct.enrs = newSubtreeSync(ct.c, ct.loc, ct.root.eroot, false)
    86  	}
    87  	return ct.syncNextRandomENR(ctx)
    88  }
    89  
    90  func (ct *clientTree) syncNextLink(ctx context.Context) error {
    91  	hash := ct.links.missing[0]
    92  	e, err := ct.links.resolveNext(ctx, hash)
    93  	if err != nil {
    94  		return err
    95  	}
    96  	ct.links.missing = ct.links.missing[1:]
    97  
    98  	if le, ok := e.(*linkEntry); ok {
    99  		lt, err := ct.c.ensureTree(le)
   100  		if err != nil {
   101  			return err
   102  		}
   103  		ct.linkCache.add(lt)
   104  	}
   105  	return nil
   106  }
   107  
   108  func (ct *clientTree) syncNextRandomENR(ctx context.Context) (*enode.Node, error) {
   109  	index := rand.Intn(len(ct.enrs.missing))
   110  	hash := ct.enrs.missing[index]
   111  	e, err := ct.enrs.resolveNext(ctx, hash)
   112  	if err != nil {
   113  		return nil, err
   114  	}
   115  	ct.enrs.missing = removeHash(ct.enrs.missing, index)
   116  	if ee, ok := e.(*enrEntry); ok {
   117  		return ee.node, nil
   118  	}
   119  	return nil, nil
   120  }
   121  
   122  func (ct *clientTree) String() string {
   123  	return ct.loc.String()
   124  }
   125  
   126  // removeHash removes the element at index from h.
   127  func removeHash(h []string, index int) []string {
   128  	if len(h) == 1 {
   129  		return nil
   130  	}
   131  	last := len(h) - 1
   132  	if index < last {
   133  		h[index] = h[last]
   134  		h[last] = ""
   135  	}
   136  	return h[:last]
   137  }
   138  
   139  // updateRoot ensures that the given tree has an up-to-date root.
   140  func (ct *clientTree) updateRoot() error {
   141  	ct.lastRootCheck = ct.c.clock.Now()
   142  	ctx, cancel := context.WithTimeout(context.Background(), ct.c.cfg.Timeout)
   143  	defer cancel()
   144  	root, err := ct.c.resolveRoot(ctx, ct.loc)
   145  	if err != nil {
   146  		return err
   147  	}
   148  	ct.root = &root
   149  
   150  	// Invalidate subtrees if changed.
   151  	if ct.links == nil || root.lroot != ct.links.root {
   152  		ct.links = newSubtreeSync(ct.c, ct.loc, root.lroot, true)
   153  		ct.linkCache.reset()
   154  	}
   155  	if ct.enrs == nil || root.eroot != ct.enrs.root {
   156  		ct.enrs = newSubtreeSync(ct.c, ct.loc, root.eroot, false)
   157  	}
   158  	return nil
   159  }
   160  
   161  // rootUpdateDue returns true when a root update is needed.
   162  func (ct *clientTree) rootUpdateDue() bool {
   163  	return ct.root == nil || time.Duration(ct.c.clock.Now()-ct.lastRootCheck) > ct.c.cfg.RecheckInterval
   164  }
   165  
   166  // subtreeSync is the sync of an ENR or link subtree.
   167  type subtreeSync struct {
   168  	c       *Client
   169  	loc     *linkEntry
   170  	root    string
   171  	missing []string // missing tree node hashes
   172  	link    bool     // true if this sync is for the link tree
   173  }
   174  
   175  func newSubtreeSync(c *Client, loc *linkEntry, root string, link bool) *subtreeSync {
   176  	return &subtreeSync{c, loc, root, []string{root}, link}
   177  }
   178  
   179  func (ts *subtreeSync) done() bool {
   180  	return len(ts.missing) == 0
   181  }
   182  
   183  func (ts *subtreeSync) resolveAll(dest map[string]entry) error {
   184  	for !ts.done() {
   185  		hash := ts.missing[0]
   186  		ctx, cancel := context.WithTimeout(context.Background(), ts.c.cfg.Timeout)
   187  		e, err := ts.resolveNext(ctx, hash)
   188  		cancel()
   189  		if err != nil {
   190  			return err
   191  		}
   192  		dest[hash] = e
   193  		ts.missing = ts.missing[1:]
   194  	}
   195  	return nil
   196  }
   197  
   198  func (ts *subtreeSync) resolveNext(ctx context.Context, hash string) (entry, error) {
   199  	e, err := ts.c.resolveEntry(ctx, ts.loc.domain, hash)
   200  	if err != nil {
   201  		return nil, err
   202  	}
   203  	switch e := e.(type) {
   204  	case *enrEntry:
   205  		if ts.link {
   206  			return nil, errENRInLinkTree
   207  		}
   208  	case *linkEntry:
   209  		if !ts.link {
   210  			return nil, errLinkInENRTree
   211  		}
   212  	case *branchEntry:
   213  		ts.missing = append(ts.missing, e.children...)
   214  	}
   215  	return e, nil
   216  }
   217  
   218  // linkCache tracks the links of a tree.
   219  type linkCache struct {
   220  	self    *clientTree
   221  	directM map[*clientTree]struct{} // direct links
   222  	allM    map[*clientTree]struct{} // direct & transitive links
   223  }
   224  
   225  // reset clears the cache.
   226  func (lc *linkCache) reset() {
   227  	lc.directM = nil
   228  	lc.allM = nil
   229  }
   230  
   231  // add adds a direct link to the cache.
   232  func (lc *linkCache) add(ct *clientTree) {
   233  	if lc.directM == nil {
   234  		lc.directM = make(map[*clientTree]struct{})
   235  	}
   236  	if _, ok := lc.directM[ct]; !ok {
   237  		lc.invalidate()
   238  	}
   239  	lc.directM[ct] = struct{}{}
   240  }
   241  
   242  // invalidate resets the cache of transitive links.
   243  func (lc *linkCache) invalidate() {
   244  	lc.allM = nil
   245  }
   246  
   247  // valid returns true when the cache of transitive links is up-to-date.
   248  func (lc *linkCache) valid() bool {
   249  	// Re-check validity of child caches to catch updates.
   250  	for ct := range lc.allM {
   251  		if ct != lc.self && !ct.linkCache.valid() {
   252  			lc.allM = nil
   253  			break
   254  		}
   255  	}
   256  	return lc.allM != nil
   257  }
   258  
   259  // all returns all trees reachable through the cache.
   260  func (lc *linkCache) all() map[*clientTree]struct{} {
   261  	if lc.valid() {
   262  		return lc.allM
   263  	}
   264  	// Remake lc.allM it by taking the union of all() across children.
   265  	m := make(map[*clientTree]struct{})
   266  	if lc.self != nil {
   267  		m[lc.self] = struct{}{}
   268  	}
   269  	for ct := range lc.directM {
   270  		m[ct] = struct{}{}
   271  		for lt := range ct.linkCache.all() {
   272  			m[lt] = struct{}{}
   273  		}
   274  	}
   275  	lc.allM = m
   276  	return m
   277  }