mirror of
https://github.com/fluencelabs/js-libp2p
synced 2025-06-17 03:01:21 +00:00
feat: metadata book (#638)
* feat: metadata book * chore: address review * chore: address review
This commit is contained in:
145
doc/API.md
145
doc/API.md
@ -29,6 +29,11 @@
|
|||||||
* [`peerStore.keyBook.delete`](#peerstorekeybookdelete)
|
* [`peerStore.keyBook.delete`](#peerstorekeybookdelete)
|
||||||
* [`peerStore.keyBook.get`](#peerstorekeybookget)
|
* [`peerStore.keyBook.get`](#peerstorekeybookget)
|
||||||
* [`peerStore.keyBook.set`](#peerstorekeybookset)
|
* [`peerStore.keyBook.set`](#peerstorekeybookset)
|
||||||
|
* [`peerStore.metadataBook.delete`](#peerstoremetadatabookdelete)
|
||||||
|
* [`peerStore.metadataBook.deleteValue`](#peerstoremetadatabookdeletevalue)
|
||||||
|
* [`peerStore.metadataBook.get`](#peerstoremetadatabookget)
|
||||||
|
* [`peerStore.metadataBook.getValue`](#peerstoremetadatabookgetvalue)
|
||||||
|
* [`peerStore.metadataBook.set`](#peerstoremetadatabookset)
|
||||||
* [`peerStore.protoBook.add`](#peerstoreprotobookadd)
|
* [`peerStore.protoBook.add`](#peerstoreprotobookadd)
|
||||||
* [`peerStore.protoBook.delete`](#peerstoreprotobookdelete)
|
* [`peerStore.protoBook.delete`](#peerstoreprotobookdelete)
|
||||||
* [`peerStore.protoBook.get`](#peerstoreprotobookget)
|
* [`peerStore.protoBook.get`](#peerstoreprotobookget)
|
||||||
@ -939,6 +944,146 @@ const publicKey = peerId.pubKey
|
|||||||
peerStore.keyBook.set(peerId, publicKey)
|
peerStore.keyBook.set(peerId, publicKey)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### peerStore.metadataBook.delete
|
||||||
|
|
||||||
|
Delete the provided peer from the book.
|
||||||
|
|
||||||
|
`peerStore.metadataBook.delete(peerId)`
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| peerId | [`PeerId`][peer-id] | peerId to remove |
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
|
||||||
|
| Type | Description |
|
||||||
|
|------|-------------|
|
||||||
|
| `boolean` | true if found and removed |
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
|
||||||
|
```js
|
||||||
|
peerStore.metadataBook.delete(peerId)
|
||||||
|
// false
|
||||||
|
peerStore.metadataBook.set(peerId, 'nickname', Buffer.from('homePeer'))
|
||||||
|
peerStore.metadataBook.delete(peerId)
|
||||||
|
// true
|
||||||
|
```
|
||||||
|
|
||||||
|
### peerStore.metadataBook.deleteValue
|
||||||
|
|
||||||
|
Deletes the provided peer metadata key-value pair from the book.
|
||||||
|
|
||||||
|
`peerStore.metadataBook.deleteValue(peerId, key)`
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| peerId | [`PeerId`][peer-id] | peerId to remove |
|
||||||
|
| key | `string` | key of the metadata value to remove |
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
|
||||||
|
| Type | Description |
|
||||||
|
|------|-------------|
|
||||||
|
| `boolean` | true if found and removed |
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
|
||||||
|
```js
|
||||||
|
peerStore.metadataBook.deleteValue(peerId, 'location')
|
||||||
|
// false
|
||||||
|
peerStore.metadataBook.set(peerId, 'location', Buffer.from('Berlin'))
|
||||||
|
peerStore.metadataBook.deleteValue(peerId, 'location')
|
||||||
|
// true
|
||||||
|
```
|
||||||
|
|
||||||
|
### peerStore.metadataBook.get
|
||||||
|
|
||||||
|
Get the known metadata of a provided peer.
|
||||||
|
|
||||||
|
`peerStore.metadataBook.get(peerId)`
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| peerId | [`PeerId`][peer-id] | peerId to get |
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
|
||||||
|
| Type | Description |
|
||||||
|
|------|-------------|
|
||||||
|
| `Map<string, Buffer>` | Peer Metadata |
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
|
||||||
|
```js
|
||||||
|
peerStore.metadataBook.get(peerId)
|
||||||
|
// undefined
|
||||||
|
peerStore.metadataBook.set(peerId, 'location', Buffer.from('Berlin'))
|
||||||
|
peerStore.metadataBook.get(peerId)
|
||||||
|
// Metadata Map
|
||||||
|
```
|
||||||
|
|
||||||
|
### peerStore.metadataBook.getValue
|
||||||
|
|
||||||
|
Get specific metadata of a provided peer.
|
||||||
|
|
||||||
|
`peerStore.metadataBook.getValue(peerId)`
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| peerId | [`PeerId`][peer-id] | peerId to get |
|
||||||
|
| key | `string` | key of the metadata value to get |
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
|
||||||
|
| Type | Description |
|
||||||
|
|------|-------------|
|
||||||
|
| `Map<string, Buffer>` | Peer Metadata |
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
|
||||||
|
```js
|
||||||
|
peerStore.metadataBook.getValue(peerId, 'location')
|
||||||
|
// undefined
|
||||||
|
peerStore.metadataBook.set(peerId, 'location', Buffer.from('Berlin'))
|
||||||
|
peerStore.metadataBook.getValue(peerId, 'location')
|
||||||
|
// Metadata Map
|
||||||
|
```
|
||||||
|
|
||||||
|
### peerStore.metadataBook.set
|
||||||
|
|
||||||
|
Set known metadata of a given `peerId`.
|
||||||
|
|
||||||
|
`peerStore.metadataBook.set(peerId, key, value)`
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| peerId | [`PeerId`][peer-id] | peerId to set |
|
||||||
|
| key | `string` | key of the metadata value to store |
|
||||||
|
| value | `Buffer` | metadata value to store |
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
|
||||||
|
| Type | Description |
|
||||||
|
|------|-------------|
|
||||||
|
| `MetadataBook` | Returns the Metadata Book component |
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
|
||||||
|
```js
|
||||||
|
peerStore.metadataBook.set(peerId, 'location', Buffer.from('Berlin'))
|
||||||
|
```
|
||||||
|
|
||||||
### peerStore.protoBook.delete
|
### peerStore.protoBook.delete
|
||||||
|
|
||||||
Delete the provided peer from the book.
|
Delete the provided peer from the book.
|
||||||
|
@ -75,7 +75,11 @@ A `peerId.toB58String()` identifier mapping to a `Set` of protocol identifier st
|
|||||||
|
|
||||||
#### Metadata Book
|
#### Metadata Book
|
||||||
|
|
||||||
**Not Yet Implemented**
|
The `metadataBook` keeps track of the known metadata of a peer. Its metadata is stored in a key value fashion, where a key identifier (`string`) represents a metadata value (`Buffer`).
|
||||||
|
|
||||||
|
`Map<string, Map<string, Buffer>>`
|
||||||
|
|
||||||
|
A `peerId.toB58String()` identifier mapping to the peer metadata Map.
|
||||||
|
|
||||||
### API
|
### API
|
||||||
|
|
||||||
@ -85,6 +89,7 @@ Access to its underlying books:
|
|||||||
|
|
||||||
- `peerStore.addressBook.*`
|
- `peerStore.addressBook.*`
|
||||||
- `peerStore.keyBook.*`
|
- `peerStore.keyBook.*`
|
||||||
|
- `peerStore.metadataBook.*`
|
||||||
- `peerStore.protoBook.*`
|
- `peerStore.protoBook.*`
|
||||||
|
|
||||||
### Events
|
### Events
|
||||||
@ -92,6 +97,8 @@ Access to its underlying books:
|
|||||||
- `peer` - emitted when a new peer is added.
|
- `peer` - emitted when a new peer is added.
|
||||||
- `change:multiaadrs` - emitted when a known peer has a different set of multiaddrs.
|
- `change:multiaadrs` - emitted when a known peer has a different set of multiaddrs.
|
||||||
- `change:protocols` - emitted when a known peer supports a different set of protocols.
|
- `change:protocols` - emitted when a known peer supports a different set of protocols.
|
||||||
|
- `change:pubkey` - emitted when a peer's public key is known.
|
||||||
|
- `change:metadata` - emitted when known metadata of a peer changes.
|
||||||
|
|
||||||
## Data Persistence
|
## Data Persistence
|
||||||
|
|
||||||
@ -123,8 +130,6 @@ All public keys are stored under the following pattern:
|
|||||||
|
|
||||||
**MetadataBook**
|
**MetadataBook**
|
||||||
|
|
||||||
_NOT_YET_IMPLEMENTED_
|
|
||||||
|
|
||||||
Metadata is stored under the following key pattern:
|
Metadata is stored under the following key pattern:
|
||||||
|
|
||||||
`/peers/metadata/<b32 peer id no padding>/<key>`
|
`/peers/metadata/<b32 peer id no padding>/<key>`
|
||||||
|
@ -97,7 +97,6 @@ class AddressBook extends Book {
|
|||||||
/**
|
/**
|
||||||
* Add known addresses of a provided peer.
|
* Add known addresses of a provided peer.
|
||||||
* If the peer is not known, it is set with the given addresses.
|
* If the peer is not known, it is set with the given addresses.
|
||||||
* @override
|
|
||||||
* @param {PeerId} peerId
|
* @param {PeerId} peerId
|
||||||
* @param {Array<Multiaddr>} multiaddrs
|
* @param {Array<Multiaddr>} multiaddrs
|
||||||
* @returns {AddressBook}
|
* @returns {AddressBook}
|
||||||
|
@ -10,6 +10,7 @@ const PeerId = require('peer-id')
|
|||||||
|
|
||||||
const AddressBook = require('./address-book')
|
const AddressBook = require('./address-book')
|
||||||
const KeyBook = require('./key-book')
|
const KeyBook = require('./key-book')
|
||||||
|
const MetadataBook = require('./metadata-book')
|
||||||
const ProtoBook = require('./proto-book')
|
const ProtoBook = require('./proto-book')
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -21,6 +22,8 @@ const {
|
|||||||
* @fires PeerStore#peer Emitted when a new peer is added.
|
* @fires PeerStore#peer Emitted when a new peer is added.
|
||||||
* @fires PeerStore#change:protocols Emitted when a known peer supports a different set of protocols.
|
* @fires PeerStore#change:protocols Emitted when a known peer supports a different set of protocols.
|
||||||
* @fires PeerStore#change:multiaddrs Emitted when a known peer has a different set of multiaddrs.
|
* @fires PeerStore#change:multiaddrs Emitted when a known peer has a different set of multiaddrs.
|
||||||
|
* @fires PeerStore#change:pubkey Emitted emitted when a peer's public key is known.
|
||||||
|
* @fires PeerStore#change:metadata Emitted when the known metadata of a peer change.
|
||||||
*/
|
*/
|
||||||
class PeerStore extends EventEmitter {
|
class PeerStore extends EventEmitter {
|
||||||
/**
|
/**
|
||||||
@ -47,6 +50,11 @@ class PeerStore extends EventEmitter {
|
|||||||
*/
|
*/
|
||||||
this.keyBook = new KeyBook(this)
|
this.keyBook = new KeyBook(this)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MetadataBook containing a map of peerIdStr to their metadata Map.
|
||||||
|
*/
|
||||||
|
this.metadataBook = new MetadataBook(this)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ProtoBook containing a map of peerIdStr to supported protocols.
|
* ProtoBook containing a map of peerIdStr to supported protocols.
|
||||||
*/
|
*/
|
||||||
@ -68,31 +76,17 @@ class PeerStore extends EventEmitter {
|
|||||||
* @returns {Map<string, Peer>}
|
* @returns {Map<string, Peer>}
|
||||||
*/
|
*/
|
||||||
get peers () {
|
get peers () {
|
||||||
|
const storedPeers = new Set([
|
||||||
|
...this.addressBook.data.keys(),
|
||||||
|
...this.keyBook.data.keys(),
|
||||||
|
...this.protoBook.data.keys(),
|
||||||
|
...this.metadataBook.data.keys()
|
||||||
|
])
|
||||||
|
|
||||||
const peersData = new Map()
|
const peersData = new Map()
|
||||||
|
storedPeers.forEach((idStr) => {
|
||||||
// AddressBook
|
peersData.set(idStr, this.get(PeerId.createFromCID(idStr)))
|
||||||
for (const [idStr, addresses] of this.addressBook.data.entries()) {
|
|
||||||
const id = this.keyBook.data.get(idStr) || PeerId.createFromCID(idStr)
|
|
||||||
peersData.set(idStr, {
|
|
||||||
id,
|
|
||||||
addresses,
|
|
||||||
protocols: this.protoBook.get(id) || []
|
|
||||||
})
|
})
|
||||||
}
|
|
||||||
|
|
||||||
// ProtoBook
|
|
||||||
for (const [idStr, protocols] of this.protoBook.data.entries()) {
|
|
||||||
const pData = peersData.get(idStr)
|
|
||||||
const id = this.keyBook.data.get(idStr) || PeerId.createFromCID(idStr)
|
|
||||||
|
|
||||||
if (!pData) {
|
|
||||||
peersData.set(idStr, {
|
|
||||||
id,
|
|
||||||
addresses: [],
|
|
||||||
protocols: Array.from(protocols)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return peersData
|
return peersData
|
||||||
}
|
}
|
||||||
@ -106,8 +100,9 @@ class PeerStore extends EventEmitter {
|
|||||||
const addressesDeleted = this.addressBook.delete(peerId)
|
const addressesDeleted = this.addressBook.delete(peerId)
|
||||||
const keyDeleted = this.keyBook.delete(peerId)
|
const keyDeleted = this.keyBook.delete(peerId)
|
||||||
const protocolsDeleted = this.protoBook.delete(peerId)
|
const protocolsDeleted = this.protoBook.delete(peerId)
|
||||||
|
const metadataDeleted = this.metadataBook.delete(peerId)
|
||||||
|
|
||||||
return addressesDeleted || keyDeleted || protocolsDeleted
|
return addressesDeleted || keyDeleted || protocolsDeleted || metadataDeleted
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -122,16 +117,18 @@ class PeerStore extends EventEmitter {
|
|||||||
|
|
||||||
const id = this.keyBook.data.get(peerId.toB58String())
|
const id = this.keyBook.data.get(peerId.toB58String())
|
||||||
const addresses = this.addressBook.get(peerId)
|
const addresses = this.addressBook.get(peerId)
|
||||||
|
const metadata = this.metadataBook.get(peerId)
|
||||||
const protocols = this.protoBook.get(peerId)
|
const protocols = this.protoBook.get(peerId)
|
||||||
|
|
||||||
if (!addresses && !protocols) {
|
if (!id && !addresses && !metadata && !protocols) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: id || peerId,
|
id: id || peerId,
|
||||||
addresses: addresses || [],
|
addresses: addresses || [],
|
||||||
protocols: protocols || []
|
protocols: protocols || [],
|
||||||
|
metadata: metadata
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
161
src/peer-store/metadata-book.js
Normal file
161
src/peer-store/metadata-book.js
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
'use strict'
|
||||||
|
|
||||||
|
const errcode = require('err-code')
|
||||||
|
const debug = require('debug')
|
||||||
|
const log = debug('libp2p:peer-store:proto-book')
|
||||||
|
log.error = debug('libp2p:peer-store:proto-book:error')
|
||||||
|
|
||||||
|
const { Buffer } = require('buffer')
|
||||||
|
|
||||||
|
const PeerId = require('peer-id')
|
||||||
|
|
||||||
|
const Book = require('./book')
|
||||||
|
|
||||||
|
const {
|
||||||
|
codes: { ERR_INVALID_PARAMETERS }
|
||||||
|
} = require('../errors')
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The MetadataBook is responsible for keeping the known supported
|
||||||
|
* protocols of a peer.
|
||||||
|
* @fires MetadataBook#change:metadata
|
||||||
|
*/
|
||||||
|
class MetadataBook extends Book {
|
||||||
|
/**
|
||||||
|
* @constructor
|
||||||
|
* @param {PeerStore} peerStore
|
||||||
|
*/
|
||||||
|
constructor (peerStore) {
|
||||||
|
/**
|
||||||
|
* PeerStore Event emitter, used by the MetadataBook to emit:
|
||||||
|
* "change:metadata" - emitted when the known metadata of a peer change.
|
||||||
|
*/
|
||||||
|
super({
|
||||||
|
peerStore,
|
||||||
|
eventName: 'change:metadata',
|
||||||
|
eventProperty: 'metadata'
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map known peers to their known protocols.
|
||||||
|
* @type {Map<string, Map<string, Buffer>>}
|
||||||
|
*/
|
||||||
|
this.data = new Map()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set metadata key and value of a provided peer.
|
||||||
|
* @override
|
||||||
|
* @param {PeerId} peerId
|
||||||
|
* @param {string} key metadata key
|
||||||
|
* @param {Buffer} value metadata value
|
||||||
|
* @returns {ProtoBook}
|
||||||
|
*/
|
||||||
|
set (peerId, key, value) {
|
||||||
|
if (!PeerId.isPeerId(peerId)) {
|
||||||
|
log.error('peerId must be an instance of peer-id to store data')
|
||||||
|
throw errcode(new Error('peerId must be an instance of peer-id'), ERR_INVALID_PARAMETERS)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof key !== 'string' || !Buffer.isBuffer(value)) {
|
||||||
|
log.error('valid key and value must be provided to store data')
|
||||||
|
throw errcode(new Error('valid key and value must be provided'), ERR_INVALID_PARAMETERS)
|
||||||
|
}
|
||||||
|
|
||||||
|
this._setValue(peerId, key, value)
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set data into the datastructure
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
_setValue (peerId, key, value, { emit = true } = {}) {
|
||||||
|
const id = peerId.toB58String()
|
||||||
|
const rec = this.data.get(id) || new Map()
|
||||||
|
const recMap = rec.get(key)
|
||||||
|
|
||||||
|
// Already exists and is equal
|
||||||
|
if (recMap && value.equals(recMap)) {
|
||||||
|
log(`the metadata provided to store is equal to the already stored for ${id} on ${key}`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rec.set(key, value)
|
||||||
|
this.data.set(id, rec)
|
||||||
|
|
||||||
|
emit && this._emit(peerId, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the known data of a provided peer.
|
||||||
|
* @param {PeerId} peerId
|
||||||
|
* @returns {Map<string, Buffer>}
|
||||||
|
*/
|
||||||
|
get (peerId) {
|
||||||
|
if (!PeerId.isPeerId(peerId)) {
|
||||||
|
throw errcode(new Error('peerId must be an instance of peer-id'), ERR_INVALID_PARAMETERS)
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.data.get(peerId.toB58String())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get specific metadata value, if it exists
|
||||||
|
* @param {PeerId} peerId
|
||||||
|
* @param {string} key
|
||||||
|
* @returns {Buffer}
|
||||||
|
*/
|
||||||
|
getValue (peerId, key) {
|
||||||
|
if (!PeerId.isPeerId(peerId)) {
|
||||||
|
throw errcode(new Error('peerId must be an instance of peer-id'), ERR_INVALID_PARAMETERS)
|
||||||
|
}
|
||||||
|
|
||||||
|
const rec = this.data.get(peerId.toB58String())
|
||||||
|
return rec && rec.get(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the provided peer from the book.
|
||||||
|
* @param {PeerId} peerId
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
delete (peerId) {
|
||||||
|
if (!PeerId.isPeerId(peerId)) {
|
||||||
|
throw errcode(new Error('peerId must be an instance of peer-id'), ERR_INVALID_PARAMETERS)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.data.delete(peerId.toB58String())) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
this._emit(peerId)
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the provided peer metadata key from the book.
|
||||||
|
* @param {PeerId} peerId
|
||||||
|
* @param {string} key
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
deleteValue (peerId, key) {
|
||||||
|
if (!PeerId.isPeerId(peerId)) {
|
||||||
|
throw errcode(new Error('peerId must be an instance of peer-id'), ERR_INVALID_PARAMETERS)
|
||||||
|
}
|
||||||
|
|
||||||
|
const rec = this.data.get(peerId.toB58String())
|
||||||
|
|
||||||
|
if (!rec || !rec.delete(key)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
this._emit(peerId, key)
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = MetadataBook
|
@ -8,5 +8,8 @@ module.exports.NAMESPACE_ADDRESS = '/peers/addrs/'
|
|||||||
// /peers/keys/<b32 peer id no padding>
|
// /peers/keys/<b32 peer id no padding>
|
||||||
module.exports.NAMESPACE_KEYS = '/peers/keys/'
|
module.exports.NAMESPACE_KEYS = '/peers/keys/'
|
||||||
|
|
||||||
|
// /peers/metadata/<b32 peer id no padding>/<key>
|
||||||
|
module.exports.NAMESPACE_METADATA = '/peers/metadata/'
|
||||||
|
|
||||||
// /peers/addrs/<b32 peer id no padding>
|
// /peers/addrs/<b32 peer id no padding>
|
||||||
module.exports.NAMESPACE_PROTOCOL = '/peers/protos/'
|
module.exports.NAMESPACE_PROTOCOL = '/peers/protos/'
|
||||||
|
@ -14,6 +14,7 @@ const {
|
|||||||
NAMESPACE_ADDRESS,
|
NAMESPACE_ADDRESS,
|
||||||
NAMESPACE_COMMON,
|
NAMESPACE_COMMON,
|
||||||
NAMESPACE_KEYS,
|
NAMESPACE_KEYS,
|
||||||
|
NAMESPACE_METADATA,
|
||||||
NAMESPACE_PROTOCOL
|
NAMESPACE_PROTOCOL
|
||||||
} = require('./consts')
|
} = require('./consts')
|
||||||
|
|
||||||
@ -43,6 +44,12 @@ class PersistentPeerStore extends PeerStore {
|
|||||||
*/
|
*/
|
||||||
this._dirtyPeers = new Set()
|
this._dirtyPeers = new Set()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Peers metadata changed mapping peer identifers to metadata changed.
|
||||||
|
* @type {Map<string, Set<string>>}
|
||||||
|
*/
|
||||||
|
this._dirtyMetadata = new Map()
|
||||||
|
|
||||||
this.threshold = threshold
|
this.threshold = threshold
|
||||||
this._addDirtyPeer = this._addDirtyPeer.bind(this)
|
this._addDirtyPeer = this._addDirtyPeer.bind(this)
|
||||||
}
|
}
|
||||||
@ -58,6 +65,7 @@ class PersistentPeerStore extends PeerStore {
|
|||||||
this.on('change:protocols', this._addDirtyPeer)
|
this.on('change:protocols', this._addDirtyPeer)
|
||||||
this.on('change:multiaddrs', this._addDirtyPeer)
|
this.on('change:multiaddrs', this._addDirtyPeer)
|
||||||
this.on('change:pubkey', this._addDirtyPeer)
|
this.on('change:pubkey', this._addDirtyPeer)
|
||||||
|
this.on('change:metadata', this._addDirtyPeerMetadata)
|
||||||
|
|
||||||
// Load data
|
// Load data
|
||||||
for await (const entry of this._datastore.query({ prefix: NAMESPACE_COMMON })) {
|
for await (const entry of this._datastore.query({ prefix: NAMESPACE_COMMON })) {
|
||||||
@ -92,6 +100,30 @@ class PersistentPeerStore extends PeerStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add modified metadata peer to the set.
|
||||||
|
* @private
|
||||||
|
* @param {Object} params
|
||||||
|
* @param {PeerId} params.peerId
|
||||||
|
* @param {string} params.metadata
|
||||||
|
*/
|
||||||
|
_addDirtyPeerMetadata ({ peerId, metadata }) {
|
||||||
|
const peerIdstr = peerId.toB58String()
|
||||||
|
|
||||||
|
log('add dirty metadata peer', peerIdstr)
|
||||||
|
this._dirtyPeers.add(peerIdstr)
|
||||||
|
|
||||||
|
// Add dirty metadata key
|
||||||
|
const mData = this._dirtyMetadata.get(peerIdstr) || new Set()
|
||||||
|
mData.add(metadata)
|
||||||
|
this._dirtyMetadata.set(peerIdstr, mData)
|
||||||
|
|
||||||
|
if (this._dirtyPeers.size >= this.threshold) {
|
||||||
|
// Commit current data
|
||||||
|
this._commitData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add all the peers current data to a datastore batch and commit it.
|
* Add all the peers current data to a datastore batch and commit it.
|
||||||
* @private
|
* @private
|
||||||
@ -120,6 +152,9 @@ class PersistentPeerStore extends PeerStore {
|
|||||||
// Key Book
|
// Key Book
|
||||||
this._batchKeyBook(peerId, batch)
|
this._batchKeyBook(peerId, batch)
|
||||||
|
|
||||||
|
// Metadata Book
|
||||||
|
this._batchMetadataBook(peerId, batch)
|
||||||
|
|
||||||
// Proto Book
|
// Proto Book
|
||||||
this._batchProtoBook(peerId, batch)
|
this._batchProtoBook(peerId, batch)
|
||||||
}
|
}
|
||||||
@ -184,6 +219,32 @@ class PersistentPeerStore extends PeerStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add metadata book data of the peer to the batch.
|
||||||
|
* @private
|
||||||
|
* @param {PeerId} peerId
|
||||||
|
* @param {Object} batch
|
||||||
|
*/
|
||||||
|
_batchMetadataBook (peerId, batch) {
|
||||||
|
const b32key = peerId.toString()
|
||||||
|
const dirtyMetada = this._dirtyMetadata.get(peerId.toB58String()) || []
|
||||||
|
|
||||||
|
try {
|
||||||
|
dirtyMetada.forEach((dirtyKey) => {
|
||||||
|
const key = new Key(`${NAMESPACE_METADATA}${b32key}/${dirtyKey}`)
|
||||||
|
const dirtyValue = this.metadataBook.getValue(peerId, dirtyKey)
|
||||||
|
|
||||||
|
if (dirtyValue) {
|
||||||
|
batch.put(key, dirtyValue)
|
||||||
|
} else {
|
||||||
|
batch.delete(key)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
log.error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add proto book data of the peer to the batch.
|
* Add proto book data of the peer to the batch.
|
||||||
* @private
|
* @private
|
||||||
@ -244,6 +305,13 @@ class PersistentPeerStore extends PeerStore {
|
|||||||
decoded,
|
decoded,
|
||||||
{ emit: false })
|
{ emit: false })
|
||||||
break
|
break
|
||||||
|
case 'metadata':
|
||||||
|
this.metadataBook._setValue(
|
||||||
|
peerId,
|
||||||
|
keyParts[4],
|
||||||
|
value,
|
||||||
|
{ emit: false })
|
||||||
|
break
|
||||||
case 'protos':
|
case 'protos':
|
||||||
decoded = Protocols.decode(value)
|
decoded = Protocols.decode(value)
|
||||||
|
|
||||||
|
@ -83,7 +83,6 @@ class ProtoBook extends Book {
|
|||||||
/**
|
/**
|
||||||
* Adds known protocols of a provided peer.
|
* Adds known protocols of a provided peer.
|
||||||
* If the peer was not known before, it will be added.
|
* If the peer was not known before, it will be added.
|
||||||
* @override
|
|
||||||
* @param {PeerId} peerId
|
* @param {PeerId} peerId
|
||||||
* @param {Array<string>} protocols
|
* @param {Array<string>} protocols
|
||||||
* @returns {ProtoBook}
|
* @returns {ProtoBook}
|
||||||
|
380
test/peer-store/metadata-book.spec.js
Normal file
380
test/peer-store/metadata-book.spec.js
Normal file
@ -0,0 +1,380 @@
|
|||||||
|
'use strict'
|
||||||
|
/* eslint-env mocha */
|
||||||
|
|
||||||
|
const chai = require('chai')
|
||||||
|
chai.use(require('dirty-chai'))
|
||||||
|
chai.use(require('chai-bytes'))
|
||||||
|
const { expect } = chai
|
||||||
|
|
||||||
|
const pDefer = require('p-defer')
|
||||||
|
const PeerStore = require('../../src/peer-store')
|
||||||
|
|
||||||
|
const peerUtils = require('../utils/creators/peer')
|
||||||
|
const {
|
||||||
|
codes: { ERR_INVALID_PARAMETERS }
|
||||||
|
} = require('../../src/errors')
|
||||||
|
|
||||||
|
describe('metadataBook', () => {
|
||||||
|
let peerId
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
[peerId] = await peerUtils.createPeerId()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('metadataBook.set', () => {
|
||||||
|
let peerStore, mb
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
peerStore = new PeerStore()
|
||||||
|
mb = peerStore.metadataBook
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
peerStore.removeAllListeners()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('throws invalid parameters error if invalid PeerId is provided', () => {
|
||||||
|
try {
|
||||||
|
mb.set('invalid peerId')
|
||||||
|
} catch (err) {
|
||||||
|
expect(err.code).to.equal(ERR_INVALID_PARAMETERS)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
throw new Error('invalid peerId should throw error')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('throws invalid parameters error if no key provided', () => {
|
||||||
|
try {
|
||||||
|
mb.set(peerId)
|
||||||
|
} catch (err) {
|
||||||
|
expect(err.code).to.equal(ERR_INVALID_PARAMETERS)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
throw new Error('no key provided should throw error')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('throws invalid parameters error if no value provided', () => {
|
||||||
|
try {
|
||||||
|
mb.set(peerId, 'location')
|
||||||
|
} catch (err) {
|
||||||
|
expect(err.code).to.equal(ERR_INVALID_PARAMETERS)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
throw new Error('no value provided should throw error')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('throws invalid parameters error if value is not a buffer', () => {
|
||||||
|
try {
|
||||||
|
mb.set(peerId, 'location', 'mars')
|
||||||
|
} catch (err) {
|
||||||
|
expect(err.code).to.equal(ERR_INVALID_PARAMETERS)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
throw new Error('invalid value provided should throw error')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('stores the content and emit change event', () => {
|
||||||
|
const defer = pDefer()
|
||||||
|
const metadataKey = 'location'
|
||||||
|
const metadataValue = Buffer.from('mars')
|
||||||
|
|
||||||
|
peerStore.once('change:metadata', ({ peerId, metadata }) => {
|
||||||
|
expect(peerId).to.exist()
|
||||||
|
expect(metadata).to.equal(metadataKey)
|
||||||
|
defer.resolve()
|
||||||
|
})
|
||||||
|
|
||||||
|
mb.set(peerId, metadataKey, metadataValue)
|
||||||
|
|
||||||
|
const value = mb.getValue(peerId, metadataKey)
|
||||||
|
expect(value).to.equalBytes(metadataValue)
|
||||||
|
|
||||||
|
const peerMetadata = mb.get(peerId)
|
||||||
|
expect(peerMetadata).to.exist()
|
||||||
|
expect(peerMetadata.get(metadataKey)).to.equalBytes(metadataValue)
|
||||||
|
|
||||||
|
return defer.promise
|
||||||
|
})
|
||||||
|
|
||||||
|
it('emits on set if not storing the exact same content', () => {
|
||||||
|
const defer = pDefer()
|
||||||
|
const metadataKey = 'location'
|
||||||
|
const metadataValue1 = Buffer.from('mars')
|
||||||
|
const metadataValue2 = Buffer.from('saturn')
|
||||||
|
|
||||||
|
let changeCounter = 0
|
||||||
|
peerStore.on('change:metadata', () => {
|
||||||
|
changeCounter++
|
||||||
|
if (changeCounter > 1) {
|
||||||
|
defer.resolve()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// set 1
|
||||||
|
mb.set(peerId, metadataKey, metadataValue1)
|
||||||
|
|
||||||
|
// set 2 (same content)
|
||||||
|
mb.set(peerId, metadataKey, metadataValue2)
|
||||||
|
|
||||||
|
const value = mb.getValue(peerId, metadataKey)
|
||||||
|
expect(value).to.equalBytes(metadataValue2)
|
||||||
|
|
||||||
|
const peerMetadata = mb.get(peerId)
|
||||||
|
expect(peerMetadata).to.exist()
|
||||||
|
expect(peerMetadata.get(metadataKey)).to.equalBytes(metadataValue2)
|
||||||
|
|
||||||
|
return defer.promise
|
||||||
|
})
|
||||||
|
|
||||||
|
it('does not emit on set if it is storing the exact same content', () => {
|
||||||
|
const defer = pDefer()
|
||||||
|
const metadataKey = 'location'
|
||||||
|
const metadataValue = Buffer.from('mars')
|
||||||
|
|
||||||
|
let changeCounter = 0
|
||||||
|
peerStore.on('change:metadata', () => {
|
||||||
|
changeCounter++
|
||||||
|
if (changeCounter > 1) {
|
||||||
|
defer.reject()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// set 1
|
||||||
|
mb.set(peerId, metadataKey, metadataValue)
|
||||||
|
|
||||||
|
// set 2 (same content)
|
||||||
|
mb.set(peerId, metadataKey, metadataValue)
|
||||||
|
|
||||||
|
// Wait 50ms for incorrect second event
|
||||||
|
setTimeout(() => {
|
||||||
|
defer.resolve()
|
||||||
|
}, 50)
|
||||||
|
|
||||||
|
return defer.promise
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('metadataBook.get', () => {
|
||||||
|
let peerStore, mb
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
peerStore = new PeerStore()
|
||||||
|
mb = peerStore.metadataBook
|
||||||
|
})
|
||||||
|
|
||||||
|
it('throws invalid parameters error if invalid PeerId is provided', () => {
|
||||||
|
try {
|
||||||
|
mb.get('invalid peerId')
|
||||||
|
} catch (err) {
|
||||||
|
expect(err.code).to.equal(ERR_INVALID_PARAMETERS)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
throw new Error('invalid peerId should throw error')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns undefined if no metadata is known for the provided peer', () => {
|
||||||
|
const metadata = mb.get(peerId)
|
||||||
|
|
||||||
|
expect(metadata).to.not.exist()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns the metadata stored', () => {
|
||||||
|
const metadataKey = 'location'
|
||||||
|
const metadataValue = Buffer.from('mars')
|
||||||
|
|
||||||
|
mb.set(peerId, metadataKey, metadataValue)
|
||||||
|
|
||||||
|
const peerMetadata = mb.get(peerId)
|
||||||
|
expect(peerMetadata).to.exist()
|
||||||
|
expect(peerMetadata.get(metadataKey)).to.equalBytes(metadataValue)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('metadataBook.getValue', () => {
|
||||||
|
let peerStore, mb
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
peerStore = new PeerStore()
|
||||||
|
mb = peerStore.metadataBook
|
||||||
|
})
|
||||||
|
|
||||||
|
it('throws invalid parameters error if invalid PeerId is provided', () => {
|
||||||
|
try {
|
||||||
|
mb.getValue('invalid peerId')
|
||||||
|
} catch (err) {
|
||||||
|
expect(err.code).to.equal(ERR_INVALID_PARAMETERS)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
throw new Error('invalid peerId should throw error')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns undefined if no metadata is known for the provided peer', () => {
|
||||||
|
const metadataKey = 'location'
|
||||||
|
const metadata = mb.getValue(peerId, metadataKey)
|
||||||
|
|
||||||
|
expect(metadata).to.not.exist()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns the metadata value stored for the given key', () => {
|
||||||
|
const metadataKey = 'location'
|
||||||
|
const metadataValue = Buffer.from('mars')
|
||||||
|
|
||||||
|
mb.set(peerId, metadataKey, metadataValue)
|
||||||
|
|
||||||
|
const value = mb.getValue(peerId, metadataKey)
|
||||||
|
expect(value).to.exist()
|
||||||
|
expect(value).to.equalBytes(metadataValue)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns undefined if no metadata is known for the provided peer and key', () => {
|
||||||
|
const metadataKey = 'location'
|
||||||
|
const metadataBadKey = 'nickname'
|
||||||
|
const metadataValue = Buffer.from('mars')
|
||||||
|
|
||||||
|
mb.set(peerId, metadataKey, metadataValue)
|
||||||
|
|
||||||
|
const metadata = mb.getValue(peerId, metadataBadKey)
|
||||||
|
|
||||||
|
expect(metadata).to.not.exist()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('metadataBook.delete', () => {
|
||||||
|
let peerStore, mb
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
peerStore = new PeerStore()
|
||||||
|
mb = peerStore.metadataBook
|
||||||
|
})
|
||||||
|
|
||||||
|
it('throwns invalid parameters error if invalid PeerId is provided', () => {
|
||||||
|
try {
|
||||||
|
mb.delete('invalid peerId')
|
||||||
|
} catch (err) {
|
||||||
|
expect(err.code).to.equal(ERR_INVALID_PARAMETERS)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
throw new Error('invalid peerId should throw error')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns false if no records exist for the peer and no event is emitted', () => {
|
||||||
|
const defer = pDefer()
|
||||||
|
|
||||||
|
peerStore.on('change:metadata', () => {
|
||||||
|
defer.reject()
|
||||||
|
})
|
||||||
|
|
||||||
|
const deleted = mb.delete(peerId)
|
||||||
|
|
||||||
|
expect(deleted).to.equal(false)
|
||||||
|
|
||||||
|
// Wait 50ms for incorrect invalid event
|
||||||
|
setTimeout(() => {
|
||||||
|
defer.resolve()
|
||||||
|
}, 50)
|
||||||
|
|
||||||
|
return defer.promise
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns true if the record exists and an event is emitted', () => {
|
||||||
|
const defer = pDefer()
|
||||||
|
const metadataKey = 'location'
|
||||||
|
const metadataValue = Buffer.from('mars')
|
||||||
|
|
||||||
|
mb.set(peerId, metadataKey, metadataValue)
|
||||||
|
|
||||||
|
// Listen after set
|
||||||
|
peerStore.on('change:metadata', () => {
|
||||||
|
defer.resolve()
|
||||||
|
})
|
||||||
|
|
||||||
|
const deleted = mb.delete(peerId)
|
||||||
|
|
||||||
|
expect(deleted).to.equal(true)
|
||||||
|
|
||||||
|
return defer.promise
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('metadataBook.deleteValue', () => {
|
||||||
|
let peerStore, mb
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
peerStore = new PeerStore()
|
||||||
|
mb = peerStore.metadataBook
|
||||||
|
})
|
||||||
|
|
||||||
|
it('throws invalid parameters error if invalid PeerId is provided', () => {
|
||||||
|
try {
|
||||||
|
mb.deleteValue('invalid peerId')
|
||||||
|
} catch (err) {
|
||||||
|
expect(err.code).to.equal(ERR_INVALID_PARAMETERS)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
throw new Error('invalid peerId should throw error')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns false if no records exist for the peer and no event is emitted', () => {
|
||||||
|
const defer = pDefer()
|
||||||
|
const metadataKey = 'location'
|
||||||
|
|
||||||
|
peerStore.on('change:metadata', () => {
|
||||||
|
defer.reject()
|
||||||
|
})
|
||||||
|
|
||||||
|
const deleted = mb.deleteValue(peerId, metadataKey)
|
||||||
|
|
||||||
|
expect(deleted).to.equal(false)
|
||||||
|
|
||||||
|
// Wait 50ms for incorrect invalid event
|
||||||
|
setTimeout(() => {
|
||||||
|
defer.resolve()
|
||||||
|
}, 50)
|
||||||
|
|
||||||
|
return defer.promise
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns true if the record exists and an event is emitted', () => {
|
||||||
|
const defer = pDefer()
|
||||||
|
const metadataKey = 'location'
|
||||||
|
const metadataValue = Buffer.from('mars')
|
||||||
|
|
||||||
|
mb.set(peerId, metadataKey, metadataValue)
|
||||||
|
|
||||||
|
// Listen after set
|
||||||
|
peerStore.on('change:metadata', () => {
|
||||||
|
defer.resolve()
|
||||||
|
})
|
||||||
|
|
||||||
|
const deleted = mb.deleteValue(peerId, metadataKey)
|
||||||
|
|
||||||
|
expect(deleted).to.equal(true)
|
||||||
|
|
||||||
|
return defer.promise
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns false if there is a record for the peer but not the given metadata key', () => {
|
||||||
|
const defer = pDefer()
|
||||||
|
const metadataKey = 'location'
|
||||||
|
const metadataBadKey = 'nickname'
|
||||||
|
const metadataValue = Buffer.from('mars')
|
||||||
|
|
||||||
|
mb.set(peerId, metadataKey, metadataValue)
|
||||||
|
|
||||||
|
peerStore.on('change:metadata', () => {
|
||||||
|
defer.reject()
|
||||||
|
})
|
||||||
|
|
||||||
|
const deleted = mb.deleteValue(peerId, metadataBadKey)
|
||||||
|
|
||||||
|
expect(deleted).to.equal(false)
|
||||||
|
|
||||||
|
// Wait 50ms for incorrect invalid event
|
||||||
|
setTimeout(() => {
|
||||||
|
defer.resolve()
|
||||||
|
}, 50)
|
||||||
|
|
||||||
|
return defer.promise
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
@ -158,4 +158,57 @@ describe('peer-store', () => {
|
|||||||
expect(peerListenint4[1].id.toB58String()).to.eql(peerIds[3].toB58String())
|
expect(peerListenint4[1].id.toB58String()).to.eql(peerIds[3].toB58String())
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('peerStore.peers', () => {
|
||||||
|
let peerStore
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
peerStore = new PeerStore()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns peers if only addresses are known', () => {
|
||||||
|
peerStore.addressBook.set(peerIds[0], [addr1])
|
||||||
|
|
||||||
|
const peers = peerStore.peers
|
||||||
|
expect(peers.size).to.equal(1)
|
||||||
|
|
||||||
|
const peerData = peers.get(peerIds[0].toB58String())
|
||||||
|
expect(peerData).to.exist()
|
||||||
|
expect(peerData.id).to.exist()
|
||||||
|
expect(peerData.addresses).to.have.lengthOf(1)
|
||||||
|
expect(peerData.protocols).to.have.lengthOf(0)
|
||||||
|
expect(peerData.metadata).to.not.exist()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns peers if only protocols are known', () => {
|
||||||
|
peerStore.protoBook.set(peerIds[0], [proto1])
|
||||||
|
|
||||||
|
const peers = peerStore.peers
|
||||||
|
expect(peers.size).to.equal(1)
|
||||||
|
|
||||||
|
const peerData = peers.get(peerIds[0].toB58String())
|
||||||
|
expect(peerData).to.exist()
|
||||||
|
expect(peerData.id).to.exist()
|
||||||
|
expect(peerData.addresses).to.have.lengthOf(0)
|
||||||
|
expect(peerData.protocols).to.have.lengthOf(1)
|
||||||
|
expect(peerData.metadata).to.not.exist()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns peers if only metadata is known', () => {
|
||||||
|
const metadataKey = 'location'
|
||||||
|
const metadataValue = Buffer.from('earth')
|
||||||
|
peerStore.metadataBook.set(peerIds[0], metadataKey, metadataValue)
|
||||||
|
|
||||||
|
const peers = peerStore.peers
|
||||||
|
expect(peers.size).to.equal(1)
|
||||||
|
|
||||||
|
const peerData = peers.get(peerIds[0].toB58String())
|
||||||
|
expect(peerData).to.exist()
|
||||||
|
expect(peerData.id).to.exist()
|
||||||
|
expect(peerData.addresses).to.have.lengthOf(0)
|
||||||
|
expect(peerData.protocols).to.have.lengthOf(0)
|
||||||
|
expect(peerData.metadata).to.exist()
|
||||||
|
expect(peerData.metadata.get(metadataKey)).to.equalBytes(metadataValue)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
@ -118,7 +118,10 @@ describe('Persisted PeerStore', () => {
|
|||||||
peerStore.protoBook.set(peers[0], protocols)
|
peerStore.protoBook.set(peers[0], protocols)
|
||||||
peerStore.protoBook.set(peers[1], protocols)
|
peerStore.protoBook.set(peers[1], protocols)
|
||||||
|
|
||||||
expect(spyDs).to.have.property('callCount', 6) // 2 AddressBook + 2 KeyBook + 2 ProtoBook
|
// MetadataBook
|
||||||
|
peerStore.metadataBook.set(peers[0], 'location', Buffer.from('earth'))
|
||||||
|
|
||||||
|
expect(spyDs).to.have.property('callCount', 7) // 2 Address + 2 Key + 2 Proto + 1 Metadata
|
||||||
expect(peerStore.peers.size).to.equal(2)
|
expect(peerStore.peers.size).to.equal(2)
|
||||||
|
|
||||||
await peerStore.stop()
|
await peerStore.stop()
|
||||||
@ -131,13 +134,14 @@ describe('Persisted PeerStore', () => {
|
|||||||
|
|
||||||
await peerStore.start()
|
await peerStore.start()
|
||||||
|
|
||||||
expect(spy).to.have.property('callCount', 6)
|
expect(spy).to.have.property('callCount', 7)
|
||||||
expect(spyDs).to.have.property('callCount', 6)
|
expect(spyDs).to.have.property('callCount', 7)
|
||||||
|
|
||||||
expect(peerStore.peers.size).to.equal(2)
|
expect(peerStore.peers.size).to.equal(2)
|
||||||
expect(peerStore.addressBook.data.size).to.equal(2)
|
expect(peerStore.addressBook.data.size).to.equal(2)
|
||||||
expect(peerStore.keyBook.data.size).to.equal(2)
|
expect(peerStore.keyBook.data.size).to.equal(2)
|
||||||
expect(peerStore.protoBook.data.size).to.equal(2)
|
expect(peerStore.protoBook.data.size).to.equal(2)
|
||||||
|
expect(peerStore.metadataBook.data.size).to.equal(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should delete content from the datastore on delete', async () => {
|
it('should delete content from the datastore on delete', async () => {
|
||||||
@ -151,11 +155,14 @@ describe('Persisted PeerStore', () => {
|
|||||||
peerStore.addressBook.set(peer, multiaddrs)
|
peerStore.addressBook.set(peer, multiaddrs)
|
||||||
// ProtoBook
|
// ProtoBook
|
||||||
peerStore.protoBook.set(peer, protocols)
|
peerStore.protoBook.set(peer, protocols)
|
||||||
|
// MetadataBook
|
||||||
|
peerStore.metadataBook.set(peer, 'location', Buffer.from('earth'))
|
||||||
|
|
||||||
const spyDs = sinon.spy(datastore, 'batch')
|
const spyDs = sinon.spy(datastore, 'batch')
|
||||||
const spyAddressBook = sinon.spy(peerStore.addressBook, 'delete')
|
const spyAddressBook = sinon.spy(peerStore.addressBook, 'delete')
|
||||||
const spyKeyBook = sinon.spy(peerStore.keyBook, 'delete')
|
const spyKeyBook = sinon.spy(peerStore.keyBook, 'delete')
|
||||||
const spyProtoBook = sinon.spy(peerStore.protoBook, 'delete')
|
const spyProtoBook = sinon.spy(peerStore.protoBook, 'delete')
|
||||||
|
const spyMetadataBook = sinon.spy(peerStore.metadataBook, 'delete')
|
||||||
|
|
||||||
// Delete from PeerStore
|
// Delete from PeerStore
|
||||||
peerStore.delete(peer)
|
peerStore.delete(peer)
|
||||||
@ -164,7 +171,8 @@ describe('Persisted PeerStore', () => {
|
|||||||
expect(spyAddressBook).to.have.property('callCount', 1)
|
expect(spyAddressBook).to.have.property('callCount', 1)
|
||||||
expect(spyKeyBook).to.have.property('callCount', 1)
|
expect(spyKeyBook).to.have.property('callCount', 1)
|
||||||
expect(spyProtoBook).to.have.property('callCount', 1)
|
expect(spyProtoBook).to.have.property('callCount', 1)
|
||||||
expect(spyDs).to.have.property('callCount', 2)
|
expect(spyMetadataBook).to.have.property('callCount', 1)
|
||||||
|
expect(spyDs).to.have.property('callCount', 3)
|
||||||
|
|
||||||
// Should have zero peer records stored in the datastore
|
// Should have zero peer records stored in the datastore
|
||||||
const queryParams = {
|
const queryParams = {
|
||||||
@ -187,6 +195,7 @@ describe('Persisted PeerStore', () => {
|
|||||||
|
|
||||||
it('should not commit until threshold is reached', async () => {
|
it('should not commit until threshold is reached', async () => {
|
||||||
const spyDirty = sinon.spy(peerStore, '_addDirtyPeer')
|
const spyDirty = sinon.spy(peerStore, '_addDirtyPeer')
|
||||||
|
const spyDirtyMetadata = sinon.spy(peerStore, '_addDirtyPeerMetadata')
|
||||||
const spyDs = sinon.spy(datastore, 'batch')
|
const spyDs = sinon.spy(datastore, 'batch')
|
||||||
|
|
||||||
const peers = await peerUtils.createPeerId({ number: 2 })
|
const peers = await peerUtils.createPeerId({ number: 2 })
|
||||||
@ -202,11 +211,13 @@ describe('Persisted PeerStore', () => {
|
|||||||
// Add Peer0 data in multiple books
|
// Add Peer0 data in multiple books
|
||||||
peerStore.addressBook.set(peers[0], multiaddrs)
|
peerStore.addressBook.set(peers[0], multiaddrs)
|
||||||
peerStore.protoBook.set(peers[0], protocols)
|
peerStore.protoBook.set(peers[0], protocols)
|
||||||
|
peerStore.metadataBook.set(peers[0], 'location', Buffer.from('earth'))
|
||||||
|
|
||||||
// Remove data from the same Peer
|
// Remove data from the same Peer
|
||||||
peerStore.addressBook.delete(peers[0])
|
peerStore.addressBook.delete(peers[0])
|
||||||
|
|
||||||
expect(spyDirty).to.have.property('callCount', 3) // 2 AddrBook ops, 1 ProtoBook op
|
expect(spyDirty).to.have.property('callCount', 3) // 2 AddrBook ops, 1 ProtoBook op
|
||||||
|
expect(spyDirtyMetadata).to.have.property('callCount', 1) // 1 MetadataBook op
|
||||||
expect(peerStore._dirtyPeers.size).to.equal(1)
|
expect(peerStore._dirtyPeers.size).to.equal(1)
|
||||||
expect(spyDs).to.have.property('callCount', 0)
|
expect(spyDs).to.have.property('callCount', 0)
|
||||||
|
|
||||||
@ -221,14 +232,15 @@ describe('Persisted PeerStore', () => {
|
|||||||
peerStore.addressBook.set(peers[1], multiaddrs)
|
peerStore.addressBook.set(peers[1], multiaddrs)
|
||||||
|
|
||||||
expect(spyDirty).to.have.property('callCount', 4)
|
expect(spyDirty).to.have.property('callCount', 4)
|
||||||
|
expect(spyDirtyMetadata).to.have.property('callCount', 1)
|
||||||
expect(spyDs).to.have.property('callCount', 1)
|
expect(spyDs).to.have.property('callCount', 1)
|
||||||
|
|
||||||
// Should have two peer records stored in the datastore
|
// Should have three peer records stored in the datastore
|
||||||
let count = 0
|
let count = 0
|
||||||
for await (const _ of datastore.query(queryParams)) { // eslint-disable-line
|
for await (const _ of datastore.query(queryParams)) { // eslint-disable-line
|
||||||
count++
|
count++
|
||||||
}
|
}
|
||||||
expect(count).to.equal(2)
|
expect(count).to.equal(3)
|
||||||
expect(peerStore.peers.size).to.equal(2)
|
expect(peerStore.peers.size).to.equal(2)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -241,7 +253,7 @@ describe('Persisted PeerStore', () => {
|
|||||||
|
|
||||||
await peerStore.start()
|
await peerStore.start()
|
||||||
|
|
||||||
// Add Peer data in a booka
|
// Add Peer data in a book
|
||||||
peerStore.protoBook.set(peer, protocols)
|
peerStore.protoBook.set(peer, protocols)
|
||||||
|
|
||||||
expect(spyDs).to.have.property('callCount', 0)
|
expect(spyDs).to.have.property('callCount', 0)
|
||||||
|
Reference in New Issue
Block a user