github.com/aigarnetwork/aigar@v0.0.0-20191115204914-d59a6eb70f8e/p2p/dnsdisc/sync.go (about)

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