github.com/decred/dcrlnd@v0.7.6/discovery/gossiper_state.go (about)

     1  package discovery
     2  
     3  import (
     4  	"errors"
     5  	"time"
     6  
     7  	"github.com/decred/dcrlnd/channeldb"
     8  	"github.com/decred/dcrlnd/routing/route"
     9  )
    10  
    11  // GossiperState is an interface that defines the functions necessary to persist
    12  // gossip data about a peer.
    13  type GossiperState interface {
    14  	// UpdatePeerLastGossipMsgTS stores the specified timestamp as the
    15  	// timestamp of the most recent gossip message received from the
    16  	// specified peer. If this timestamp is older than a previously stored
    17  	// timestamp, this function returns false and a nil error.
    18  	UpdatePeerLastGossipMsgTS(peer route.Vertex, ts time.Time) (bool, error)
    19  
    20  	// ReadPeerLastGossipMsgTS returns the timestamp stored as most recent
    21  	// for a gossip message received from the specified peer.
    22  	ReadPeerLastGossipMsgTS(peer route.Vertex) (time.Time, error)
    23  }
    24  
    25  // gossiperStateDB fulfills the GossiperState interface by using a backing
    26  // channeldb.DB instance.
    27  type gossiperStateDB struct {
    28  	db *channeldb.DB
    29  }
    30  
    31  // NewGossiperState initializes a new GossiperState using the specified channel
    32  // database for persistent storage.
    33  func NewGossiperState(db *channeldb.DB) GossiperState {
    34  	return &gossiperStateDB{db: db}
    35  }
    36  
    37  // UpdatePeerLastGossipMsgTS stores the specified timestamp as the
    38  // timestamp of the most recent gossip message received from the
    39  // specified peer. If this timestamp is older than a previously stored
    40  // timestamp, this function returns an error.
    41  func (gs *gossiperStateDB) UpdatePeerLastGossipMsgTS(peer route.Vertex, ts time.Time) (bool, error) {
    42  	err := gs.db.UpdatePeerLastGossipMsgTS(peer, ts)
    43  	if errors.Is(err, channeldb.ErrOutdatedLastGossipMsgTS) {
    44  		// We mute this error, because it could happen due to the
    45  		// ordering of processing the messages and it doesn't affect
    46  		// future queries.
    47  		log.Tracef("GossipSyncer(%s): Outdated ts %s",
    48  			peer, ts)
    49  		return false, nil
    50  	}
    51  	return err == nil, err
    52  }
    53  
    54  // ReadPeerLastGossipMsgTS returns the timestamp stored as most recent
    55  // for a gossip message received from the specified peer.
    56  func (gs *gossiperStateDB) ReadPeerLastGossipMsgTS(peer route.Vertex) (time.Time, error) {
    57  	return gs.db.ReadPeerLastGossipMsgTS(peer)
    58  }
    59  
    60  // updateGossiperMsgTS is called after processing a remote gossip message to
    61  // persist the state of the gossip syncer.
    62  func (d *AuthenticatedGossiper) updateGossiperMsgTS(peer route.Vertex, ts time.Time) {
    63  	syncer, ok := d.syncMgr.GossipSyncer(peer)
    64  	if !ok {
    65  		log.Warnf("Gossip syncer for peer=%s not found",
    66  			peer)
    67  		return
    68  	}
    69  
    70  	// Ignore updates when the timestamp is in the future, to avoid missing
    71  	// future updates when someone announced with an incorrect timestamp.
    72  	if ts.After(time.Now()) {
    73  		log.Tracef("GossipSyncer(%s): ignoring update to future ts %s",
    74  			peer, ts)
    75  		return
    76  	}
    77  
    78  	// Only update the stored last sync time if we're in active sync mode.
    79  	// Storing at other times could cause us to store a timestamp received
    80  	// from a historical search.
    81  	if syncer.syncState() != chansSynced || syncer.SyncType() != ActiveSync {
    82  		log.Tracef("GossipSyncer(%s): ignoring update to ts %s",
    83  			peer, ts)
    84  		return
    85  	}
    86  
    87  	updated, err := d.cfg.GossiperState.UpdatePeerLastGossipMsgTS(peer, ts)
    88  	if err != nil {
    89  		log.Warnf("GossipSyncer(%s): Unable to update last gossip msg ts: %v",
    90  			peer, err)
    91  		return
    92  	}
    93  
    94  	if updated {
    95  		log.Debugf("GossipSyncer(%s): Updated last gossip msg ts to %s",
    96  			peer, ts)
    97  	}
    98  }
    99  
   100  // initialGossipTimestamp returns the initial timestamp to use for the next
   101  // gossip timestamp range message. This is either the timestamp for the last
   102  // message we received from this syncer or the current time (if we never synced
   103  // to this peer before).
   104  func (g *GossipSyncer) initialGossipTimestamp() time.Time {
   105  	ts, err := g.cfg.gossiperState.ReadPeerLastGossipMsgTS(g.cfg.peerPub)
   106  	if err != nil {
   107  		return time.Now()
   108  	}
   109  
   110  	// When we wrongly stored the timestamp as a date in the future, use
   111  	// a date in the past as initial gossip timestamp. This prevents some
   112  	// classes of bugs where we never receive new gossip messages because
   113  	// we stored a timestamp in the future.
   114  	now := time.Now()
   115  	if ts.After(now) {
   116  		ts = now.Add(-time.Hour * 24)
   117  	}
   118  
   119  	return ts
   120  }