github.com/ethereum-optimism/optimism@v1.7.2/op-node/p2p/store/mdbook.go (about)

     1  package store
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"sync/atomic"
     7  	"time"
     8  
     9  	"github.com/ethereum-optimism/optimism/op-service/clock"
    10  	"github.com/ethereum/go-ethereum/log"
    11  	ds "github.com/ipfs/go-datastore"
    12  	"github.com/libp2p/go-libp2p/core/peer"
    13  )
    14  
    15  const (
    16  	mdCacheSize        = 100
    17  	mdRecordExpiration = time.Hour * 24 * 7
    18  )
    19  
    20  var metadataBase = ds.NewKey("/peers/md")
    21  
    22  // LastUpdate requires atomic update operations. Use the helper functions SetLastUpdated and LastUpdated to modify and access this field.
    23  type metadataRecord struct {
    24  	LastUpdate   int64        `json:"lastUpdate"` // unix timestamp in seconds
    25  	PeerMetadata PeerMetadata `json:"peerMetadata"`
    26  }
    27  
    28  type PeerMetadata struct {
    29  	ENR       string `json:"enr"`
    30  	OPStackID uint64 `json:"opStackID"`
    31  }
    32  
    33  func (m *metadataRecord) SetLastUpdated(t time.Time) {
    34  	atomic.StoreInt64(&m.LastUpdate, t.Unix())
    35  }
    36  
    37  func (m *metadataRecord) LastUpdated() time.Time {
    38  	return time.Unix(atomic.LoadInt64(&m.LastUpdate), 0)
    39  }
    40  
    41  func (m *metadataRecord) MarshalBinary() (data []byte, err error) {
    42  	return json.Marshal(m)
    43  }
    44  
    45  func (m *metadataRecord) UnmarshalBinary(data []byte) error {
    46  	return json.Unmarshal(data, m)
    47  }
    48  
    49  type metadataBook struct {
    50  	book *recordsBook[peer.ID, *metadataRecord]
    51  }
    52  
    53  func newMetadataRecord() *metadataRecord {
    54  	return new(metadataRecord)
    55  }
    56  
    57  func newMetadataBook(ctx context.Context, logger log.Logger, clock clock.Clock, store ds.Batching) (*metadataBook, error) {
    58  	book, err := newRecordsBook[peer.ID, *metadataRecord](ctx, logger, clock, store, mdCacheSize, mdRecordExpiration, metadataBase, newMetadataRecord, peerIDKey)
    59  	if err != nil {
    60  		return nil, err
    61  	}
    62  	return &metadataBook{book: book}, nil
    63  }
    64  
    65  func (m *metadataBook) startGC() {
    66  	m.book.startGC()
    67  }
    68  
    69  func (m *metadataBook) GetPeerMetadata(id peer.ID) (PeerMetadata, error) {
    70  	record, err := m.book.getRecord(id)
    71  	// If the record is not found, return an empty PeerMetadata
    72  	if err == UnknownRecordErr {
    73  		return PeerMetadata{}, nil
    74  	}
    75  	if err != nil {
    76  		return PeerMetadata{}, err
    77  	}
    78  	return record.PeerMetadata, nil
    79  }
    80  
    81  // Apply simply overwrites the record with the new one.
    82  // presently, metadata is only collected during peering, so this is fine.
    83  // if in the future this data can be updated or expanded, this function will need to be updated.
    84  func (md *metadataRecord) Apply(rec *metadataRecord) {
    85  	*rec = *md
    86  }
    87  
    88  func (m *metadataBook) SetPeerMetadata(id peer.ID, md PeerMetadata) (PeerMetadata, error) {
    89  	rec := newMetadataRecord()
    90  	rec.PeerMetadata = md
    91  	rec.SetLastUpdated(m.book.clock.Now())
    92  	v, err := m.book.SetRecord(id, rec)
    93  	return v.PeerMetadata, err
    94  }
    95  
    96  func (m *metadataBook) Close() {
    97  	m.book.Close()
    98  }