github.com/decred/dcrlnd@v0.7.6/channeldb/dcrmigrations/migration02/dcrmigration02.go (about)

     1  package dcrmigration02
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"io"
     7  	"time"
     8  
     9  	"github.com/decred/dcrlnd/kvdb"
    10  )
    11  
    12  const (
    13  	// pastOffsetFix is how much to rewind the date of the last gossip
    14  	// timestamp from now.
    15  	pastOffsetFix = time.Hour * 72
    16  )
    17  
    18  var (
    19  	// peersBucket is the key for the bucket that holds per-gossip-peer
    20  	// info.
    21  	peersBucket = []byte("peers-bucket")
    22  
    23  	// peerLastGossipMsgTSKey is the key for a value in the peer bucket that
    24  	// tracks the unix timestamp of the last received gossip message
    25  	// (ChannelUpdate, ChannelAnnouncement, NodeAnnounce).
    26  	peerLastGossipMsgTSKey = []byte("last-gossip-msg-ts")
    27  
    28  	// Big endian is the preferred byte order, due to cursor scans over
    29  	// integer keys iterating in order.
    30  	byteOrder = binary.BigEndian
    31  
    32  	// nowFunc is modified during tests to ensure a consistent now.
    33  	nowFunc = time.Now
    34  )
    35  
    36  // deserializeTime deserializes time as unix nanoseconds.
    37  func deserializeTime(r io.Reader) (time.Time, error) {
    38  	var scratch [8]byte
    39  	if _, err := io.ReadFull(r, scratch[:]); err != nil {
    40  		return time.Time{}, err
    41  	}
    42  
    43  	// Convert to time.Time. Interpret unix nano time zero as a zero
    44  	// time.Time value.
    45  	unixNano := byteOrder.Uint64(scratch[:])
    46  	if unixNano == 0 {
    47  		return time.Time{}, nil
    48  	}
    49  
    50  	return time.Unix(0, int64(unixNano)), nil
    51  }
    52  
    53  // serializeTime serializes time as unix nanoseconds.
    54  func serializeTime(w io.Writer, t time.Time) error {
    55  	var scratch [8]byte
    56  
    57  	// Convert to unix nano seconds, but only if time is non-zero. Calling
    58  	// UnixNano() on a zero time yields an undefined result.
    59  	var unixNano int64
    60  	if !t.IsZero() {
    61  		unixNano = t.UnixNano()
    62  	}
    63  
    64  	byteOrder.PutUint64(scratch[:], uint64(unixNano))
    65  	_, err := w.Write(scratch[:])
    66  	return err
    67  }
    68  
    69  // RemoveFutureTimestampFromPeers fixes a bug that caused the db to store
    70  // a future date as the latest timestamp for gossip messages, causing the peer
    71  // to fail to request gossip updates.
    72  func RemoveFutureTimestampFromPeers(tx kvdb.RwTx) error {
    73  	// If the timestamp is in the future, set it as 72h in the past to
    74  	// ensure we get any new channel updates.
    75  	now := nowFunc()
    76  	newTs := now.Add(-pastOffsetFix)
    77  
    78  	var b bytes.Buffer
    79  	err := serializeTime(&b, newTs)
    80  	if err != nil {
    81  		return err
    82  	}
    83  
    84  	// Get the target bucket.
    85  	var migrated int
    86  	bucket := tx.ReadWriteBucket(peersBucket)
    87  	err = bucket.ForEach(func(k, v []byte) error {
    88  		peerBucket := bucket.NestedReadWriteBucket(k)
    89  		if peerBucket == nil {
    90  			return nil
    91  		}
    92  
    93  		tsBytes := peerBucket.Get(peerLastGossipMsgTSKey)
    94  		if tsBytes == nil {
    95  			return nil
    96  		}
    97  
    98  		ts, err := deserializeTime(bytes.NewReader(tsBytes))
    99  		if err != nil {
   100  			return nil
   101  		}
   102  
   103  		if !ts.After(now) {
   104  			return nil
   105  		}
   106  
   107  		// Entry needs to be migrated.
   108  		migrated++
   109  		return peerBucket.Put(peerLastGossipMsgTSKey, b.Bytes())
   110  	})
   111  
   112  	if err != nil {
   113  		return err
   114  	}
   115  
   116  	log.Infof("Migrated %d future timestamps to %s", migrated,
   117  		newTs.Format(time.RFC3339))
   118  	return nil
   119  }