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  }