Compare commits

..

26 Commits

Author SHA1 Message Date
de64a49007 chore: release version v0.2.7 2020-03-20 20:57:31 +01:00
3cfd9714bd chore: update contributors 2020-03-20 20:57:30 +01:00
78e015cef3 fix: add buffer (#39) 2020-03-20 20:53:54 +01:00
bd5a8b9337 chore(deps): bump sinon from 8.1.1 to 9.0.0
Bumps [sinon](https://github.com/sinonjs/sinon) from 8.1.1 to 9.0.0.
- [Release notes](https://github.com/sinonjs/sinon/releases)
- [Changelog](https://github.com/sinonjs/sinon/blob/master/CHANGELOG.md)
- [Commits](https://github.com/sinonjs/sinon/compare/v8.1.1...v9.0.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-19 08:20:14 -05:00
098390cd31 chore: release version v0.2.6 2020-02-17 16:21:31 -05:00
953e289e1a chore: update contributors 2020-02-17 16:21:30 -05:00
ba822856ef chore(deps-dev): bump aegir from 20.6.1 to 21.0.2
Bumps [aegir](https://github.com/ipfs/aegir) from 20.6.1 to 21.0.2.
- [Release notes](https://github.com/ipfs/aegir/releases)
- [Changelog](https://github.com/ipfs/aegir/blob/master/CHANGELOG.md)
- [Commits](https://github.com/ipfs/aegir/compare/v20.6.1...v21.0.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-17 22:19:35 +01:00
c77d8de2e7 fix: remove use of assert module (#34)
* fix: remove use of assert module

The polyfill is big, we can simulate it by throwing an Error and it doesn't work under React Native.

* chore: pr comments
2020-02-17 22:19:13 +01:00
6203109751 chore: release version v0.2.5 2020-02-04 18:38:20 +01:00
25eeedd20e chore: update contributors 2020-02-04 18:38:19 +01:00
5c88d77aaa fix(connection): tracks streams properly (#25) 2020-02-04 17:34:56 +01:00
f6871afd76 chore(deps): bump sinon from 7.5.0 to 8.1.1
Bumps [sinon](https://github.com/sinonjs/sinon) from 7.5.0 to 8.1.1.
- [Release notes](https://github.com/sinonjs/sinon/releases)
- [Changelog](https://github.com/sinonjs/sinon/blob/master/CHANGELOG.md)
- [Commits](https://github.com/sinonjs/sinon/compare/v7.5.0...v8.1.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-04 15:55:39 +01:00
734c491bb0 chore(deps): bump abortable-iterator from 2.1.0 to 3.0.0 (#24)
Bumps [abortable-iterator](https://github.com/alanshaw/abortable-iterator) from 2.1.0 to 3.0.0.
- [Release notes](https://github.com/alanshaw/abortable-iterator/releases)
- [Commits](https://github.com/alanshaw/abortable-iterator/compare/v2.1.0...v3.0.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-04 15:55:13 +01:00
c43a52d176 chore: release version v0.2.4 2020-02-04 12:29:36 +01:00
af1ba5a409 chore: update contributors 2020-02-04 12:29:35 +01:00
c5b724ab93 fix: dependencies for tests should not be needed by who requires the tests (#18)
* fix: dependencies for tests should not be needed by who requires the tests

* chore: address review
2020-02-04 10:53:05 +01:00
6fb45b775b chore: remove unused dependency (#22) 2020-02-04 10:51:38 +01:00
85c68b5df4 chore: release version v0.2.3 2020-01-21 16:10:16 +01:00
27ac0a5670 chore: update contributors 2020-01-21 16:10:16 +01:00
2de533e15b fix(transport): make close listener test more resilient (#21) 2020-01-21 16:06:13 +01:00
aacb0e27d0 chore: release version v0.2.2 2020-01-17 15:41:02 +01:00
c00f94dfaf chore: update contributors 2020-01-17 15:41:02 +01:00
5967834a2f fix(connection): dont require remoteAddr on creation (#20) 2020-01-17 15:38:14 +01:00
32ee3e18e2 chore: release version v0.2.1 2019-12-27 21:46:43 -07:00
557f6cab66 chore: update contributors 2019-12-27 21:46:43 -07:00
d98bb23fea feat: add crypto transmission error (#17)
* feat: add crypto transmission error

* docs: add error to crypto readme
2019-12-27 19:51:10 -08:00
14 changed files with 234 additions and 39 deletions

1
.gitignore vendored
View File

@ -13,3 +13,4 @@ docs
node_modules node_modules
# Lock files # Lock files
package-lock.json package-lock.json
yarn.lock

View File

@ -1,3 +1,73 @@
<a name="0.2.7"></a>
## [0.2.7](https://github.com/libp2p/js-interfaces/compare/v0.2.6...v0.2.7) (2020-03-20)
### Bug Fixes
* add buffer ([#39](https://github.com/libp2p/js-interfaces/issues/39)) ([78e015c](https://github.com/libp2p/js-interfaces/commit/78e015c))
<a name="0.2.6"></a>
## [0.2.6](https://github.com/libp2p/js-interfaces/compare/v0.2.5...v0.2.6) (2020-02-17)
### Bug Fixes
* remove use of assert module ([#34](https://github.com/libp2p/js-interfaces/issues/34)) ([c77d8de](https://github.com/libp2p/js-interfaces/commit/c77d8de))
<a name="0.2.5"></a>
## [0.2.5](https://github.com/libp2p/js-interfaces/compare/v0.2.4...v0.2.5) (2020-02-04)
### Bug Fixes
* **connection:** tracks streams properly ([#25](https://github.com/libp2p/js-interfaces/issues/25)) ([5c88d77](https://github.com/libp2p/js-interfaces/commit/5c88d77))
<a name="0.2.4"></a>
## [0.2.4](https://github.com/libp2p/js-interfaces/compare/v0.2.3...v0.2.4) (2020-02-04)
### Bug Fixes
* dependencies for tests should not be needed by who requires the tests ([#18](https://github.com/libp2p/js-interfaces/issues/18)) ([c5b724a](https://github.com/libp2p/js-interfaces/commit/c5b724a))
<a name="0.2.3"></a>
## [0.2.3](https://github.com/libp2p/js-interfaces/compare/v0.2.2...v0.2.3) (2020-01-21)
### Bug Fixes
* **transport:** make close listener test more resilient ([#21](https://github.com/libp2p/js-interfaces/issues/21)) ([2de533e](https://github.com/libp2p/js-interfaces/commit/2de533e))
<a name="0.2.2"></a>
## [0.2.2](https://github.com/libp2p/js-interfaces/compare/v0.2.1...v0.2.2) (2020-01-17)
### Bug Fixes
* **connection:** dont require remoteAddr on creation ([#20](https://github.com/libp2p/js-interfaces/issues/20)) ([5967834](https://github.com/libp2p/js-interfaces/commit/5967834))
<a name="0.2.1"></a>
## [0.2.1](https://github.com/libp2p/js-interfaces/compare/v0.2.0...v0.2.1) (2019-12-28)
### Features
* add crypto transmission error ([#17](https://github.com/libp2p/js-interfaces/issues/17)) ([d98bb23](https://github.com/libp2p/js-interfaces/commit/d98bb23))
<a name="0.2.0"></a> <a name="0.2.0"></a>
# [0.2.0](https://github.com/libp2p/js-interfaces/compare/v0.1.7...v0.2.0) (2019-12-20) # [0.2.0](https://github.com/libp2p/js-interfaces/compare/v0.1.7...v0.2.0) (2019-12-20)

View File

@ -1,6 +1,6 @@
{ {
"name": "libp2p-interfaces", "name": "libp2p-interfaces",
"version": "0.2.0", "version": "0.2.7",
"description": "Interfaces for JS Libp2p", "description": "Interfaces for JS Libp2p",
"leadMaintainer": "Jacob Heun <jacobheun@gmail.com>", "leadMaintainer": "Jacob Heun <jacobheun@gmail.com>",
"main": "src/index.js", "main": "src/index.js",
@ -34,8 +34,8 @@
"homepage": "https://github.com/libp2p/js-interfaces#readme", "homepage": "https://github.com/libp2p/js-interfaces#readme",
"dependencies": { "dependencies": {
"abort-controller": "^3.0.0", "abort-controller": "^3.0.0",
"abortable-iterator": "^2.1.0", "abortable-iterator": "^3.0.0",
"async-iterator-to-pull-stream": "^1.3.0", "buffer": "^5.5.0",
"chai": "^4.2.0", "chai": "^4.2.0",
"chai-checkmark": "^1.0.1", "chai-checkmark": "^1.0.1",
"class-is": "^1.1.0", "class-is": "^1.1.0",
@ -43,20 +43,20 @@
"dirty-chai": "^2.0.1", "dirty-chai": "^2.0.1",
"err-code": "^2.0.0", "err-code": "^2.0.0",
"it-goodbye": "^2.0.1", "it-goodbye": "^2.0.1",
"it-pair": "^1.0.0",
"it-pipe": "^1.0.1",
"libp2p-tcp": "^0.14.1", "libp2p-tcp": "^0.14.1",
"multiaddr": "^7.1.0", "multiaddr": "^7.1.0",
"p-limit": "^2.2.1", "p-limit": "^2.2.2",
"p-wait-for": "^3.1.0", "p-wait-for": "^3.1.0",
"peer-id": "^0.13.3", "peer-id": "^0.13.3",
"sinon": "^7.5.0", "peer-info": "^0.17.0",
"sinon": "^9.0.0",
"streaming-iterables": "^4.1.0" "streaming-iterables": "^4.1.0"
}, },
"devDependencies": { "devDependencies": {
"aegir": "^20.4.1", "aegir": "^21.0.2",
"it-handshake": "^1.0.0", "it-handshake": "^1.0.1"
"it-pair": "^1.0.0",
"it-pipe": "^1.0.1",
"peer-info": "^0.17.0"
}, },
"contributors": [ "contributors": [
"Alan Shaw <alan.shaw@protocol.ai>", "Alan Shaw <alan.shaw@protocol.ai>",
@ -66,6 +66,7 @@
"Friedel Ziegelmayer <dignifiedquire@gmail.com>", "Friedel Ziegelmayer <dignifiedquire@gmail.com>",
"Greg Zuro <gregzuro@users.noreply.github.com>", "Greg Zuro <gregzuro@users.noreply.github.com>",
"Jacob Heun <jacobheun@gmail.com>", "Jacob Heun <jacobheun@gmail.com>",
"Jacob Heun <jacobheun@Jacobs-MacBook-Pro.local>",
"James Ray <16969914+jamesray1@users.noreply.github.com>", "James Ray <16969914+jamesray1@users.noreply.github.com>",
"Jeffrey Hulten <jhulten@gmail.com>", "Jeffrey Hulten <jhulten@gmail.com>",
"João Santos <joaosantos15@users.noreply.github.com>", "João Santos <joaosantos15@users.noreply.github.com>",
@ -78,6 +79,7 @@
"Sathya Narrayanan <plasmashadowx@gmail.com>", "Sathya Narrayanan <plasmashadowx@gmail.com>",
"Vasco Santos <vasco.santos@moxy.studio>", "Vasco Santos <vasco.santos@moxy.studio>",
"Vasco Santos <vasco.santos@ua.pt>", "Vasco Santos <vasco.santos@ua.pt>",
"dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>",
"dirkmc <dirkmdev@gmail.com>", "dirkmc <dirkmdev@gmail.com>",
"dmitriy ryajov <dryajov@dmitriys-MBP.HomeNET>", "dmitriy ryajov <dryajov@dmitriys-MBP.HomeNET>",
"greenkeeperio-bot <support@greenkeeper.io>", "greenkeeperio-bot <support@greenkeeper.io>",

View File

@ -110,7 +110,7 @@ const conn = new Connection({
Creates a new Connection instance. Creates a new Connection instance.
`localAddr` is the optional [multiaddr](https://github.com/multiformats/multiaddr) address used by the local peer to reach the remote. `localAddr` is the optional [multiaddr](https://github.com/multiformats/multiaddr) address used by the local peer to reach the remote.
`remoteAddr` is the [multiaddr](https://github.com/multiformats/multiaddr) address used to communicate with the remote peer. `remoteAddr` is the optional [multiaddr](https://github.com/multiformats/multiaddr) address used to communicate with the remote peer.
`localPeer` is the [PeerId](https://github.com/libp2p/js-peer-id) of the local peer. `localPeer` is the [PeerId](https://github.com/libp2p/js-peer-id) of the local peer.
`remotePeer` is the [PeerId](https://github.com/libp2p/js-peer-id) of the remote peer. `remotePeer` is the [PeerId](https://github.com/libp2p/js-peer-id) of the remote peer.
`newStream` is the `function` responsible for getting a new muxed+multistream-selected stream. `newStream` is the `function` responsible for getting a new muxed+multistream-selected stream.

View File

@ -2,13 +2,56 @@
const PeerId = require('peer-id') const PeerId = require('peer-id')
const multiaddr = require('multiaddr') const multiaddr = require('multiaddr')
const withIs = require('class-is') const withIs = require('class-is')
const assert = require('assert')
const errCode = require('err-code') const errCode = require('err-code')
const Status = require('./status') const Status = require('./status')
function validateArgs (localAddr, localPeer, remotePeer, newStream, close, getStreams, stat) {
if (localAddr && !multiaddr.isMultiaddr(localAddr)) {
throw errCode(new Error('localAddr must be an instance of multiaddr'), 'ERR_INVALID_PARAMETERS')
}
if (!PeerId.isPeerId(localPeer)) {
throw errCode(new Error('localPeer must be an instance of peer-id'), 'ERR_INVALID_PARAMETERS')
}
if (!PeerId.isPeerId(remotePeer)) {
throw errCode(new Error('remotePeer must be an instance of peer-id'), 'ERR_INVALID_PARAMETERS')
}
if (typeof newStream !== 'function') {
throw errCode(new Error('new stream must be a function'), 'ERR_INVALID_PARAMETERS')
}
if (typeof close !== 'function') {
throw errCode(new Error('close must be a function'), 'ERR_INVALID_PARAMETERS')
}
if (typeof getStreams !== 'function') {
throw errCode(new Error('getStreams must be a function'), 'ERR_INVALID_PARAMETERS')
}
if (!stat) {
throw errCode(new Error('connection metadata object must be provided'), 'ERR_INVALID_PARAMETERS')
}
if (stat.direction !== 'inbound' && stat.direction !== 'outbound') {
throw errCode(new Error('direction must be "inbound" or "outbound"'), 'ERR_INVALID_PARAMETERS')
}
if (!stat.timeline) {
throw errCode(new Error('connection timeline object must be provided in the stat object'), 'ERR_INVALID_PARAMETERS')
}
if (!stat.timeline.open) {
throw errCode(new Error('connection open timestamp must be provided'), 'ERR_INVALID_PARAMETERS')
}
if (!stat.timeline.upgraded) {
throw errCode(new Error('connection upgraded timestamp must be provided'), 'ERR_INVALID_PARAMETERS')
}
}
/** /**
* An implementation of the js-libp2p connection. * An implementation of the js-libp2p connection.
* Any libp2p transport should use an upgrader to return this connection. * Any libp2p transport should use an upgrader to return this connection.
@ -18,7 +61,7 @@ class Connection {
* Creates an instance of Connection. * Creates an instance of Connection.
* @param {object} properties properties of the connection. * @param {object} properties properties of the connection.
* @param {multiaddr} [properties.localAddr] local multiaddr of the connection if known. * @param {multiaddr} [properties.localAddr] local multiaddr of the connection if known.
* @param {multiaddr} properties.remoteAddr remote multiaddr of the connection. * @param {multiaddr} [properties.remoteAddr] remote multiaddr of the connection.
* @param {PeerId} properties.localPeer local peer-id. * @param {PeerId} properties.localPeer local peer-id.
* @param {PeerId} properties.remotePeer remote peer-id. * @param {PeerId} properties.remotePeer remote peer-id.
* @param {function} properties.newStream new stream muxer function. * @param {function} properties.newStream new stream muxer function.
@ -33,18 +76,7 @@ class Connection {
* @param {string} [properties.stat.encryption] connection encryption method identifier. * @param {string} [properties.stat.encryption] connection encryption method identifier.
*/ */
constructor ({ localAddr, remoteAddr, localPeer, remotePeer, newStream, close, getStreams, stat }) { constructor ({ localAddr, remoteAddr, localPeer, remotePeer, newStream, close, getStreams, stat }) {
localAddr && assert(multiaddr.isMultiaddr(localAddr), 'localAddr must be an instance of multiaddr') validateArgs(localAddr, localPeer, remotePeer, newStream, close, getStreams, stat)
assert(multiaddr.isMultiaddr(remoteAddr), 'remoteAddr must be an instance of multiaddr')
assert(PeerId.isPeerId(localPeer), 'localPeer must be an instance of peer-id')
assert(PeerId.isPeerId(remotePeer), 'remotePeer must be an instance of peer-id')
assert(typeof newStream === 'function', 'new stream must be a function')
assert(typeof close === 'function', 'close must be a function')
assert(typeof getStreams === 'function', 'getStreams must be a function')
assert(stat, 'connection metadata object must be provided')
assert(stat.direction === 'inbound' || stat.direction === 'outbound', 'direction must be "inbound" or "outbound"')
assert(stat.timeline, 'connection timeline object must be provided in the stat object')
assert(stat.timeline.open, 'connection open timestamp must be provided')
assert(stat.timeline.upgraded, 'connection upgraded timestamp must be provided')
/** /**
* Connection identifier. * Connection identifier.
@ -139,7 +171,7 @@ class Connection {
const { stream, protocol } = await this._newStream(protocols) const { stream, protocol } = await this._newStream(protocols)
this.addStream(stream, protocol) this.addStream(stream, { protocol })
return { return {
stream, stream,

View File

@ -120,6 +120,19 @@ module.exports = (test) => {
expect(connection.stat.status).to.equal(Status.CLOSED) expect(connection.stat.status).to.equal(Status.CLOSED)
}) })
it('should properly track streams', async () => {
// Open stream
const protocol = '/echo/0.0.1'
const { stream } = await connection.newStream(protocol)
const trackedStream = connection.registry.get(stream.id)
expect(trackedStream).to.have.property('protocol', protocol)
// Close stream
await stream.close()
expect(connection.registry.get(stream.id)).to.not.exist()
})
it('should support a proxy on the timeline', async () => { it('should support a proxy on the timeline', async () => {
sinon.spy(proxyHandler, 'set') sinon.spy(proxyHandler, 'set')
expect(connection.stat.timeline.close).to.not.exist() expect(connection.stat.timeline.close).to.not.exist()

View File

@ -85,6 +85,7 @@ Common crypto errors come with the interface, and can be imported directly. All
```js ```js
const { const {
InvalidCryptoExchangeError, InvalidCryptoExchangeError,
InvalidCryptoTransmissionError,
UnexpectedPeerError UnexpectedPeerError
} = require('libp2p-interfaces/src/crypto/errors') } = require('libp2p-interfaces/src/crypto/errors')
@ -95,4 +96,5 @@ console.log(error.code === UnexpectedPeerError.code) // true
### Error Types ### Error Types
- `InvalidCryptoExchangeError` - Should be thrown when a peer provides data that is insufficient to finish the crypto exchange. - `InvalidCryptoExchangeError` - Should be thrown when a peer provides data that is insufficient to finish the crypto exchange.
- `InvalidCryptoTransmissionError` - Should be thrown when an error occurs during encryption/decryption.
- `UnexpectedPeerError` - Should be thrown when the expected peer id does not match the peer id determined via the crypto exchange. - `UnexpectedPeerError` - Should be thrown when the expected peer id does not match the peer id determined via the crypto exchange.

View File

@ -22,7 +22,19 @@ class InvalidCryptoExchangeError extends Error {
} }
} }
class InvalidCryptoTransmissionError extends Error {
constructor (message = 'Invalid crypto transmission') {
super(message)
this.code = InvalidCryptoTransmissionError.code
}
static get code () {
return 'ERR_INVALID_CRYPTO_TRANSMISSION'
}
}
module.exports = { module.exports = {
UnexpectedPeerError, UnexpectedPeerError,
InvalidCryptoExchangeError InvalidCryptoExchangeError,
InvalidCryptoTransmissionError
} }

View File

@ -1,6 +1,7 @@
/* eslint-env mocha */ /* eslint-env mocha */
'use strict' 'use strict'
const { Buffer } = require('buffer')
const duplexPair = require('it-pair/duplex') const duplexPair = require('it-pair/duplex')
const pipe = require('it-pipe') const pipe = require('it-pipe')
const peers = require('../../utils/peers') const peers = require('../../utils/peers')

View File

@ -2,6 +2,7 @@
/* eslint max-nested-callbacks: ["error", 8] */ /* eslint max-nested-callbacks: ["error", 8] */
'use strict' 'use strict'
const { Buffer } = require('buffer')
const pair = require('it-pair/duplex') const pair = require('it-pair/duplex')
const pipe = require('it-pipe') const pipe = require('it-pipe')
const { consume } = require('streaming-iterables') const { consume } = require('streaming-iterables')

View File

@ -1,6 +1,5 @@
'use strict' 'use strict'
const assert = require('assert')
const withIs = require('class-is') const withIs = require('class-is')
const Topology = require('./index') const Topology = require('./index')
@ -24,12 +23,21 @@ class MulticodecTopology extends Topology {
}) { }) {
super({ min, max, handlers }) super({ min, max, handlers })
assert(multicodecs, 'one or more multicodec should be provided') if (!multicodecs) {
assert(handlers, 'the handlers should be provided') throw new Error('one or more multicodec should be provided')
assert(handlers.onConnect && typeof handlers.onConnect === 'function', }
'the \'onConnect\' handler must be provided')
assert(handlers.onDisconnect && typeof handlers.onDisconnect === 'function', if (!handlers) {
'the \'onDisconnect\' handler must be provided') throw new Error('the handlers should be provided')
}
if (typeof handlers.onConnect !== 'function') {
throw new Error('the \'onConnect\' handler must be provided')
}
if (typeof handlers.onDisconnect !== 'function') {
throw new Error('the \'onDisconnect\' handler must be provided')
}
this.multicodecs = Array.isArray(multicodecs) ? multicodecs : [multicodecs] this.multicodecs = Array.isArray(multicodecs) ? multicodecs : [multicodecs]
this._registrar = undefined this._registrar = undefined

View File

@ -2,6 +2,7 @@
/* eslint-env mocha */ /* eslint-env mocha */
'use strict' 'use strict'
const { Buffer } = require('buffer')
const chai = require('chai') const chai = require('chai')
const dirtyChai = require('dirty-chai') const dirtyChai = require('dirty-chai')
const expect = chai.expect const expect = chai.expect
@ -70,7 +71,7 @@ module.exports = (common) => {
]) ])
// Give the listener a chance to finish its upgrade // Give the listener a chance to finish its upgrade
await new Promise(resolve => setTimeout(resolve, 0)) await pWaitFor(() => listenerConns.length === 2)
// Wait for the data send and close to finish // Wait for the data send and close to finish
await Promise.all([ await Promise.all([

View File

@ -25,7 +25,7 @@ describe('compliance tests', () => {
const openStreams = [] const openStreams = []
let streamId = 0 let streamId = 0
return new Connection({ const connection = new Connection({
localPeer, localPeer,
remotePeer, remotePeer,
localAddr, localAddr,
@ -43,7 +43,10 @@ describe('compliance tests', () => {
const id = streamId++ const id = streamId++
const stream = pair() const stream = pair()
stream.close = () => stream.sink([]) stream.close = async () => {
await stream.sink([])
connection.removeStream(stream.id)
}
stream.id = id stream.id = id
openStreams.push(stream) openStreams.push(stream)
@ -57,6 +60,7 @@ describe('compliance tests', () => {
getStreams: () => openStreams, getStreams: () => openStreams,
...properties ...properties
}) })
return connection
}, },
async teardown () { async teardown () {
// cleanup resources created by setup() // cleanup resources created by setup()

View File

@ -0,0 +1,48 @@
/* eslint-env mocha */
'use strict'
const { Connection } = require('../../src/connection')
const peers = require('../../src/utils/peers')
const PeerId = require('peer-id')
const pair = require('it-pair')
describe('connection tests', () => {
it('should not require local or remote addrs', async () => {
const [localPeer, remotePeer] = await Promise.all([
PeerId.createFromJSON(peers[0]),
PeerId.createFromJSON(peers[1])
])
const openStreams = []
let streamId = 0
return new Connection({
localPeer,
remotePeer,
stat: {
timeline: {
open: Date.now() - 10,
upgraded: Date.now()
},
direction: 'outbound',
encryption: '/secio/1.0.0',
multiplexer: '/mplex/6.7.0'
},
newStream: (protocols) => {
const id = streamId++
const stream = pair()
stream.close = () => stream.sink([])
stream.id = id
openStreams.push(stream)
return {
stream,
protocol: protocols[0]
}
},
close: () => {},
getStreams: () => openStreams
})
})
})