github.com/status-im/status-go@v1.1.0/services/ext/mailservers/cache.go (about) 1 package mailservers 2 3 import ( 4 "encoding/json" 5 "time" 6 7 "github.com/syndtr/goleveldb/leveldb" 8 "github.com/syndtr/goleveldb/leveldb/iterator" 9 "github.com/syndtr/goleveldb/leveldb/util" 10 11 "github.com/ethereum/go-ethereum/p2p/enode" 12 13 "github.com/status-im/status-go/db" 14 "github.com/status-im/status-go/eth-node/types" 15 ) 16 17 // NewPeerRecord returns instance of the peer record. 18 func NewPeerRecord(node *enode.Node) PeerRecord { 19 return PeerRecord{node: node} 20 } 21 22 // PeerRecord is set data associated with each peer that is stored on disk. 23 // PeerRecord stored with a enode as a key in leveldb, and body marshalled as json. 24 type PeerRecord struct { 25 node *enode.Node 26 27 // last time it was used. 28 LastUsed time.Time 29 } 30 31 // Encode encodes PeerRecords to bytes. 32 func (r PeerRecord) Encode() ([]byte, error) { 33 return json.Marshal(r) 34 } 35 36 // ID returns enode identity of the node. 37 func (r PeerRecord) ID() enode.ID { 38 return r.node.ID() 39 } 40 41 // Node returs pointer to original object. 42 // enode.Node doensn't allow modification on the object. 43 func (r PeerRecord) Node() *enode.Node { 44 return r.node 45 } 46 47 // EncodeKey returns bytes that will should be used as a key in persistent storage. 48 func (r PeerRecord) EncodeKey() ([]byte, error) { 49 return r.Node().MarshalText() 50 } 51 52 // NewCache returns pointer to a Cache instance. 53 func NewCache(db *leveldb.DB) *Cache { 54 return &Cache{db: db} 55 } 56 57 // Cache is wrapper for operations on disk with leveldb. 58 type Cache struct { 59 db *leveldb.DB 60 } 61 62 // Replace deletes old and adds new records in the persistent cache. 63 func (c *Cache) Replace(nodes []*enode.Node) error { 64 batch := new(leveldb.Batch) 65 iter := createPeersIterator(c.db) 66 defer iter.Release() 67 newNodes := nodesToMap(nodes) 68 for iter.Next() { 69 record, err := unmarshalKeyValue(keyWithoutPrefix(iter.Key()), iter.Value()) 70 if err != nil { 71 return err 72 } 73 if _, exist := newNodes[types.EnodeID(record.ID())]; exist { 74 delete(newNodes, types.EnodeID(record.ID())) 75 } else { 76 batch.Delete(iter.Key()) 77 } 78 } 79 for _, n := range newNodes { 80 enodeKey, err := n.MarshalText() 81 if err != nil { 82 return err 83 } 84 // we put nil as default value doesn't have any state associated with them. 85 batch.Put(db.Key(db.MailserversCache, enodeKey), nil) 86 } 87 return c.db.Write(batch, nil) 88 } 89 90 // LoadAll loads all records from persistent database. 91 func (c *Cache) LoadAll() (rst []PeerRecord, err error) { 92 iter := createPeersIterator(c.db) 93 for iter.Next() { 94 record, err := unmarshalKeyValue(keyWithoutPrefix(iter.Key()), iter.Value()) 95 if err != nil { 96 return nil, err 97 } 98 rst = append(rst, record) 99 } 100 return rst, nil 101 } 102 103 // UpdateRecord updates single record. 104 func (c *Cache) UpdateRecord(record PeerRecord) error { 105 enodeKey, err := record.EncodeKey() 106 if err != nil { 107 return err 108 } 109 value, err := record.Encode() 110 if err != nil { 111 return err 112 } 113 return c.db.Put(db.Key(db.MailserversCache, enodeKey), value, nil) 114 } 115 116 func unmarshalKeyValue(key, value []byte) (record PeerRecord, err error) { 117 enodeKey := key 118 node := new(enode.Node) 119 err = node.UnmarshalText(enodeKey) 120 if err != nil { 121 return record, err 122 } 123 record = PeerRecord{node: node} 124 if len(value) != 0 { 125 err = json.Unmarshal(value, &record) 126 } 127 return record, err 128 } 129 130 func nodesToMap(nodes []*enode.Node) map[types.EnodeID]*enode.Node { 131 rst := map[types.EnodeID]*enode.Node{} 132 for _, n := range nodes { 133 rst[types.EnodeID(n.ID())] = n 134 } 135 return rst 136 } 137 138 func createPeersIterator(level *leveldb.DB) iterator.Iterator { 139 return level.NewIterator(util.BytesPrefix([]byte{byte(db.MailserversCache)}), nil) 140 } 141 142 // keyWithoutPrefix removes first byte from key. 143 func keyWithoutPrefix(key []byte) []byte { 144 return key[1:] 145 }