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 }