diff --git a/.aegir.js b/.aegir.js index d562f4be..6895593b 100644 --- a/.aegir.js +++ b/.aegir.js @@ -1,130 +1,23 @@ 'use strict' -const pull = require('pull-stream') -const WebSocketStarRendezvous = require('libp2p-websocket-star-rendezvous') -const sigServer = require('libp2p-webrtc-star/src/sig-server') -const promisify = require('promisify-es6') -const mplex = require('pull-mplex') -const spdy = require('libp2p-spdy') -const PeerBook = require('peer-book') -const PeerId = require('peer-id') -const PeerInfo = require('peer-info') -const path = require('path') -const Switch = require('./src/switch') +const TransportManager = require('./src/transport-manager') +const mockUpgrader = require('./test/utils/mockUpgrader') +const { MULTIADDRS_WEBSOCKETS } = require('./test/fixtures/browser') +let tm + const WebSockets = require('libp2p-websockets') -const Node = require('./test/utils/bundle-nodejs.js') -const { - getPeerRelay, - WRTC_RENDEZVOUS_MULTIADDR, - WS_RENDEZVOUS_MULTIADDR -} = require('./test/utils/constants') - -let wrtcRendezvous -let wsRendezvous -let node -let peerInfo -let switchA -let switchB - -function echo (protocol, conn) { pull(conn, conn) } -function idJSON (id) { - const p = path.join(__dirname, `./test/switch/test-data/id-${id}.json`) - return require(p) -} - -function createSwitchA () { - return new Promise((resolve, reject) => { - PeerId.createFromJSON(idJSON(1), (err, id) => { - if (err) { return reject(err) } - - const peerA = new PeerInfo(id) - const maA = '/ip4/127.0.0.1/tcp/15337/ws' - - peerA.multiaddrs.add(maA) - const sw = new Switch(peerA, new PeerBook()) - - sw.transport.add('ws', new WebSockets()) - sw.start((err) => { - if (err) { return reject(err) } - resolve(sw) - }) - }) - }) -} - -function createSwitchB () { - return new Promise((resolve, reject) => { - PeerId.createFromJSON(idJSON(2), (err, id) => { - if (err) { return reject(err) } - - const peerB = new PeerInfo(id) - const maB = '/ip4/127.0.0.1/tcp/15347/ws' - - peerB.multiaddrs.add(maB) - const sw = new Switch(peerB, new PeerBook()) - - sw.transport.add('ws', new WebSockets()) - sw.connection.addStreamMuxer(mplex) - sw.connection.addStreamMuxer(spdy) - sw.connection.reuse() - sw.handle('/echo/1.0.0', echo) - sw.start((err) => { - if (err) { return reject(err) } - resolve(sw) - }) - }) - }) -} - const before = async () => { - [ - wrtcRendezvous, - wsRendezvous, - peerInfo, - switchA, - switchB - ] = await Promise.all([ - sigServer.start({ - port: WRTC_RENDEZVOUS_MULTIADDR.nodeAddress().port - // cryptoChallenge: true TODO: needs https://github.com/libp2p/js-libp2p-webrtc-star/issues/128 - }), - WebSocketStarRendezvous.start({ - port: WS_RENDEZVOUS_MULTIADDR.nodeAddress().port, - refreshPeerListIntervalMS: 1000, - strictMultiaddr: false, - cryptoChallenge: true - }), - getPeerRelay(), - createSwitchA(), - createSwitchB() - ]) - - node = new Node({ - peerInfo, - config: { - relay: { - enabled: true, - hop: { - enabled: true, - active: true - } - } - } + tm = new TransportManager({ + upgrader: mockUpgrader, + onConnection: () => {} }) - - node.handle('/echo/1.0.0', (protocol, conn) => pull(conn, conn)) - await node.start() + tm.add(WebSockets.prototype[Symbol.toStringTag], WebSockets) + await tm.listen(MULTIADDRS_WEBSOCKETS) } -const after = () => { - return Promise.all([ - wrtcRendezvous.stop(), - wsRendezvous.stop(), - node.stop(), - promisify(switchA.stop, { context: switchA })(), - promisify(switchB.stop, { context: switchB })() - ]) +const after = async () => { + await tm.close() } module.exports = { diff --git a/.travis.yml b/.travis.yml index 8fe36cb4..5eb222c1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,16 +29,14 @@ jobs: addons: chrome: stable script: - - npx aegir test -t browser - - npx aegir test -t webworker + - npx aegir test -t browser -t webworker - stage: test name: firefox addons: firefox: latest script: - - npx aegir test -t browser -- --browsers FirefoxHeadless - - npx aegir test -t webworker -- --browsers FirefoxHeadless + - npx aegir test -t browser -t webworker -- --browsers FirefoxHeadless notifications: email: false \ No newline at end of file diff --git a/package.json b/package.json index 406134ff..db94adc3 100644 --- a/package.json +++ b/package.json @@ -36,9 +36,6 @@ }, "homepage": "https://libp2p.io", "license": "MIT", - "browser": { - "./test/utils/bundle-nodejs": "./test/utils/bundle-browser" - }, "engines": { "node": ">=10.0.0", "npm": ">=6.0.0" @@ -54,25 +51,24 @@ "interface-connection": "~0.3.3", "latency-monitor": "~0.2.1", "libp2p-crypto": "^0.16.2", - "libp2p-websockets": "^0.12.2", - "mafmt": "^6.0.7", + "mafmt": "^7.0.0", "merge-options": "^1.0.1", "moving-average": "^1.0.0", - "multiaddr": "^6.1.0", + "multiaddr": "^7.1.0", "multistream-select": "~0.14.6", "once": "^1.4.0", + "p-settle": "^3.1.0", "peer-book": "^0.9.1", - "peer-id": "^0.12.2", - "peer-info": "~0.15.1", + "peer-id": "^0.13.3", + "peer-info": "^0.17.0", + "promisify-es6": "^1.0.3", + "protons": "^1.0.1", "pull-cat": "^1.1.11", "pull-defer": "~0.2.3", "pull-handshake": "^1.1.4", "pull-reader": "^1.3.1", "pull-stream": "^3.6.9", - "promisify-es6": "^1.0.3", - "protons": "^1.0.1", "retimer": "^2.0.0", - "superstruct": "^0.6.0", "xsalsa20": "^1.0.2" }, "devDependencies": { @@ -96,10 +92,8 @@ "libp2p-pnet": "~0.1.0", "libp2p-secio": "^0.11.1", "libp2p-spdy": "^0.13.2", - "libp2p-tcp": "^0.13.0", - "libp2p-webrtc-star": "^0.16.1", - "libp2p-websocket-star": "~0.10.2", - "libp2p-websocket-star-rendezvous": "~0.4.1", + "libp2p-tcp": "^0.14.1", + "libp2p-websockets": "^0.13.0", "lodash.times": "^4.3.2", "nock": "^10.0.6", "portfinder": "^1.0.20", diff --git a/src/config.js b/src/config.js index 587167cf..8f87b602 100644 --- a/src/config.js +++ b/src/config.js @@ -1,8 +1,6 @@ 'use strict' const mergeOptions = require('merge-options') -const { struct, superstruct } = require('superstruct') -const { optional, list } = struct const DefaultConfig = { connectionManager: { @@ -38,67 +36,10 @@ const DefaultConfig = { } } -// Define custom types -const s = superstruct({ - types: { - transport: value => { - if (value.length === 0) return 'ERROR_EMPTY' - value.forEach(i => { - if (!i.dial) return 'ERR_NOT_A_TRANSPORT' - }) - return true - }, - protector: value => { - if (!value.protect) return 'ERR_NOT_A_PROTECTOR' - return true - } - } -}) - -const modulesSchema = s({ - connEncryption: optional(list([s('object|function')])), - // this is hacky to simulate optional because interface doesnt work correctly with it - // change to optional when fixed upstream - connProtector: s('undefined|protector'), - contentRouting: optional(list(['object'])), - dht: optional(s('null|function|object')), - pubsub: optional(s('null|function|object')), - peerDiscovery: optional(list([s('object|function')])), - peerRouting: optional(list(['object'])), - streamMuxer: optional(list([s('object|function')])), - transport: 'transport' -}) - -const configSchema = s({ - peerDiscovery: 'object?', - relay: 'object?', - dht: 'object?', - pubsub: 'object?' -}) - -const optionsSchema = s({ - switch: 'object?', - connectionManager: 'object?', - datastore: 'object?', - peerInfo: 'object', - peerBook: 'object?', - modules: modulesSchema, - config: configSchema -}) - module.exports.validate = (opts) => { opts = mergeOptions(DefaultConfig, opts) - const [error, options] = optionsSchema.validate(opts) - // Improve errors throwed, reduce stack by throwing here and add reason to the message - if (error) { - throw new Error(`${error.message}${error.reason ? ' - ' + error.reason : ''}`) - } else { - // Throw when dht is enabled but no dht module provided - if (options.config.dht.enabled) { - s('function|object')(options.modules.dht) - } - } + if (opts.modules.transport.length < 1) throw new Error("'options.modules.transport' must contain at least 1 transport") - return options + return opts } diff --git a/src/errors.js b/src/errors.js index 19fb8530..5219891a 100644 --- a/src/errors.js +++ b/src/errors.js @@ -9,5 +9,9 @@ exports.codes = { DHT_DISABLED: 'ERR_DHT_DISABLED', PUBSUB_NOT_STARTED: 'ERR_PUBSUB_NOT_STARTED', ERR_NODE_NOT_STARTED: 'ERR_NODE_NOT_STARTED', - ERR_DISCOVERED_SELF: 'ERR_DISCOVERED_SELF' + ERR_NO_VALID_ADDRESSES: 'ERR_NO_VALID_ADDRESSES', + ERR_DISCOVERED_SELF: 'ERR_DISCOVERED_SELF', + ERR_DUPLICATE_TRANSPORT: 'ERR_DUPLICATE_TRANSPORT', + ERR_INVALID_KEY: 'ERR_INVALID_KEY', + ERR_TRANSPORT_UNAVAILABLE: 'ERR_TRANSPORT_UNAVAILABLE' } diff --git a/src/index.js b/src/index.js index 62815a9e..b973739b 100644 --- a/src/index.js +++ b/src/index.js @@ -9,15 +9,12 @@ const errCode = require('err-code') const promisify = require('promisify-es6') const each = require('async/each') -const series = require('async/series') -const parallel = require('async/parallel') const nextTick = require('async/nextTick') const PeerBook = require('peer-book') const PeerInfo = require('peer-info') const Switch = require('./switch') const Ping = require('./ping') -const WebSockets = require('libp2p-websockets') const ConnectionManager = require('./connection-manager') const { emitFirst } = require('./util') @@ -29,6 +26,8 @@ const { getPeerInfoRemote } = require('./get-peer-info') const validateConfig = require('./config').validate const { codes } = require('./errors') +const TransportManager = require('./transport-manager') + const notStarted = (action, state) => { return errCode( new Error(`libp2p cannot ${action} when not started; state is ${state}`), @@ -67,6 +66,21 @@ class Libp2p extends EventEmitter { this.stats = this._switch.stats this.connectionManager = new ConnectionManager(this, this._options.connectionManager) + // Setup the transport manager + this.transportManager = new TransportManager({ + libp2p: this, + // TODO: set the actual upgrader + upgrader: { + upgradeInbound: (maConn) => maConn, + upgradeOutbound: (maConn) => maConn + }, + // TODO: Route incoming connections to a multiplex protocol router + onConnection: () => {} + }) + this._modules.transport.forEach((Transport) => { + this.transportManager.add(Transport.prototype[Symbol.toStringTag], Transport) + }) + // Attach stream multiplexers if (this._modules.streamMuxer) { const muxers = this._modules.streamMuxer @@ -336,146 +350,42 @@ class Libp2p extends EventEmitter { this._switch.unhandle(protocol) } - _onStarting () { + async _onStarting () { if (!this._modules.transport) { this.emit('error', new Error('no transports were present')) return this.state('abort') } - let ws - - // so that we can have webrtc-star addrs without adding manually the id - const maOld = [] - const maNew = [] - this.peerInfo.multiaddrs.toArray().forEach((ma) => { - if (!ma.getPeerId()) { - maOld.push(ma) - maNew.push(ma.encapsulate('/p2p/' + this.peerInfo.id.toB58String())) - } - }) - this.peerInfo.multiaddrs.replace(maOld, maNew) - const multiaddrs = this.peerInfo.multiaddrs.toArray() - this._modules.transport.forEach((Transport) => { - let t + // Start parallel tasks + try { + await Promise.all([ + this.transportManager.listen(multiaddrs) + ]) + } catch (err) { + log.error(err) + this.emit('error', err) + return this.state('stop') + } - if (typeof Transport === 'function') { - t = new Transport({ libp2p: this }) - } else { - t = Transport - } - - if (t.filter(multiaddrs).length > 0) { - this._switch.transport.add(t.tag || t[Symbol.toStringTag], t) - } else if (WebSockets.isWebSockets(t)) { - // TODO find a cleaner way to signal that a transport is always used - // for dialing, even if no listener - ws = t - } - this._transport.push(t) - }) - - series([ - (cb) => { - this.connectionManager.start() - this._switch.start(cb) - }, - (cb) => { - if (ws) { - // always add dialing on websockets - this._switch.transport.add(ws.tag || ws.constructor.name, ws) - } - - // detect which multiaddrs we don't have a transport for and remove them - const multiaddrs = this.peerInfo.multiaddrs.toArray() - - multiaddrs.forEach((multiaddr) => { - if (!multiaddr.toString().match(/\/p2p-circuit($|\/)/) && - !this._transport.find((transport) => transport.filter(multiaddr).length > 0)) { - this.peerInfo.multiaddrs.delete(multiaddr) - } - }) - cb() - }, - (cb) => { - if (this._dht) { - this._dht.start(() => { - this._dht.on('peer', this._peerDiscovered) - cb() - }) - } else { - cb() - } - }, - (cb) => { - if (this.pubsub) { - return this.pubsub.start(cb) - } - cb() - }, - // Peer Discovery - (cb) => { - if (this._modules.peerDiscovery) { - this._setupPeerDiscovery(cb) - } else { - cb() - } - } - ], (err) => { - if (err) { - log.error(err) - this.emit('error', err) - return this.state('stop') - } - this.state('done') - }) + // libp2p has started + this.state('done') } - _onStopping () { - series([ - (cb) => { - // stop all discoveries before continuing with shutdown - parallel( - this._discovery.map((d) => { - d.removeListener('peer', this._peerDiscovered) - return (_cb) => d.stop((err) => { - log.error('an error occurred stopping the discovery service', err) - _cb() - }) - }), - cb - ) - }, - (cb) => { - if (this.pubsub) { - return this.pubsub.stop(cb) - } - cb() - }, - (cb) => { - if (this._dht) { - this._dht.removeListener('peer', this._peerDiscovered) - return this._dht.stop(cb) - } - cb() - }, - (cb) => { - this.connectionManager.stop() - this._switch.stop(cb) - }, - (cb) => { - // Ensures idempotent restarts, ignore any errors - // from removeAll, they're not useful at this point - this._switch.transport.removeAll(() => cb()) - } - ], (err) => { + async _onStopping () { + // Start parallel tasks + try { + await this.transportManager.close() + } catch (err) { if (err) { log.error(err) this.emit('error', err) } - this.state('done') - }) + } + + // libp2p has stopped + this.state('done') } /** diff --git a/src/transport-manager.js b/src/transport-manager.js new file mode 100644 index 00000000..371f2f57 --- /dev/null +++ b/src/transport-manager.js @@ -0,0 +1,177 @@ +'use strict' + +const pSettle = require('p-settle') +const { codes } = require('./errors') +const errCode = require('err-code') +const debug = require('debug') +const log = debug('libp2p:transports') +log.error = debug('libp2p:transports:error') + +class TransportManager { + /** + * @constructor + * @param {object} options + * @param {Libp2p} options.libp2p The Libp2p instance. It will be passed to the transports. + * @param {Upgrader} options.upgrader The upgrader to provide to the transports + * @param {function(Connection)} options.onConnection Called whenever an incoming connection is received + */ + constructor ({ libp2p, upgrader, onConnection }) { + this.libp2p = libp2p + this.upgrader = upgrader + this._transports = new Map() + this._listeners = new Map() + this.onConnection = onConnection + } + + /** + * Adds a `Transport` to the manager + * + * @param {String} key + * @param {Transport} Transport + * @returns {void} + */ + add (key, Transport) { + log('adding %s', key) + if (!key) { + throw errCode(new Error(`Transport must have a valid key, was given '${key}'`), codes.ERR_INVALID_KEY) + } + if (this._transports.has(key)) { + throw errCode(new Error('There is already a transport with this key'), codes.ERR_DUPLICATE_TRANSPORT) + } + + const transport = new Transport({ + libp2p: this.libp2p, + upgrader: this.upgrader + }) + + this._transports.set(key, transport) + this._listeners.set(key, []) + } + + /** + * Stops all listeners + * @async + */ + async close () { + const tasks = [] + for (const [key, listeners] of this._listeners) { + log('closing listeners for %s', key) + while (listeners.length) { + tasks.push(listeners.pop().close()) + } + } + + await Promise.all(tasks) + this._listeners.clear() + } + + /** + * Dials the given Multiaddr over it's supported transport + * @param {Multiaddr} ma + * @param {*} options + * @returns {Promise} + */ + async dial (ma, options) { + const transport = this.transportForMultiaddr(ma) + if (!transport) { + throw errCode(new Error(`No transport available for address ${String(ma)}`), codes.ERR_TRANSPORT_UNAVAILABLE) + } + const conn = await transport.dial(ma, options) + return conn + } + + /** + * Returns all Multiaddr's the listeners are using + * @returns {Multiaddr[]} + */ + getAddrs () { + let addrs = [] + for (const listeners of this._listeners.values()) { + for (const listener of listeners) { + addrs = [...addrs, ...listener.getAddrs()] + } + } + return addrs + } + + /** + * Finds a transport that matches the given Multiaddr + * @param {Multiaddr} ma + * @returns {Transport|null} + */ + transportForMultiaddr (ma) { + for (const transport of this._transports.values()) { + const addrs = transport.filter([ma]) + if (addrs.length) return transport + } + return null + } + + /** + * Starts listeners for each given Multiaddr. + * @async + * @param {Multiaddr[]} addrs + */ + async listen (addrs) { + for (const [key, transport] of this._transports.entries()) { + const supportedAddrs = transport.filter(addrs) + const tasks = [] + + // For each supported multiaddr, create a listener + for (const addr of supportedAddrs) { + log('creating listener for %s on %s', key, addr) + const listener = transport.createListener({}, this.onConnection) + this._listeners.get(key).push(listener) + + // We need to attempt to listen on everything + tasks.push(listener.listen(addr)) + } + + const results = await pSettle(tasks) + // If we are listening on at least 1 address, succeed. + // TODO: we should look at adding a retry (`p-retry`) here to better support + // listening on remote addresses as they may be offline. We could then potentially + // just wait for any (`p-any`) listener to succeed on each transport before returning + const isListening = results.find(r => r.isFulfilled === true) + if (!isListening) { + throw errCode(new Error(`Transport (${key}) could not listen on any available address`), codes.ERR_NO_VALID_ADDRESSES) + } + } + } + + /** + * Removes the given transport from the manager. + * If a transport has any running listeners, they will be closed. + * + * @async + * @param {string} key + */ + async remove (key) { + log('removing %s', key) + if (this._listeners.has(key)) { + // Close any running listeners + for (const listener of this._listeners.get(key)) { + await listener.close() + } + } + + this._transports.delete(key) + this._listeners.delete(key) + } + + /** + * Removes all transports from the manager. + * If any listeners are running, they will be closed. + * @async + */ + async removeAll () { + const tasks = [] + for (const key of this._transports.keys()) { + tasks.push(this.remove(key)) + } + + await Promise.all(tasks) + } +} + +module.exports = TransportManager diff --git a/test/browser.js b/test/browser.js deleted file mode 100644 index cac1fae1..00000000 --- a/test/browser.js +++ /dev/null @@ -1,6 +0,0 @@ -'use strict' - -require('./circuit-relay.browser') -require('./transports.browser') - -require('./switch/browser') diff --git a/test/circuit-relay.browser.js b/test/circuit-relay.browser.js deleted file mode 100644 index 6455f3c6..00000000 --- a/test/circuit-relay.browser.js +++ /dev/null @@ -1,93 +0,0 @@ -/* eslint-env mocha */ -'use strict' - -const chai = require('chai') -chai.use(require('dirty-chai')) -const expect = chai.expect - -const createNode = require('./utils/create-node') -const tryEcho = require('./utils/try-echo') -const echo = require('./utils/echo') - -const { - getPeerRelay -} = require('./utils/constants') - -function setupNodeWithRelay (addrs, options = {}) { - options = { - config: { - relay: { - enabled: true - }, - ...options.config - }, - ...options - } - - return new Promise((resolve) => { - createNode(addrs, options, (err, node) => { - expect(err).to.not.exist() - - node.handle(echo.multicodec, echo) - node.start((err) => { - expect(err).to.not.exist() - resolve(node) - }) - }) - }) -} - -describe('circuit relay', () => { - let browserNode1 - let browserNode2 - let peerRelay - - before('get peer relay', async () => { - peerRelay = await getPeerRelay() - }) - - before('create the browser nodes', async () => { - [browserNode1, browserNode2] = await Promise.all([ - setupNodeWithRelay([]), - setupNodeWithRelay([]) - ]) - }) - - before('connect to the relay node', async () => { - await Promise.all( - [browserNode1, browserNode2].map((node) => { - return new Promise(resolve => { - node.dialProtocol(peerRelay, (err) => { - expect(err).to.not.exist() - resolve() - }) - }) - }) - ) - }) - - before('give time for HOP support to be determined', async () => { - await new Promise(resolve => { - setTimeout(resolve, 1e3) - }) - }) - - after(async () => { - await Promise.all( - [browserNode1, browserNode2].map((node) => { - return new Promise((resolve) => { - node.stop(resolve) - }) - }) - ) - }) - - it('should be able to echo over relay', (done) => { - browserNode1.dialProtocol(browserNode2.peerInfo, echo.multicodec, (err, conn) => { - expect(err).to.not.exist() - expect(conn).to.exist() - - tryEcho(conn, done) - }) - }) -}) diff --git a/test/circuit-relay.node.js b/test/circuit-relay.node.js deleted file mode 100644 index e93f1d7d..00000000 --- a/test/circuit-relay.node.js +++ /dev/null @@ -1,215 +0,0 @@ -/* eslint-env mocha */ -'use strict' - -const chai = require('chai') -chai.use(require('dirty-chai')) -const expect = chai.expect -const sinon = require('sinon') -const waterfall = require('async/waterfall') -const series = require('async/series') -const parallel = require('async/parallel') -const Circuit = require('../src/circuit') -const multiaddr = require('multiaddr') - -const createNode = require('./utils/create-node') -const tryEcho = require('./utils/try-echo') -const echo = require('./utils/echo') - -describe('circuit relay', () => { - const handlerSpies = [] - let relayNode1 - let relayNode2 - let nodeWS1 - let nodeWS2 - let nodeTCP1 - let nodeTCP2 - - function setupNode (addrs, options, callback) { - if (typeof options === 'function') { - callback = options - options = {} - } - - options = options || {} - - return createNode(addrs, options, (err, node) => { - expect(err).to.not.exist() - - node.handle('/echo/1.0.0', echo) - node.start((err) => { - expect(err).to.not.exist() - - handlerSpies.push(sinon.spy( - node._switch.transports[Circuit.tag].listeners[0].hopHandler, 'handle' - )) - - callback(node) - }) - }) - } - - before(function (done) { - this.timeout(20 * 1000) - - waterfall([ - // set up passive relay - (cb) => setupNode([ - '/ip4/0.0.0.0/tcp/0/ws', - '/ip4/0.0.0.0/tcp/0' - ], { - config: { - relay: { - enabled: true, - hop: { - enabled: true, - active: false // passive relay - } - } - } - }, (node) => { - relayNode1 = node - cb() - }), - // setup active relay - (cb) => setupNode([ - '/ip4/0.0.0.0/tcp/0/ws', - '/ip4/0.0.0.0/tcp/0' - ], { - config: { - relay: { - enabled: true, - hop: { - enabled: true, - active: false // passive relay - } - } - } - }, (node) => { - relayNode2 = node - cb() - }), - // setup node with WS - (cb) => setupNode([ - '/ip4/0.0.0.0/tcp/0/ws' - ], { - config: { - relay: { - enabled: true - } - } - }, (node) => { - nodeWS1 = node - cb() - }), - // setup node with WS - (cb) => setupNode([ - '/ip4/0.0.0.0/tcp/0/ws' - ], { - config: { - relay: { - enabled: true - } - } - }, (node) => { - nodeWS2 = node - cb() - }), - // set up node with TCP - (cb) => setupNode([ - '/ip4/0.0.0.0/tcp/0' - ], { - config: { - relay: { - enabled: true - } - } - }, (node) => { - nodeTCP1 = node - cb() - }), - // set up node with TCP - (cb) => setupNode([ - '/ip4/0.0.0.0/tcp/0' - ], { - config: { - relay: { - enabled: true - } - } - }, (node) => { - nodeTCP2 = node - cb() - }) - ], (err) => { - expect(err).to.not.exist() - - series([ - (cb) => nodeWS1.dial(relayNode1.peerInfo, cb), - (cb) => nodeWS1.dial(relayNode2.peerInfo, cb), - (cb) => nodeTCP1.dial(relayNode1.peerInfo, cb), - (cb) => nodeTCP2.dial(relayNode2.peerInfo, cb) - ], done) - }) - }) - - after((done) => { - parallel([ - (cb) => relayNode1.stop(cb), - (cb) => relayNode2.stop(cb), - (cb) => nodeWS1.stop(cb), - (cb) => nodeWS2.stop(cb), - (cb) => nodeTCP1.stop(cb), - (cb) => nodeTCP2.stop(cb) - ], done) - }) - - describe('any relay', function () { - this.timeout(20 * 1000) - - it('dial from WS1 to TCP1 over any R', (done) => { - nodeWS1.dialProtocol(nodeTCP1.peerInfo, '/echo/1.0.0', (err, conn) => { - expect(err).to.not.exist() - expect(conn).to.exist() - tryEcho(conn, done) - }) - }) - - it('fail to dial - no R from WS2 to TCP1', (done) => { - nodeWS2.dialProtocol(nodeTCP2.peerInfo, '/echo/1.0.0', (err, conn) => { - expect(err).to.exist() - expect(conn).to.not.exist() - done() - }) - }) - }) - - describe('explicit relay', function () { - this.timeout(20 * 1000) - - it('dial from WS1 to TCP1 over R1', (done) => { - nodeWS1.dialProtocol(nodeTCP1.peerInfo, '/echo/1.0.0', (err, conn) => { - expect(err).to.not.exist() - expect(conn).to.exist() - - tryEcho(conn, () => { - const addr = multiaddr(handlerSpies[0].args[2][0].dstPeer.addrs[0]).toString() - expect(addr).to.equal(`/ipfs/${nodeTCP1.peerInfo.id.toB58String()}`) - done() - }) - }) - }) - - it('dial from WS1 to TCP2 over R2', (done) => { - nodeWS1.dialProtocol(nodeTCP2.peerInfo, '/echo/1.0.0', (err, conn) => { - expect(err).to.not.exist() - expect(conn).to.exist() - - tryEcho(conn, () => { - const addr = multiaddr(handlerSpies[1].args[2][0].dstPeer.addrs[0]).toString() - expect(addr).to.equal(`/ipfs/${nodeTCP2.peerInfo.id.toB58String()}`) - done() - }) - }) - }) - }) -}) diff --git a/test/circuit/dialer.spec.js b/test/circuit/dialer.spec.js deleted file mode 100644 index 610c8364..00000000 --- a/test/circuit/dialer.spec.js +++ /dev/null @@ -1,303 +0,0 @@ -/* eslint-env mocha */ -/* eslint max-nested-callbacks: ["error", 5] */ - -'use strict' - -const Dialer = require('../../src/circuit/circuit/dialer') -const nodes = require('./fixtures/nodes') -const Connection = require('interface-connection').Connection -const multiaddr = require('multiaddr') -const PeerInfo = require('peer-info') -const PeerId = require('peer-id') -const pull = require('pull-stream/pull') -const values = require('pull-stream/sources/values') -const asyncMap = require('pull-stream/throughs/async-map') -const pair = require('pull-pair/duplex') -const pb = require('pull-protocol-buffers') - -const proto = require('../../src/circuit/protocol') -const utilsFactory = require('../../src/circuit/circuit/utils') - -const sinon = require('sinon') -const chai = require('chai') -const dirtyChai = require('dirty-chai') -const expect = chai.expect -chai.use(dirtyChai) - -describe('dialer tests', function () { - let dialer - - beforeEach(() => { - dialer = sinon.createStubInstance(Dialer) - }) - - afterEach(() => { - sinon.restore() - }) - - describe('.dial', function () { - beforeEach(function () { - dialer.relayPeers = new Map() - dialer.relayPeers.set(nodes.node2.id, new Connection()) - dialer.relayPeers.set(nodes.node3.id, new Connection()) - dialer.dial.callThrough() - }) - - it('fail on non circuit addr', function () { - const dstMa = multiaddr(`/ipfs/${nodes.node4.id}`) - expect(() => dialer.dial(dstMa, (err) => { - err.to.match(/invalid circuit address/) - })) - }) - - it('dial a peer', function (done) { - const dstMa = multiaddr(`/p2p-circuit/ipfs/${nodes.node3.id}`) - dialer._dialPeer.callsFake(function (dstMa, relay, callback) { - return callback(null, dialer.relayPeers.get(nodes.node3.id)) - }) - - dialer.dial(dstMa, (err, conn) => { - expect(err).to.not.exist() - expect(conn).to.be.an.instanceOf(Connection) - done() - }) - }) - - it('dial a peer over the specified relay', function (done) { - const dstMa = multiaddr(`/ipfs/${nodes.node3.id}/p2p-circuit/ipfs/${nodes.node4.id}`) - dialer._dialPeer.callsFake(function (dstMa, relay, callback) { - expect(relay.toString()).to.equal(`/ipfs/${nodes.node3.id}`) - return callback(null, new Connection()) - }) - - dialer.dial(dstMa, (err, conn) => { - expect(err).to.not.exist() - expect(conn).to.be.an.instanceOf(Connection) - done() - }) - }) - }) - - describe('.canHop', function () { - let fromConn = null - const peer = new PeerInfo(PeerId.createFromB58String('QmQWqGdndSpAkxfk8iyiJyz3XXGkrDNujvc8vEst3baubA')) - - let p = null - beforeEach(function () { - p = pair() - fromConn = new Connection(p[0]) - - dialer.relayPeers = new Map() - dialer.relayConns = new Map() - dialer.utils = utilsFactory({}) - dialer.canHop.callThrough() - dialer._dialRelayHelper.callThrough() - }) - - it('should handle successful CAN_HOP', (done) => { - dialer._dialRelay.callsFake((_, cb) => { - pull( - values([{ - type: proto.CircuitRelay.type.HOP, - code: proto.CircuitRelay.Status.SUCCESS - }]), - pb.encode(proto.CircuitRelay), - p[1] - ) - cb(null, fromConn) - }) - - dialer.canHop(peer, (err) => { - expect(err).to.not.exist() - expect(dialer.relayPeers.has(peer.id.toB58String())).to.be.ok() - done() - }) - }) - - it('should handle failed CAN_HOP', function (done) { - dialer._dialRelay.callsFake((_, cb) => { - pull( - values([{ - type: proto.CircuitRelay.type.HOP, - code: proto.CircuitRelay.Status.HOP_CANT_SPEAK_RELAY - }]), - pb.encode(proto.CircuitRelay), - p[1] - ) - cb(null, fromConn) - }) - - dialer.canHop(peer, (err) => { - expect(err).to.exist() - expect(dialer.relayPeers.has(peer.id.toB58String())).not.to.be.ok() - done() - }) - }) - }) - - describe('._dialPeer', function () { - beforeEach(function () { - dialer.relayPeers = new Map() - dialer.relayPeers.set(nodes.node1.id, new Connection()) - dialer.relayPeers.set(nodes.node2.id, new Connection()) - dialer.relayPeers.set(nodes.node3.id, new Connection()) - dialer._dialPeer.callThrough() - }) - - it('should dial a peer over any relay', function (done) { - const dstMa = multiaddr(`/ipfs/${nodes.node4.id}`) - dialer._negotiateRelay.callsFake(function (conn, dstMa, callback) { - if (conn === dialer.relayPeers.get(nodes.node3.id)) { - return callback(null, dialer.relayPeers.get(nodes.node3.id)) - } - - callback(new Error('error')) - }) - - dialer._dialPeer(dstMa, (err, conn) => { - expect(err).to.not.exist() - expect(conn).to.be.an.instanceOf(Connection) - expect(conn).to.deep.equal(dialer.relayPeers.get(nodes.node3.id)) - done() - }) - }) - - it('should fail dialing a peer over any relay', function (done) { - const dstMa = multiaddr(`/ipfs/${nodes.node4.id}`) - dialer._negotiateRelay.callsFake(function (conn, dstMa, callback) { - callback(new Error('error')) - }) - - dialer._dialPeer(dstMa, (err, conn) => { - expect(conn).to.be.undefined() - expect(err).to.not.be.null() - expect(err).to.equal('no relay peers were found or all relays failed to dial') - done() - }) - }) - }) - - describe('._negotiateRelay', function () { - const dstMa = multiaddr(`/ipfs/${nodes.node4.id}`) - - let conn = null - let peer = null - let p = null - - before((done) => { - PeerId.createFromJSON(nodes.node4, (_, peerId) => { - PeerInfo.create(peerId, (err, peerInfo) => { - peer = peerInfo - peer.multiaddrs.add('/p2p-circuit/ipfs/QmSswe1dCFRepmhjAMR5VfHeokGLcvVggkuDJm7RMfJSrE') - done(err) - }) - }) - }) - - beforeEach(() => { - dialer.swarm = { - _peerInfo: peer - } - dialer.utils = utilsFactory({}) - dialer.relayConns = new Map() - dialer._negotiateRelay.callThrough() - dialer._dialRelayHelper.callThrough() - peer = new PeerInfo(PeerId.createFromB58String('QmSswe1dCFRepmhjAMR5VfHeokGLcvVggkuDJm7RMfJSrE')) - p = pair() - conn = new Connection(p[1]) - }) - - it('should write the correct dst addr', function (done) { - dialer._dialRelay.callsFake((_, cb) => { - pull( - p[0], - pb.decode(proto.CircuitRelay), - asyncMap((msg, cb) => { - expect(msg.dstPeer.addrs[0]).to.deep.equal(dstMa.buffer) - cb(null, { - type: proto.CircuitRelay.Type.STATUS, - code: proto.CircuitRelay.Status.SUCCESS - }) - }), - pb.encode(proto.CircuitRelay), - p[0] - ) - cb(null, conn) - }) - - dialer._negotiateRelay(peer, dstMa, done) - }) - - it('should negotiate relay', function (done) { - dialer._dialRelay.callsFake((_, cb) => { - pull( - p[0], - pb.decode(proto.CircuitRelay), - asyncMap((msg, cb) => { - expect(msg.dstPeer.addrs[0]).to.deep.equal(dstMa.buffer) - cb(null, { - type: proto.CircuitRelay.Type.STATUS, - code: proto.CircuitRelay.Status.SUCCESS - }) - }), - pb.encode(proto.CircuitRelay), - p[0] - ) - cb(null, conn) - }) - - dialer._negotiateRelay(peer, dstMa, (err, conn) => { - expect(err).to.not.exist() - expect(conn).to.be.instanceOf(Connection) - done() - }) - }) - - it('should fail with an invalid peer id', function (done) { - const dstMa = multiaddr('/ip4/127.0.0.1/tcp/4001') - dialer._dialRelay.callsFake((_, cb) => { - pull( - p[0], - pb.decode(proto.CircuitRelay), - asyncMap((msg, cb) => { - expect(msg.dstPeer.addrs[0]).to.deep.equal(dstMa.buffer) - cb(null, { - type: proto.CircuitRelay.Type.STATUS, - code: proto.CircuitRelay.Status.SUCCESS - }) - }), - pb.encode(proto.CircuitRelay), - p[0] - ) - cb(null, conn) - }) - - dialer._negotiateRelay(peer, dstMa, (err, conn) => { - expect(err).to.exist() - expect(conn).to.not.exist() - done() - }) - }) - - it('should handle failed relay negotiation', function (done) { - dialer._dialRelay.callsFake((_, cb) => { - cb(null, conn) - pull( - values([{ - type: proto.CircuitRelay.Type.STATUS, - code: proto.CircuitRelay.Status.MALFORMED_MESSAGE - }]), - pb.encode(proto.CircuitRelay), - p[0] - ) - }) - - dialer._negotiateRelay(peer, dstMa, (err, conn) => { - expect(err).to.not.be.null() - expect(err).to.be.an.instanceOf(Error) - expect(err.message).to.be.equal('Got 400 error code trying to dial over relay') - done() - }) - }) - }) -}) diff --git a/test/circuit/fixtures/nodes.js b/test/circuit/fixtures/nodes.js deleted file mode 100644 index 71a274d9..00000000 --- a/test/circuit/fixtures/nodes.js +++ /dev/null @@ -1,25 +0,0 @@ -'use strict' - -exports.node1 = { - id: 'QmSswe1dCFRepmhjAMR5VfHeokGLcvVggkuDJm7RMfJSrE', - privKey: 'CAASpwkwggSjAgEAAoIBAQDJwzJPar4nylKY71Mm5q2BOED8uPf1ILvIi15VwVZWqter6flnlii/RKEcBypPbFqJHHa56MvybgQgrFmHKwDjnJvq4jyOZfR+o/D/99Ft1p2FAEBjImSXAgNpK4YsbyV5r0Q1+Avcj++aWWlLu6enUrL9WGzeUkf0U5L6XwXEPRUQdEojAIQi241P1hyqXX5gKAZVGqcPtKb6p1db3fcXodkS1G6JR90TopJHCqTCECp3SB9c6LlG7KXU92sIHJBlhOEEzGkEI1pM1SWnNnW5VLEypU7P56ifzzp4QxPNiJeC+cmE5SrgR3cXP44iKOuNVRJwBpCh5oNYqECzgqJ9AgMBAAECggEBAJpCdqXHrAmKJCqv2HiGqCODGhTfax1s4IYNIJwaTOPIjUrwgfKUGSVb2H4wcEX3RyVLsO6lMcFyIg/FFlJFK9HavE8SmFAbXZqxx6I9HE+JZjf5IEFrW1Mlg+wWDejNNe7adSF6O79wATaWo+32VNGWZilTQTGd4UvJ1jc9DZCh8zZeNhm4C6exXD45gMB0HI1t2ZNl47scsBEE4rV+s7F7y8Yk/tIsf0wSI/H8KSXS5I9aFxr3Z9c3HOfbVwhnIfNUDqcFTeU5BnhByYNLJ4v9xGj7puidcabVXkt2zLmm/LHbKVeGzec9LW5D+KkuB/pKaslsCXN6bVlu+SbVr9UCgYEA7MXfzZw36vDyfn4LPCN0wgzz11uh3cm31QzOPlWpA7hIsL/eInpvc8wa9yBRC1sRk41CedPHn913MR6EJi0Ne6/B1QOmRYBUjr60VPRNdTXCAiLykjXg6+TZ+AKnxlUGK1hjTo8krhpWq7iD/JchVlLoqDAXGFHvSxN0H3WEUm8CgYEA2iWC9w1v+YHfT2PXcLxYde9EuLVkIS4TM7Kb0N3wr/4+K4xWjVXuaJJLJoAbihNAZw0Y+2s1PswDUEpSG0jXeNXLs6XcQxYSEAu/pFdvHFeg2BfwVQoeEFlWyTJR29uti9/APaXMo8FSVAPPR5lKZLStJDM9hEfAPfUaHyic39MCgYAKQbwjNQw7Ejr+/cjQzxxkt5jskFyftfhPs2FP0/ghYB9OANHHnpQraQEWCYFZQ5WsVac2jdUM+NQL/a1t1e/Klt+HscPHKPsAwAQh1f9w/2YrH4ZwjQL0VRKYKs1HyzEcOZT7tzm4jQ2KHNEi5Q0dpzPK7WJivFHoZ6xVHIsh4wKBgAQq20mk9BKsLHvzyFXbA0WdgI6WyIbpvmwqaVegJcz26nEiiTTCA3/z64OcxunoXD6bvXJwJeBBPX73LIJg7dzdGLsh3AdcEJRF5S9ajEDaW7RFIM4/FzvwuPu2/mFY3QPjDmUfGb23H7+DIx6XCxjJatVaNT6lsEJ+wDUALZ8JAoGAO0YJSEziA7y0dXPK5azkJUMJ5yaN+zRDmoBnEggza34rQW0s16NnIR0EBzKGwbpNyePlProv4dQEaLF1kboKsSYvV2rW2ftLVdNqBHEUYFRC9ofPctCxwM1YU21TI2/k1squ+swApg2EHMev2+WKd+jpVPIbCIvJ3AjiAKZtiGQ=', - pubKey: 'CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDJwzJPar4nylKY71Mm5q2BOED8uPf1ILvIi15VwVZWqter6flnlii/RKEcBypPbFqJHHa56MvybgQgrFmHKwDjnJvq4jyOZfR+o/D/99Ft1p2FAEBjImSXAgNpK4YsbyV5r0Q1+Avcj++aWWlLu6enUrL9WGzeUkf0U5L6XwXEPRUQdEojAIQi241P1hyqXX5gKAZVGqcPtKb6p1db3fcXodkS1G6JR90TopJHCqTCECp3SB9c6LlG7KXU92sIHJBlhOEEzGkEI1pM1SWnNnW5VLEypU7P56ifzzp4QxPNiJeC+cmE5SrgR3cXP44iKOuNVRJwBpCh5oNYqECzgqJ9AgMBAAE=' -} - -exports.node2 = { - id: 'QmYJjAri5soV8RbeQcHaYYcTAYTET17QTvcoFMyKvRDTXe', - privKey: 'CAASpgkwggSiAgEAAoIBAQDt7YgUeBQsoN/lrgo690mB7yEh8G9iXhZiDecgZCLRRSl3v2cH9w4WjhoW9erfnVbdoTqkCK+se8uK01ySi/ubQQDPcrjacXTa6wAuRTbCG/0bUR9RxKtxZZBS1HaY7L923ulgGDTiVaRQ3JQqhzmQkaU0ikNcluSGaw0kmhXP6JmcL+wndKgW5VD9etcp2Qlk8uUFC/GAO90cOAuER3wnI3ocHGm9on9zyb97g4TDzIfjSaTW4Wanmx2yVbURQxmCba16X3LT9IMPqQaGOzq3+EewMLeCESbUm/uJaJLdqWrWRK4oNzxcMgmUkzav+s476HdA9CRo72am+g3Vdq+lAgMBAAECggEAcByKD6MZVoIjnlVo6qoVUA1+3kAuK/rLrz5/1wp4QYXGaW+eO+mVENm6v3D3UJESGnLbb+nL5Ymbunmn2EHvuBNkL1wOcJgfiPxM5ICmscaAeHu8N0plwpQp8m28yIheG8Qj0az2VmQmfhfCFVwMquuGHgC8hwdu/Uu6MLIObx1xjtaGbY9kk7nzAeXHeJ4RDeuNN0QrYuQVKwrIz1NtPNDR/cli298ZXJcm+HEhBCIHVIYpAq6BHSuiXVqPGEOYWYXo+yVhEtDJ8BmNqlN1Y1s6bnfu/tFkKUN6iQQ46vYnQEGTGR9lg7J/c6tqfRs9FcywWb9J1SX6HxPO8184zQKBgQD6vDYl20UT4ZtrzhFfMyV/1QUqFM/TdwNuiOsIewHBol9o7aOjrxrrbYVa1HOxETyBjmFsW+iIfOVl61SG2HcU4CG+O2s9WBo4JdRlOm4YQ8/83xO3YfbXzuTx8BMCyP/i1uPIZTKQFFAN0HiL96r4L60xHoWB7tQsbZiEbIO/2wKBgQDy7HnkgVeTld6o0+sT84FYRUotjDB00oUWiSeGtj0pFC4yIxhMhD8QjKiWoJyJItcoCsQ/EncuuwwRtuXi83793lJQR1DBYd+TSPg0M8J1pw97fUIPi/FU+jHtrsx7Vn/7Bk9voictsYVLAfbi68tYdsZpAaYOWYMY9NUfVuAmfwKBgCYZDwk1hgt9TkZVK2KRvPLthTldrC5veQAEoeHJ/vxTFbg105V9d9Op8odYnLOc8NqmrbrvRCfpAlo4JcHPhliPrdDf6m2Jw4IgjWNMO4pIU4QSyUYmBoHIGBWC6wCTVf47tKSwa7xkub0/nfF2km3foKtD/fk+NtMBXBlS+7ndAoGAJo6GIlCtN82X07AfJcGGjB4jUetoXYJ0gUkvruAKARUk5+xOFQcAg33v3EiNz+5pu/9JesFRjWc+2Sjwf/8p7t10ry1Ckg8Yz2XLj22PteDYQj91VsZdfaFgf1s5NXJbSdqMjSltkoEUqP0c1JOcaOQhRdVvJ+PpPPLPSPQfC70CgYBvJE1I06s7BEM1DOli3VyfNaJDI4k9W2dCJOU6Bh2MNmbdRjM3xnpOKH5SqRlCz/oI9pn4dxgbX6WPg331MD9CNYy2tt5KBQRrSuDj8p4jlzMIpX36hsyTTrzYU6WWSIPz6jXW8IexXKvXEmr8TVb78ZPiQfbG012cdUhAJniNgg==', - pubKey: 'CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDt7YgUeBQsoN/lrgo690mB7yEh8G9iXhZiDecgZCLRRSl3v2cH9w4WjhoW9erfnVbdoTqkCK+se8uK01ySi/ubQQDPcrjacXTa6wAuRTbCG/0bUR9RxKtxZZBS1HaY7L923ulgGDTiVaRQ3JQqhzmQkaU0ikNcluSGaw0kmhXP6JmcL+wndKgW5VD9etcp2Qlk8uUFC/GAO90cOAuER3wnI3ocHGm9on9zyb97g4TDzIfjSaTW4Wanmx2yVbURQxmCba16X3LT9IMPqQaGOzq3+EewMLeCESbUm/uJaJLdqWrWRK4oNzxcMgmUkzav+s476HdA9CRo72am+g3Vdq+lAgMBAAE=' -} - -exports.node3 = { - id: 'QmQWqGdndSpAkxfk8iyiJyz3XXGkrDNujvc8vEst3baubA', - privKey: 'CAASpwkwggSjAgEAAoIBAQDdnGp0X7Pix5dIawfyuffVryRDRS5JXdyjayKUkgikJLYoiijB5TakrFKhx1SDKpmVLxxqAGz8m5iA2cHwetIQXTZvdYx7XXxv332En3ji8TiGRUiEFM8KQ5WCJ5G7yw8R2pv/pYdnMrPd04QbtSCn0cFVCiiA2Zkl5KnwBo/lf+sVI/TEeiwmVD9nxi13qWgBTmCysqH8Ppyu8fq+bQgqRZSlalVDswyIhgWlepPkD0uYakJJhhOxY+2RlbNhGY0qjRyMTYou2uR/hfd6j8uR++WdB0v3+DYWG2Kc3sWa4BLYb5r4trvQGO1Iagnwuk3AVoi7PldsaInekzWEVljDAgMBAAECggEAXx0jE49/xXWkmJBXePYYSL5C8hxfIV4HtJvm251R2CFpjTy/AXk/Wq4bSRQkUaeXA1CVAWntXP3rFmJfurb8McnP80agZNJa9ikV1jYbzEt71yUlWosT0XPwV0xkYBVnAmKxUafZ1ZENYcfGi53RxjVgpP8XIzZBZOIfjcVDPVw9NAOzQmq4i3DJEz5xZAkaeSM8mn5ZFl1JMBUOgyOHB7d4BWd3zuLyvnn0/08HlsaSUl0mZa3f2Lm2NlsjOiNfMCJTOIT+xDEP9THm5n2cqieSjvtpAZzV4kcoD0rB8OsyHQlFAEXzkgELDr5dVXji0rrIdVz8stYAKGfi996OAQKBgQDuviV1sc+ClJQA59vqbBiKxWqcuCKMzvmL4Yk1e/AkQeRt+JX9kALWzBx65fFmHTj4Lus8AIQoiruPxa0thtqh/m3SlucWnrdaW410xbz3KqQWS7bx+0sFWZIEi4N+PESrIYhtVbFuRiabYgliqdSU9shxtXXnvfhjl+9quZltiwKBgQDtoUCKqrZbm0bmzLvpnKdNodg1lUHaKGgEvWgza2N1t3b/GE07iha2KO3hBDta3bdfIEEOagY8o13217D0VIGsYNKpiEGLEeNIjfcXBEqAKiTfa/sXUfTprpWBZQ/7ZS+eZIYtQjq14EHa7ifAby1v3yDrMIuxphz5JfKdXFgYqQKBgHr47FikPwu2tkmFJCyqgzWvnEufOQSoc7eOc1tePIKggiX2/mM+M4gqWJ0hJeeAM+D6YeZlKa2sUBItMxeZN7JrWGw5mEx5cl4TfFhipgP2LdDiLRiVZL4bte+rYQ67wm8XdatDkYIIlkhBBi6Q5dPZDcQsQNAedPvvvb2OXi4jAoGBAKp06FpP+L2fle2LYSRDlhNvDCvrpDA8mdkEkRGJb/AKKdb09LnH5WDH3VNy+KzGrHoVJfWUAmNPAOFHeYzabaZcUeEAd5utui7afytIjbSABrEpwRTKWneiH2aROzSnMdBZ5ZHjlz/N3Q+RlHxKg/piwTdUPHCzasch/HX6vsr5AoGAGvhCNPKyCwpu8Gg5GQdx0yN6ZPar9wieD345cLanDZWKkLRQbo4SfkfoS+PDfOLzDbWFdPRnWQ0qhdPm3D/N1YD/nudHqbeDlx0dj/6lEHmmPKFFO2kiNFEhn8DycNGbvWyVBKksacuRXav21+LvW+TatUkRMhi8fgRoypnbJjg=', - pubKey: 'CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDdnGp0X7Pix5dIawfyuffVryRDRS5JXdyjayKUkgikJLYoiijB5TakrFKhx1SDKpmVLxxqAGz8m5iA2cHwetIQXTZvdYx7XXxv332En3ji8TiGRUiEFM8KQ5WCJ5G7yw8R2pv/pYdnMrPd04QbtSCn0cFVCiiA2Zkl5KnwBo/lf+sVI/TEeiwmVD9nxi13qWgBTmCysqH8Ppyu8fq+bQgqRZSlalVDswyIhgWlepPkD0uYakJJhhOxY+2RlbNhGY0qjRyMTYou2uR/hfd6j8uR++WdB0v3+DYWG2Kc3sWa4BLYb5r4trvQGO1Iagnwuk3AVoi7PldsaInekzWEVljDAgMBAAE=' -} - -exports.node4 = { - id: 'QmQvM2mpqkjyXWbTHSUidUAWN26GgdMphTh9iGDdjgVXCy', - privKey: 'CAASqAkwggSkAgEAAoIBAQC6pg6LYWbY+49SOYdYap6RPqKqZxg80IXeo3hiTUbiGtTruxVYZpnz3UbernL9J9mwlXJGRUQJUKmXmi1yePTQiyclpH0KyPefaWLbpxJQdCBI1TPZpDWo2hutWSPqhKBU1QyH2FLKQPWLdxuIX1cNFPtIlSl5gCxN6oiDIwh7++kxNM1G+d5XgJX6iHLlLoNv3Wn6XYX+VtYdyZRFk8gYyT2BrISbxmsrSjSOodwUUzF8TYTjsqW6ksL2x0mrRm2cMM9evULktqwU+I8l9ulASDbFWBXUToXaZSL9M+Oq5JvZO0WIjPeYVpAgWladtayhdxg5dBv8aTbDaM5DZvyRAgMBAAECggEAR65YbZz1k6Vg0HI5kXI4/YzxicHYJBrtHqjnJdGJxHILjZCmzPFydJ5phkG29ZRlXRS381bMn0s0Jn3WsFzVoHWgjitSvl6aAsXFapgKR42hjHcc15vh47wH3xYZ3gobTRkZG96vRO+XnX0bvM7orqR9MM3gRMI9wZqt3LcKnhpiqSlyEZ3Zehu7ZZ8B+XcUw42H6ZTXgmg5mCFEjS/1rVt+EsdZl7Ll7jHigahPA6qMjyRiZB6T20qQ0FFYfmaNuRuuC6cWUXf8DOgnEjMB/Mi/Feoip9bTqNBrVYn2XeDxdMv5pDznNKXpalsMkZwx5FpNOMKnIMdQFyAGtkeQ9QKBgQD3rjTiulitpbbQBzF8VXeymtMJAbR1TAqNv2yXoowhL3JZaWICM7nXHjjsJa3UzJygbi8bO0KWrw7tY0nUbPy5SmHtNYhmUsEjiTjqEnNRrYN68tEKr0HlgX+9rArsjOcwucl2svFSfk+rTYDHU5neZkDDhu1QmnZm/pQI92Lo4wKBgQDA6wpMd53fmX9DhWegs3xelRStcqBTw1ucWVRyPgY1hO1cJ0oReYIXKEw9CHNLW0RHvnVM26kRnqCl+dTcg7dhLuqrckuyQyY1KcRYG1ryJnz3euucaSF2UCsZCHvFNV7Vz8dszUMUVCogWmroVP6HE/BoazUCNh25s/dNwE+i+wKBgEfa1WL1luaBzgCaJaQhk4FQY2sYgIcLEYDACTwQn0C9aBpCdXmYEhEzpmX0JHM5DTOJ48atsYrPrK/3/yJOoB8NUk2kGzc8SOYLWGSoB6aphRx1N2o3IBH6ONoJAH5R/nxnWehCz7oUBP74lCS/v0MDPUS8bzrUJQeKUd4sDxjrAoGBAIRO7rJA+1qF+J1DWi4ByxNHJXZLfh/UhPj23w628SU1dGDWZVsUvZ7KOXdGW2RcRLj7q5E5uXtnEoCillViVJtnRPSun7Gzkfm2Gn3ezQH0WZKVkA+mnpd5JgW2JsS69L6pEPnS0OWZT4b+3AFZgXL8vs2ucR2CJeLdxYdilHuPAoGBAPLCzBkAboXZZtvEWqzqtVNqdMrjLHihFrpg4TXSsk8+ZQZCVN+sRyTGTvBX8+Jvx4at6ClaSgT3eJ/412fEH6CHvrFXjUE9W9y6X0axxaT63y1OXmFiB/hU3vjLWZKZWSDGNS7St02fYri4tWmGtJDjYG1maLRhMSzcoj4fP1xz', - pubKey: 'CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6pg6LYWbY+49SOYdYap6RPqKqZxg80IXeo3hiTUbiGtTruxVYZpnz3UbernL9J9mwlXJGRUQJUKmXmi1yePTQiyclpH0KyPefaWLbpxJQdCBI1TPZpDWo2hutWSPqhKBU1QyH2FLKQPWLdxuIX1cNFPtIlSl5gCxN6oiDIwh7++kxNM1G+d5XgJX6iHLlLoNv3Wn6XYX+VtYdyZRFk8gYyT2BrISbxmsrSjSOodwUUzF8TYTjsqW6ksL2x0mrRm2cMM9evULktqwU+I8l9ulASDbFWBXUToXaZSL9M+Oq5JvZO0WIjPeYVpAgWladtayhdxg5dBv8aTbDaM5DZvyRAgMBAAE=' -} diff --git a/test/circuit/helpers/test-node.js b/test/circuit/helpers/test-node.js deleted file mode 100644 index bba56622..00000000 --- a/test/circuit/helpers/test-node.js +++ /dev/null @@ -1,22 +0,0 @@ -'use strict' - -const Libp2p = require('../../../src') -const secio = require('libp2p-secio') - -class TestNode extends Libp2p { - constructor (peerInfo, transports, muxer, options) { - options = options || {} - - const modules = { - transport: transports, - connection: { - muxer: [muxer], - crypto: options.isCrypto ? [secio] : null - }, - discovery: [] - } - super(modules, peerInfo, null, options) - } -} - -module.exports = TestNode diff --git a/test/circuit/helpers/utils.js b/test/circuit/helpers/utils.js deleted file mode 100644 index 30482de5..00000000 --- a/test/circuit/helpers/utils.js +++ /dev/null @@ -1,78 +0,0 @@ -'use strict' - -const TestNode = require('./test-node') -const PeerInfo = require('peer-info') -const PeerId = require('peer-id') -const eachAsync = require('async/each') - -exports.createNodes = function createNodes (configNodes, callback) { - const nodes = {} - eachAsync(Object.keys(configNodes), (key, cb1) => { - const config = configNodes[key] - - const setup = (err, peer) => { - if (err) { - callback(err) - } - - eachAsync(config.addrs, (addr, cb2) => { - peer.multiaddrs.add(addr) - cb2() - }, (err) => { - if (err) { - return callback(err) - } - - nodes[key] = new TestNode(peer, config.transports, config.muxer, config.config) - cb1() - }) - } - - if (config.id) { - PeerId.createFromJSON(config.id, (err, peerId) => { - if (err) return callback(err) - PeerInfo.create(peerId, setup) - }) - } else { - PeerInfo.create(setup) - } - }, (err) => { - if (err) { - return callback(err) - } - - startNodes(nodes, (err) => { - if (err) { - callback(err) - } - - callback(null, nodes) - }) - }) -} - -function startNodes (nodes, callback) { - eachAsync(Object.keys(nodes), - (key, cb) => { - nodes[key].start(cb) - }, - (err) => { - if (err) { - return callback(err) - } - callback(null) - }) -} - -exports.stopNodes = function stopNodes (nodes, callback) { - eachAsync(Object.keys(nodes), - (key, cb) => { - nodes[key].stop(cb) - }, - (err) => { - if (err) { - return callback(err) - } - callback() - }) -} diff --git a/test/circuit/hop.spec.js b/test/circuit/hop.spec.js deleted file mode 100644 index 0b30809e..00000000 --- a/test/circuit/hop.spec.js +++ /dev/null @@ -1,433 +0,0 @@ -/* eslint-env mocha */ -/* eslint max-nested-callbacks: ["error", 5] */ -'use strict' - -const Hop = require('../../src/circuit/circuit/hop') -const nodes = require('./fixtures/nodes') -const Connection = require('interface-connection').Connection -const handshake = require('pull-handshake') -const waterfall = require('async/waterfall') -const PeerInfo = require('peer-info') -const PeerId = require('peer-id') -const multiaddr = require('multiaddr') -const pull = require('pull-stream/pull') -const values = require('pull-stream/sources/values') -const collect = require('pull-stream/sinks/collect') -const lp = require('pull-length-prefixed') -const proto = require('../../src/circuit/protocol') -const StreamHandler = require('../../src/circuit/circuit/stream-handler') - -const sinon = require('sinon') -const chai = require('chai') -const dirtyChai = require('dirty-chai') -const expect = chai.expect -chai.use(dirtyChai) - -describe('relay', () => { - describe('.handle', () => { - let relay - let swarm - let fromConn - let stream - let shake - - beforeEach((done) => { - stream = handshake({ timeout: 1000 * 60 }) - shake = stream.handshake - fromConn = new Connection(stream) - const peerInfo = new PeerInfo(PeerId.createFromB58String('QmQWqGdndSpAkxfk8iyiJyz3XXGkrDNujvc8vEst3baubA')) - fromConn.setPeerInfo(peerInfo) - - const peers = { - QmSswe1dCFRepmhjAMR5VfHeokGLcvVggkuDJm7RMfJSrE: - new PeerInfo(PeerId.createFromB58String('QmSswe1dCFRepmhjAMR5VfHeokGLcvVggkuDJm7RMfJSrE')), - QmQWqGdndSpAkxfk8iyiJyz3XXGkrDNujvc8vEst3baubA: - new PeerInfo(PeerId.createFromB58String('QmQWqGdndSpAkxfk8iyiJyz3XXGkrDNujvc8vEst3baubA')), - QmQvM2mpqkjyXWbTHSUidUAWN26GgdMphTh9iGDdjgVXCy: - new PeerInfo(PeerId.createFromB58String('QmQvM2mpqkjyXWbTHSUidUAWN26GgdMphTh9iGDdjgVXCy')) - } - - Object.keys(peers).forEach((key) => { peers[key]._connectedMultiaddr = true }) // make it truthy - - waterfall([ - (cb) => PeerId.createFromJSON(nodes.node4, cb), - (peerId, cb) => PeerInfo.create(peerId, cb), - (peer, cb) => { - peer.multiaddrs.add('/p2p-circuit/ipfs/QmSswe1dCFRepmhjAMR5VfHeokGLcvVggkuDJm7RMfJSrE') - swarm = { - _peerInfo: peer, - conns: { - QmSswe1dCFRepmhjAMR5VfHeokGLcvVggkuDJm7RMfJSrE: new Connection(), - QmQWqGdndSpAkxfk8iyiJyz3XXGkrDNujvc8vEst3baubA: new Connection(), - QmQvM2mpqkjyXWbTHSUidUAWN26GgdMphTh9iGDdjgVXCy: new Connection() - }, - _peerBook: { - get: (peer) => { - if (!peers[peer]) { - throw new Error() - } - - return peers[peer] - } - } - } - - cb() - } - ], () => { - relay = new Hop(swarm, { enabled: true }) - relay._circuit = sinon.stub() - relay._circuit.callsArgWith(2, null, new Connection()) - done() - }) - }) - - afterEach(() => { - relay._circuit.reset() - }) - - it('should handle a valid circuit request', (done) => { - const relayMsg = { - type: proto.CircuitRelay.Type.HOP, - srcPeer: { - id: PeerId.createFromB58String('QmSswe1dCFRepmhjAMR5VfHeokGLcvVggkuDJm7RMfJSrE').id, - addrs: [multiaddr('/ipfs/QmSswe1dCFRepmhjAMR5VfHeokGLcvVggkuDJm7RMfJSrE').buffer] - }, - dstPeer: { - id: PeerId.createFromB58String('QmQWqGdndSpAkxfk8iyiJyz3XXGkrDNujvc8vEst3baubA').id, - addrs: [multiaddr('/ipfs/QmQWqGdndSpAkxfk8iyiJyz3XXGkrDNujvc8vEst3baubA').buffer] - } - } - - relay.on('circuit:success', () => { - expect(relay._circuit.calledWith(sinon.match.any, relayMsg)).to.be.ok() - done() - }) - - relay.handle(relayMsg, new StreamHandler(fromConn)) - }) - - it('should handle a request to passive circuit', (done) => { - const relayMsg = { - type: proto.CircuitRelay.Type.HOP, - srcPeer: { - id: PeerId.createFromB58String('QmQWqGdndSpAkxfk8iyiJyz3XXGkrDNujvc8vEst3baubA').id, - addrs: [multiaddr('/ipfs/QmQWqGdndSpAkxfk8iyiJyz3XXGkrDNujvc8vEst3baubA').buffer] - }, - dstPeer: { - id: PeerId.createFromB58String('QmYJjAri5soV8RbeQcHaYYcTAYTET17QTvcoFMyKvRDTXe').id, - addrs: [multiaddr('/ipfs/QmYJjAri5soV8RbeQcHaYYcTAYTET17QTvcoFMyKvRDTXe').buffer] - } - } - - relay.active = false - lp.decodeFromReader( - shake, - (err, msg) => { - expect(err).to.not.exist() - - const response = proto.CircuitRelay.decode(msg) - expect(response.code).to.equal(proto.CircuitRelay.Status.HOP_NO_CONN_TO_DST) - expect(response.type).to.equal(proto.CircuitRelay.Type.STATUS) - done() - }) - - relay.handle(relayMsg, new StreamHandler(fromConn)) - }) - - it('should handle a request to active circuit', (done) => { - const relayMsg = { - type: proto.CircuitRelay.Type.HOP, - srcPeer: { - id: PeerId.createFromB58String('QmQWqGdndSpAkxfk8iyiJyz3XXGkrDNujvc8vEst3baubA').id, - addrs: [multiaddr('/ipfs/QmQWqGdndSpAkxfk8iyiJyz3XXGkrDNujvc8vEst3baubA').buffer] - }, - dstPeer: { - id: PeerId.createFromB58String('QmYJjAri5soV8RbeQcHaYYcTAYTET17QTvcoFMyKvRDTXe').id, - addrs: [multiaddr('/ipfs/QmYJjAri5soV8RbeQcHaYYcTAYTET17QTvcoFMyKvRDTXe').buffer] - } - } - - relay.active = true - relay.on('circuit:success', () => { - expect(relay._circuit.calledWith(sinon.match.any, relayMsg)).to.be.ok() - done() - }) - - relay.on('circuit:error', (err) => { - done(err) - }) - - relay.handle(relayMsg, new StreamHandler(fromConn)) - }) - - it('not dial to self', (done) => { - const relayMsg = { - type: proto.CircuitRelay.Type.HOP, - srcPeer: { - id: PeerId.createFromB58String('QmSswe1dCFRepmhjAMR5VfHeokGLcvVggkuDJm7RMfJSrE').id, - addrs: [multiaddr('/ipfs/QmSswe1dCFRepmhjAMR5VfHeokGLcvVggkuDJm7RMfJSrE').buffer] - }, - dstPeer: { - id: PeerId.createFromB58String('QmSswe1dCFRepmhjAMR5VfHeokGLcvVggkuDJm7RMfJSrE').id, - addrs: [multiaddr('/ipfs/QmSswe1dCFRepmhjAMR5VfHeokGLcvVggkuDJm7RMfJSrE').buffer] - } - } - - lp.decodeFromReader( - shake, - (err, msg) => { - expect(err).to.not.exist() - - const response = proto.CircuitRelay.decode(msg) - expect(response.code).to.equal(proto.CircuitRelay.Status.HOP_CANT_RELAY_TO_SELF) - expect(response.type).to.equal(proto.CircuitRelay.Type.STATUS) - done() - }) - - relay.handle(relayMsg, new StreamHandler(fromConn)) - }) - - it('fail on invalid src address', (done) => { - const relayMsg = { - type: proto.CircuitRelay.Type.HOP, - srcPeer: { - id: 'sdfkjsdnfkjdsb', - addrs: ['sdfkjsdnfkjdsb'] - }, - dstPeer: { - id: PeerId.createFromB58String('QmQWqGdndSpAkxfk8iyiJyz3XXGkrDNujvc8vEst3baubA').id, - addrs: [multiaddr('/ipfs/QmQWqGdndSpAkxfk8iyiJyz3XXGkrDNujvc8vEst3baubA').buffer] - } - } - - lp.decodeFromReader( - shake, - (err, msg) => { - expect(err).to.not.exist() - - const response = proto.CircuitRelay.decode(msg) - expect(response.code).to.equal(proto.CircuitRelay.Status.HOP_SRC_MULTIADDR_INVALID) - expect(response.type).to.equal(proto.CircuitRelay.Type.STATUS) - done() - }) - - relay.handle(relayMsg, new StreamHandler(fromConn)) - }) - - it('fail on invalid dst address', (done) => { - const relayMsg = { - type: proto.CircuitRelay.Type.HOP, - srcPeer: { - id: PeerId.createFromB58String('QmQWqGdndSpAkxfk8iyiJyz3XXGkrDNujvc8vEst3baubA').id, - addrs: [multiaddr('/ipfs/QmQWqGdndSpAkxfk8iyiJyz3XXGkrDNujvc8vEst3baubA').buffer] - }, - dstPeer: { - id: PeerId.createFromB58String('QmSswe1dCFRepmhjAMR5VfHeokGLcvVggkuDJm7RMfJSrE').id, - addrs: ['sdfkjsdnfkjdsb'] - } - } - - lp.decodeFromReader( - shake, - (err, msg) => { - expect(err).to.not.exist() - - const response = proto.CircuitRelay.decode(msg) - expect(response.code).to.equal(proto.CircuitRelay.Status.HOP_DST_MULTIADDR_INVALID) - expect(response.type).to.equal(proto.CircuitRelay.Type.STATUS) - done() - }) - - relay.handle(relayMsg, new StreamHandler(fromConn)) - }) - }) - - describe('._circuit', () => { - let relay - let swarm - let srcConn - let dstConn - let srcStream - let dstStream - let srcShake - let dstShake - - before((done) => { - srcStream = handshake({ timeout: 1000 * 60 }) - srcShake = srcStream.handshake - srcConn = new Connection(srcStream) - dstStream = handshake({ timeout: 1000 * 60 }) - dstShake = dstStream.handshake - dstConn = new Connection(dstStream) - const peerInfo = new PeerInfo(PeerId.createFromB58String('QmQWqGdndSpAkxfk8iyiJyz3XXGkrDNujvc8vEst3baubA')) - srcConn.setPeerInfo(peerInfo) - - const peers = { - QmSswe1dCFRepmhjAMR5VfHeokGLcvVggkuDJm7RMfJSrE: - new PeerInfo(PeerId.createFromB58String('QmSswe1dCFRepmhjAMR5VfHeokGLcvVggkuDJm7RMfJSrE')), - QmQWqGdndSpAkxfk8iyiJyz3XXGkrDNujvc8vEst3baubA: - new PeerInfo(PeerId.createFromB58String('QmQWqGdndSpAkxfk8iyiJyz3XXGkrDNujvc8vEst3baubA')), - QmQvM2mpqkjyXWbTHSUidUAWN26GgdMphTh9iGDdjgVXCy: - new PeerInfo(PeerId.createFromB58String('QmQvM2mpqkjyXWbTHSUidUAWN26GgdMphTh9iGDdjgVXCy')) - } - - Object.keys(peers).forEach((key) => { peers[key]._connectedMultiaddr = true }) // make it truthy - - waterfall([ - (cb) => PeerId.createFromJSON(nodes.node4, cb), - (peerId, cb) => PeerInfo.create(peerId, cb), - (peer, cb) => { - peer.multiaddrs.add('/p2p-circuit/ipfs/QmSswe1dCFRepmhjAMR5VfHeokGLcvVggkuDJm7RMfJSrE') - swarm = { - _peerInfo: peer, - conns: { - QmSswe1dCFRepmhjAMR5VfHeokGLcvVggkuDJm7RMfJSrE: new Connection(), - QmQWqGdndSpAkxfk8iyiJyz3XXGkrDNujvc8vEst3baubA: new Connection(), - QmQvM2mpqkjyXWbTHSUidUAWN26GgdMphTh9iGDdjgVXCy: new Connection() - }, - _peerBook: { - get: (peer) => { - if (!peers[peer]) { - throw new Error() - } - - return peers[peer] - } - } - } - - cb() - } - ], () => { - relay = new Hop(swarm, { enabled: true }) - relay._dialPeer = sinon.stub() - relay._dialPeer.callsArgWith(1, null, dstConn) - - done() - }) - }) - - after(() => relay._dialPeer.reset()) - - describe('should correctly dial destination node', () => { - const msg = { - type: proto.CircuitRelay.Type.STOP, - srcPeer: { - id: Buffer.from('QmQWqGdndSpAkxfk8iyiJyz3XXGkrDNujvc8vEst3baubA'), - addrs: [Buffer.from('dsfsdfsdf')] - }, - dstPeer: { - id: Buffer.from('QmSswe1dCFRepmhjAMR5VfHeokGLcvVggkuDJm7RMfJSrE'), - addrs: [Buffer.from('sdflksdfndsklfnlkdf')] - } - } - - before(() => { - relay._circuit( - new StreamHandler(srcConn), - msg, - (err) => { - expect(err).to.not.exist() - }) - }) - - it('should respond with SUCCESS to source node', (done) => { - lp.decodeFromReader( - srcShake, - (err, msg) => { - expect(err).to.not.exist() - - const response = proto.CircuitRelay.decode(msg) - expect(response.type).to.equal(proto.CircuitRelay.Type.STATUS) - expect(response.code).to.equal(proto.CircuitRelay.Status.SUCCESS) - done() - }) - }) - - it('should send STOP message to destination node', (done) => { - lp.decodeFromReader( - dstShake, - (err, _msg) => { - expect(err).to.not.exist() - - const response = proto.CircuitRelay.decode(_msg) - expect(response.type).to.deep.equal(msg.type) - expect(response.srcPeer).to.deep.equal(msg.srcPeer) - expect(response.dstPeer).to.deep.equal(msg.dstPeer) - done() - }) - }) - - it('should create circuit', (done) => { - pull( - values([proto.CircuitRelay.encode({ - type: proto.CircuitRelay.Type.STATUS, - code: proto.CircuitRelay.Status.SUCCESS - })]), - lp.encode(), - collect((err, encoded) => { - expect(err).to.not.exist() - - encoded.forEach((e) => dstShake.write(e)) - pull( - values([Buffer.from('hello')]), - lp.encode(), - collect((err, encoded) => { - expect(err).to.not.exist() - - encoded.forEach((e) => srcShake.write(e)) - lp.decodeFromReader( - dstShake, - (err, _msg) => { - expect(err).to.not.exist() - expect(_msg.toString()).to.equal('hello') - - done() - }) - }) - ) - }) - ) - }) - }) - - describe('should fail creating circuit', () => { - const msg = { - type: proto.CircuitRelay.Type.STOP, - srcPeer: { - id: Buffer.from('QmQWqGdndSpAkxfk8iyiJyz3XXGkrDNujvc8vEst3baubA'), - addrs: [Buffer.from('dsfsdfsdf')] - }, - dstPeer: { - id: Buffer.from('QmSswe1dCFRepmhjAMR5VfHeokGLcvVggkuDJm7RMfJSrE'), - addrs: [Buffer.from('sdflksdfndsklfnlkdf')] - } - } - - it('should not create circuit', (done) => { - relay._circuit( - new StreamHandler(srcConn), - msg, - (err) => { - expect(err).to.exist() - expect(err).to.match(/Unable to create circuit!/) - done() - }) - - pull( - values([proto.CircuitRelay.encode({ - type: proto.CircuitRelay.Type.STATUS, - code: proto.CircuitRelay.Status.STOP_RELAY_REFUSED - })]), - lp.encode(), - collect((err, encoded) => { - expect(err).to.not.exist() - - encoded.forEach((e) => dstShake.write(e)) - }) - ) - }) - }) - }) -}) diff --git a/test/circuit/listener.spec.js b/test/circuit/listener.spec.js deleted file mode 100644 index 2e821380..00000000 --- a/test/circuit/listener.spec.js +++ /dev/null @@ -1,292 +0,0 @@ -/* eslint-env mocha */ -'use strict' - -const Listener = require('../../src/circuit/listener') -const nodes = require('./fixtures/nodes') -const waterfall = require('async/waterfall') -const PeerInfo = require('peer-info') -const PeerId = require('peer-id') -const multiaddr = require('multiaddr') -const handshake = require('pull-handshake') -const Connection = require('interface-connection').Connection -const proto = require('../../src/circuit/protocol') -const lp = require('pull-length-prefixed') -const pull = require('pull-stream/pull') -const values = require('pull-stream/sources/values') -const collect = require('pull-stream/sinks/collect') -const multicodec = require('../../src/circuit/multicodec') - -const chai = require('chai') -const dirtyChai = require('dirty-chai') -const expect = chai.expect -chai.use(dirtyChai) -const sinon = require('sinon') - -describe('listener', function () { - describe('listen', function () { - let swarm = null - let handlerSpy = null - let listener = null - let stream = null - let shake = null - let conn = null - - beforeEach(function (done) { - stream = handshake({ timeout: 1000 * 60 }) - shake = stream.handshake - conn = new Connection(stream) - conn.setPeerInfo(new PeerInfo(PeerId - .createFromB58String('QmSswe1dCFRepmhjAMR5VfHeokGLcvVggkuDJm7RMfJSrE'))) - - waterfall([ - (cb) => PeerId.createFromJSON(nodes.node4, cb), - (peerId, cb) => PeerInfo.create(peerId, cb), - (peer, cb) => { - swarm = { - _peerInfo: peer, - handle: sinon.spy((proto, h) => { - handlerSpy = sinon.spy(h) - }), - conns: { - QmSswe1dCFRepmhjAMR5VfHeokGLcvVggkuDJm7RMfJSrE: new Connection() - } - } - - listener = Listener(swarm, {}, () => {}) - listener.listen() - cb() - } - ], done) - }) - - afterEach(() => { - listener = null - }) - - it('should handle HOP', function (done) { - handlerSpy(multicodec.relay, conn) - - const relayMsg = { - type: proto.CircuitRelay.Type.HOP, - srcPeer: { - id: 'QmSswe1dCFRepmhjAMR5VfHeokGLcvVggkuDJm7RMfJSrE', - addrs: ['/ipfs/QmSswe1dCFRepmhjAMR5VfHeokGLcvVggkuDJm7RMfJSrE'] - }, - dstPeer: { - id: 'QmQvM2mpqkjyXWbTHSUidUAWN26GgdMphTh9iGDdjgVXCy', - addrs: ['/ipfs/QmQvM2mpqkjyXWbTHSUidUAWN26GgdMphTh9iGDdjgVXCy'] - } - } - - listener.hopHandler.handle = (message, conn) => { - expect(message.type).to.equal(proto.CircuitRelay.Type.HOP) - - expect(message.srcPeer.id.toString()).to.equal(relayMsg.srcPeer.id) - expect(message.srcPeer.addrs[0].toString()).to.equal(relayMsg.srcPeer.addrs[0]) - - expect(message.dstPeer.id.toString()).to.equal(relayMsg.dstPeer.id) - expect(message.dstPeer.addrs[0].toString()).to.equal(relayMsg.dstPeer.addrs[0]) - - done() - } - - pull( - values([proto.CircuitRelay.encode(relayMsg)]), - lp.encode(), - collect((err, encoded) => { - expect(err).to.not.exist() - encoded.forEach((e) => shake.write(e)) - }) - ) - }) - - it('should handle STOP', function (done) { - handlerSpy(multicodec.relay, conn) - - const relayMsg = { - type: proto.CircuitRelay.Type.STOP, - srcPeer: { - id: 'QmSswe1dCFRepmhjAMR5VfHeokGLcvVggkuDJm7RMfJSrE', - addrs: ['/ipfs/QmSswe1dCFRepmhjAMR5VfHeokGLcvVggkuDJm7RMfJSrE'] - }, - dstPeer: { - id: 'QmQvM2mpqkjyXWbTHSUidUAWN26GgdMphTh9iGDdjgVXCy', - addrs: ['/ipfs/QmQvM2mpqkjyXWbTHSUidUAWN26GgdMphTh9iGDdjgVXCy'] - } - } - - listener.stopHandler.handle = (message, conn) => { - expect(message.type).to.equal(proto.CircuitRelay.Type.STOP) - - expect(message.srcPeer.id.toString()).to.equal(relayMsg.srcPeer.id) - expect(message.srcPeer.addrs[0].toString()).to.equal(relayMsg.srcPeer.addrs[0]) - - expect(message.dstPeer.id.toString()).to.equal(relayMsg.dstPeer.id) - expect(message.dstPeer.addrs[0].toString()).to.equal(relayMsg.dstPeer.addrs[0]) - - done() - } - - pull( - values([proto.CircuitRelay.encode(relayMsg)]), - lp.encode(), - collect((err, encoded) => { - expect(err).to.not.exist() - encoded.forEach((e) => shake.write(e)) - }) - ) - }) - - it('should emit \'connection\'', function (done) { - handlerSpy(multicodec.relay, conn) - - const relayMsg = { - type: proto.CircuitRelay.Type.STOP, - srcPeer: { - id: 'QmSswe1dCFRepmhjAMR5VfHeokGLcvVggkuDJm7RMfJSrE', - addrs: ['/ipfs/QmSswe1dCFRepmhjAMR5VfHeokGLcvVggkuDJm7RMfJSrE'] - }, - dstPeer: { - id: 'QmQvM2mpqkjyXWbTHSUidUAWN26GgdMphTh9iGDdjgVXCy', - addrs: ['/ipfs/QmQvM2mpqkjyXWbTHSUidUAWN26GgdMphTh9iGDdjgVXCy'] - } - } - - listener.stopHandler.handle = (message, sh) => { - const newConn = new Connection(sh.rest()) - listener.stopHandler.emit('connection', newConn) - } - - listener.on('connection', (conn) => { - expect(conn).to.be.instanceof(Connection) - done() - }) - - pull( - values([proto.CircuitRelay.encode(relayMsg)]), - lp.encode(), - collect((err, encoded) => { - expect(err).to.not.exist() - encoded.forEach((e) => shake.write(e)) - }) - ) - }) - - it('should handle CAN_HOP', function (done) { - handlerSpy(multicodec.relay, conn) - - const relayMsg = { - type: proto.CircuitRelay.Type.CAN_HOP, - srcPeer: { - id: 'QmSswe1dCFRepmhjAMR5VfHeokGLcvVggkuDJm7RMfJSrE', - addrs: ['/ipfs/QmSswe1dCFRepmhjAMR5VfHeokGLcvVggkuDJm7RMfJSrE'] - }, - dstPeer: { - id: 'QmQvM2mpqkjyXWbTHSUidUAWN26GgdMphTh9iGDdjgVXCy', - addrs: ['/ipfs/QmQvM2mpqkjyXWbTHSUidUAWN26GgdMphTh9iGDdjgVXCy'] - } - } - - listener.hopHandler.handle = (message, conn) => { - expect(message.type).to.equal(proto.CircuitRelay.Type.CAN_HOP) - - expect(message.srcPeer.id.toString()).to.equal(relayMsg.srcPeer.id) - expect(message.srcPeer.addrs[0].toString()).to.equal(relayMsg.srcPeer.addrs[0]) - - expect(message.dstPeer.id.toString()).to.equal(relayMsg.dstPeer.id) - expect(message.dstPeer.addrs[0].toString()).to.equal(relayMsg.dstPeer.addrs[0]) - - done() - } - - pull( - values([proto.CircuitRelay.encode(relayMsg)]), - lp.encode(), - collect((err, encoded) => { - expect(err).to.not.exist() - encoded.forEach((e) => shake.write(e)) - }) - ) - }) - - it('should handle invalid message correctly', function (done) { - handlerSpy(multicodec.relay, conn) - - const relayMsg = { - type: 100000, - srcPeer: { - id: Buffer.from('QmSswe1dCFRepmhjAMR5VfHeokGLcvVggkuDJm7RMfJSrE'), - addrs: [multiaddr('/ipfs/QmSswe1dCFRepmhjAMR5VfHeokGLcvVggkuDJm7RMfJSrE').buffer] - }, - dstPeer: { - id: Buffer.from('QmQvM2mpqkjyXWbTHSUidUAWN26GgdMphTh9iGDdjgVXCy'), - addrs: [multiaddr('/ipfs/QmQvM2mpqkjyXWbTHSUidUAWN26GgdMphTh9iGDdjgVXCy').buffer] - } - } - - pull( - values([Buffer.from([relayMsg])]), - lp.encode(), - collect((err, encoded) => { - expect(err).to.not.exist() - encoded.forEach((e) => shake.write(e)) - }), - lp.decodeFromReader(shake, { maxLength: this.maxLength }, (err, msg) => { - expect(err).to.not.exist() - expect(proto.CircuitRelay.decode(msg).type).to.equal(proto.CircuitRelay.Type.STATUS) - expect(proto.CircuitRelay.decode(msg).code).to.equal(proto.CircuitRelay.Status.MALFORMED_MESSAGE) - done() - }) - ) - }) - }) - - describe('getAddrs', function () { - let swarm = null - let listener = null - let peerInfo = null - - beforeEach(function (done) { - waterfall([ - (cb) => PeerId.createFromJSON(nodes.node4, cb), - (peerId, cb) => PeerInfo.create(peerId, cb), - (peer, cb) => { - swarm = { - _peerInfo: peer - } - - peerInfo = peer - listener = Listener(swarm, {}, () => {}) - cb() - } - ], done) - }) - - afterEach(() => { - peerInfo = null - }) - - it('should return correct addrs', function () { - peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/4002') - peerInfo.multiaddrs.add('/ip4/127.0.0.1/tcp/4003/ws') - - listener.getAddrs((err, addrs) => { - expect(err).to.not.exist() - expect(addrs).to.deep.equal([ - multiaddr('/p2p-circuit/ip4/0.0.0.0/tcp/4002/ipfs/QmQvM2mpqkjyXWbTHSUidUAWN26GgdMphTh9iGDdjgVXCy'), - multiaddr('/p2p-circuit/ip4/127.0.0.1/tcp/4003/ws/ipfs/QmQvM2mpqkjyXWbTHSUidUAWN26GgdMphTh9iGDdjgVXCy')]) - }) - }) - - it('don\'t return default addrs in an explicit p2p-circuit addres', function () { - peerInfo.multiaddrs.add('/ip4/127.0.0.1/tcp/4003/ws') - peerInfo.multiaddrs.add('/p2p-circuit/ip4/0.0.0.0/tcp/4002') - listener.getAddrs((err, addrs) => { - expect(err).to.not.exist() - expect(addrs[0] - .toString()) - .to.equal('/p2p-circuit/ip4/0.0.0.0/tcp/4002/ipfs/QmQvM2mpqkjyXWbTHSUidUAWN26GgdMphTh9iGDdjgVXCy') - }) - }) - }) -}) diff --git a/test/circuit/proto.spec.js b/test/circuit/proto.spec.js deleted file mode 100644 index bb3a6671..00000000 --- a/test/circuit/proto.spec.js +++ /dev/null @@ -1,50 +0,0 @@ -/* eslint-env mocha */ -'use strict' - -const chai = require('chai') -const dirtyChai = require('dirty-chai') -const expect = chai.expect -chai.use(dirtyChai) - -const multiaddr = require('multiaddr') - -const proto = require('../../src/circuit/protocol') - -describe('protocol', function () { - let msgObject = null - let message = null - - before(() => { - msgObject = { - type: proto.CircuitRelay.Type.HOP, - srcPeer: { - id: Buffer.from('QmSource'), - addrs: [ - multiaddr('/p2p-circuit/ipfs/QmSource').buffer, - multiaddr('/p2p-circuit/ip4/0.0.0.0/tcp/9000/ipfs/QmSource').buffer, - multiaddr('/ip4/0.0.0.0/tcp/9000/ipfs/QmSource').buffer - ] - }, - dstPeer: { - id: Buffer.from('QmDest'), - addrs: [ - multiaddr('/p2p-circuit/ipfs/QmDest').buffer, - multiaddr('/p2p-circuit/ip4/1.1.1.1/tcp/9000/ipfs/QmDest').buffer, - multiaddr('/ip4/1.1.1.1/tcp/9000/ipfs/QmDest').buffer - ] - } - } - - const buff = proto.CircuitRelay.encode(msgObject) - message = proto.CircuitRelay.decode(buff) - }) - - it('should source and dest', () => { - expect(message.srcPeer).to.deep.equal(msgObject.srcPeer) - expect(message.dstPeer).to.deep.equal(msgObject.dstPeer) - }) - - it('should encode message', () => { - expect(message.message).to.deep.equal(msgObject.message) - }) -}) diff --git a/test/circuit/stop.spec.js b/test/circuit/stop.spec.js deleted file mode 100644 index 55a34817..00000000 --- a/test/circuit/stop.spec.js +++ /dev/null @@ -1,85 +0,0 @@ -/* eslint-env mocha */ -'use strict' - -const Stop = require('../../src/circuit/circuit/stop') -const nodes = require('./fixtures/nodes') -const Connection = require('interface-connection').Connection -const handshake = require('pull-handshake') -const waterfall = require('async/waterfall') -const PeerInfo = require('peer-info') -const PeerId = require('peer-id') -const StreamHandler = require('../../src/circuit/circuit/stream-handler') -const proto = require('../../src/circuit/protocol') - -const chai = require('chai') -const dirtyChai = require('dirty-chai') -const expect = chai.expect -chai.use(dirtyChai) - -describe('stop', function () { - describe('handle relayed connections', function () { - let stopHandler - - let swarm - let conn - let stream - - beforeEach(function (done) { - stream = handshake({ timeout: 1000 * 60 }) - conn = new Connection(stream) - const peerId = PeerId.createFromB58String('QmSswe1dCFRepmhjAMR5VfHeokGLcvVggkuDJm7RMfJSrE') - conn.setPeerInfo(new PeerInfo(peerId)) - - waterfall([ - (cb) => PeerId.createFromJSON(nodes.node4, cb), - (peerId, cb) => PeerInfo.create(peerId, cb), - (peer, cb) => { - peer.multiaddrs.add('/p2p-circuit/ipfs/QmSswe1dCFRepmhjAMR5VfHeokGLcvVggkuDJm7RMfJSrE') - swarm = { - _peerInfo: peer, - conns: { - QmSswe1dCFRepmhjAMR5VfHeokGLcvVggkuDJm7RMfJSrE: new Connection() - } - } - - stopHandler = new Stop(swarm) - cb() - } - ], done) - }) - - it('handle request with a valid multiaddr', function (done) { - stopHandler.handle({ - type: proto.CircuitRelay.Type.STOP, - srcPeer: { - id: 'QmSswe1dCFRepmhjAMR5VfHeokGLcvVggkuDJm7RMfJSrE', - addrs: ['/ipfs/QmSswe1dCFRepmhjAMR5VfHeokGLcvVggkuDJm7RMfJSrE'] - }, - dstPeer: { - id: 'QmQvM2mpqkjyXWbTHSUidUAWN26GgdMphTh9iGDdjgVXCy', - addrs: ['/ipfs/QmQvM2mpqkjyXWbTHSUidUAWN26GgdMphTh9iGDdjgVXCy'] - } - }, new StreamHandler(conn), (conn) => { // multistream handler doesn't expect errors... - expect(conn).to.be.instanceOf(Connection) - done() - }) - }) - - it('handle request with invalid multiaddr', function (done) { - stopHandler.handle({ - type: proto.CircuitRelay.Type.STOP, - srcPeer: { - id: 'QmSswe1dCFRepmhjAMR5VfHeokGLcvVggkuDJm7RMfJSrE', - addrs: ['dsfsdfsdf'] - }, - dstPeer: { - id: 'QmQvM2mpqkjyXWbTHSUidUAWN26GgdMphTh9iGDdjgVXCy', - addrs: ['sdflksdfndsklfnlkdf'] - } - }, new StreamHandler(conn), (conn) => { - expect(conn).to.not.exist() - done() - }) - }) - }) -}) diff --git a/test/config.spec.js b/test/config.spec.js deleted file mode 100644 index 146f2e3d..00000000 --- a/test/config.spec.js +++ /dev/null @@ -1,358 +0,0 @@ -/* eslint-env mocha */ -'use strict' - -const chai = require('chai') -chai.use(require('dirty-chai')) -const expect = chai.expect -const PeerInfo = require('peer-info') -const PeerId = require('peer-id') -const waterfall = require('async/waterfall') -const WS = require('libp2p-websockets') -const Bootstrap = require('libp2p-bootstrap') -const DelegatedPeerRouter = require('libp2p-delegated-peer-routing') -const DelegatedContentRouter = require('libp2p-delegated-content-routing') -const DHT = require('libp2p-kad-dht') - -const validateConfig = require('../src/config').validate - -describe('configuration', () => { - let peerInfo - - before((done) => { - waterfall([ - (cb) => PeerId.create({ bits: 512 }, cb), - (peerId, cb) => PeerInfo.create(peerId, cb), - (info, cb) => { - peerInfo = info - cb() - } - ], () => done()) - }) - - it('should throw an error if peerInfo is missing', () => { - expect(() => { - validateConfig({ - modules: { - transport: [WS] - } - }) - }).to.throw() - }) - - it('should throw an error if modules is missing', () => { - expect(() => { - validateConfig({ - peerInfo - }) - }).to.throw() - }) - - it('should throw an error if there are no transports', () => { - expect(() => { - validateConfig({ - peerInfo, - modules: { - transport: [] - } - }) - }).to.throw('ERROR_EMPTY') - }) - - it('should add defaults to config', () => { - const options = { - peerInfo, - modules: { - transport: [WS], - peerDiscovery: [Bootstrap], - dht: DHT - } - } - - const expected = { - peerInfo, - connectionManager: { - minPeers: 25 - }, - modules: { - transport: [WS], - peerDiscovery: [Bootstrap], - dht: DHT - }, - config: { - peerDiscovery: { - autoDial: true - }, - pubsub: { - enabled: true, - emitSelf: true, - signMessages: true, - strictSigning: true - }, - dht: { - kBucketSize: 20, - enabled: false, - randomWalk: { - enabled: false, - queriesPerPeriod: 1, - interval: 300000, - timeout: 10000 - } - }, - relay: { - enabled: true, - hop: { - active: false, - enabled: false - } - } - } - } - - expect(validateConfig(options)).to.deep.equal(expected) - }) - - it('should add defaults to missing items', () => { - const options = { - peerInfo, - modules: { - transport: [WS], - peerDiscovery: [Bootstrap], - dht: DHT - }, - config: { - peerDiscovery: { - bootstrap: { - interval: 1000, - enabled: true - } - }, - dht: { - enabled: false - }, - relay: { - enabled: true - }, - pubsub: { - enabled: true - } - } - } - - const expected = { - peerInfo, - connectionManager: { - minPeers: 25 - }, - modules: { - transport: [WS], - peerDiscovery: [Bootstrap], - dht: DHT - }, - config: { - peerDiscovery: { - autoDial: true, - bootstrap: { - interval: 1000, - enabled: true - } - }, - pubsub: { - enabled: true, - emitSelf: true, - signMessages: true, - strictSigning: true - }, - dht: { - kBucketSize: 20, - enabled: false, - randomWalk: { - enabled: false, - queriesPerPeriod: 1, - interval: 300000, - timeout: 10000 - } - }, - relay: { - enabled: true, - hop: { - active: false, - enabled: false - } - } - } - } - - expect(validateConfig(options)).to.deep.equal(expected) - }) - - it('should allow for configuring the switch', () => { - const options = { - peerInfo, - switch: { - denyTTL: 60e3, - denyAttempts: 5, - maxParallelDials: 100, - maxColdCalls: 50, - dialTimeout: 30e3 - }, - modules: { - transport: [WS], - peerDiscovery: [] - } - } - - expect(validateConfig(options)).to.deep.include({ - switch: { - denyTTL: 60e3, - denyAttempts: 5, - maxParallelDials: 100, - maxColdCalls: 50, - dialTimeout: 30e3 - } - }) - }) - - it('should allow for delegated content and peer routing', () => { - const peerRouter = new DelegatedPeerRouter() - const contentRouter = new DelegatedContentRouter(peerInfo) - - const options = { - peerInfo, - modules: { - transport: [WS], - peerDiscovery: [Bootstrap], - peerRouting: [peerRouter], - contentRouting: [contentRouter], - dht: DHT - }, - config: { - peerDiscovery: { - bootstrap: { - interval: 1000, - enabled: true - } - } - } - } - - expect(validateConfig(options).modules).to.deep.include({ - peerRouting: [peerRouter], - contentRouting: [contentRouter] - }) - }) - - it('should not allow for dht to be enabled without it being provided', () => { - const options = { - peerInfo, - modules: { - transport: [WS] - }, - config: { - dht: { - enabled: true - } - } - } - - expect(() => validateConfig(options)).to.throw() - }) - - it('should be able to add validators and selectors for dht', () => { - const selectors = {} - const validators = {} - - const options = { - peerInfo, - modules: { - transport: [WS], - dht: DHT - }, - config: { - dht: { - selectors, - validators - } - } - } - const expected = { - peerInfo, - connectionManager: { - minPeers: 25 - }, - modules: { - transport: [WS], - dht: DHT - }, - config: { - pubsub: { - enabled: true, - emitSelf: true, - signMessages: true, - strictSigning: true - }, - peerDiscovery: { - autoDial: true - }, - relay: { - enabled: true, - hop: { - active: false, - enabled: false - } - }, - dht: { - kBucketSize: 20, - enabled: false, - randomWalk: { - enabled: false, - queriesPerPeriod: 1, - interval: 300000, - timeout: 10000 - }, - selectors, - validators - } - } - } - expect(validateConfig(options)).to.deep.equal(expected) - }) - - it('should support new properties for the dht config', () => { - const options = { - peerInfo, - modules: { - transport: [WS], - dht: DHT - }, - config: { - dht: { - kBucketSize: 20, - enabled: false, - myNewDHTConfigProperty: true, - randomWalk: { - enabled: false, - queriesPerPeriod: 1, - interval: 300000, - timeout: 10000 - } - } - } - } - - const expected = { - kBucketSize: 20, - enabled: false, - myNewDHTConfigProperty: true, - randomWalk: { - enabled: false, - queriesPerPeriod: 1, - interval: 300000, - timeout: 10000 - } - } - - const actual = validateConfig(options).config.dht - - expect(actual).to.deep.equal(expected) - }) -}) diff --git a/test/connection-manager/default.js b/test/connection-manager/default.js deleted file mode 100644 index f4bbfad3..00000000 --- a/test/connection-manager/default.js +++ /dev/null @@ -1,19 +0,0 @@ -/* eslint-env mocha */ -'use strict' - -const Prepare = require('./utils/prepare') - -describe('default', function () { - const prepare = Prepare(3, { pollInterval: 1000 }) - before(prepare.before) - after(prepare.after) - - it('does not kick out any peer', (done) => { - prepare.connManagers().forEach((connManager) => { - connManager.on('disconnected', () => { - throw new Error('should not have disconnected') - }) - }) - setTimeout(done, 1900) - }) -}) diff --git a/test/connection-manager/max-data.js b/test/connection-manager/max-data.js deleted file mode 100644 index 1511b31f..00000000 --- a/test/connection-manager/max-data.js +++ /dev/null @@ -1,36 +0,0 @@ -/* eslint-env mocha */ -'use strict' - -const chai = require('chai') -chai.use(require('dirty-chai')) -const expect = chai.expect - -const Prepare = require('./utils/prepare') - -const PEER_COUNT = 3 - -describe('maxData', function () { - const prepare = Prepare(PEER_COUNT, { - maxData: 100, - minPeers: 1 - }) - before(prepare.create) - after(prepare.after) - - it('kicks out peer after maxData reached', function (done) { - this.timeout(10000) - - let disconnects = 0 - const manager = prepare.connManagers()[0] - manager.on('disconnected', () => { - disconnects++ - expect(disconnects).to.be.most(PEER_COUNT - 2) - manager.removeAllListeners('disconnected') - done() - }) - - prepare.tryConnectAll((err) => { - expect(err).to.not.exist() - }) - }) -}) diff --git a/test/connection-manager/max-event-loop-delay.js b/test/connection-manager/max-event-loop-delay.js deleted file mode 100644 index 7896c963..00000000 --- a/test/connection-manager/max-event-loop-delay.js +++ /dev/null @@ -1,59 +0,0 @@ -/* eslint-env mocha */ -'use strict' - -const chai = require('chai') -chai.use(require('dirty-chai')) -const expect = chai.expect - -const Prepare = require('./utils/prepare') - -const PEER_COUNT = 3 - -describe('maxEventLoopDelay', function () { - const prepare = Prepare(PEER_COUNT, [{ - pollInterval: 1000, - maxEventLoopDelay: 5, - minPeers: 1 - }]) - before(prepare.create) - after(prepare.after) - - it('kicks out peer after maxEventLoopDelay reached', function (done) { - this.timeout(10000) - let stopped = false - - let disconnects = 0 - const manager = prepare.connManagers()[0] - manager.on('disconnected', () => { - disconnects++ - expect(disconnects).to.be.most(PEER_COUNT - 2) - manager.removeAllListeners('disconnected') - stopped = true - done() - }) - - prepare.tryConnectAll((err) => { - expect(err).to.not.exist() - makeDelay() - }) - - function makeDelay () { - let sum = 0 - for (let i = 0; i < 1000000; i++) { - sum += Math.random() - } - debug(sum) - - if (!stopped) { - setTimeout(makeDelay, 0) - } - } - }) -}) - -function debug (what) { - if (what === 0) { - // never true but the compiler doesn't know that - throw new Error('something went wrong') - } -} diff --git a/test/connection-manager/max-peer-per-protocol.js b/test/connection-manager/max-peer-per-protocol.js deleted file mode 100644 index 7cc4947d..00000000 --- a/test/connection-manager/max-peer-per-protocol.js +++ /dev/null @@ -1,37 +0,0 @@ -/* eslint-env mocha */ -'use strict' - -const chai = require('chai') -chai.use(require('dirty-chai')) -const expect = chai.expect - -const Prepare = require('./utils/prepare') - -const PEER_COUNT = 3 - -describe('maxPeers', function () { - const prepare = Prepare(PEER_COUNT, [{ - maxPeersPerProtocol: { - tcp: 1 - } - }]) - before(prepare.create) - after(prepare.after) - - it('kicks out peers in excess', function (done) { - this.timeout(10000) - - let disconnects = 0 - const manager = prepare.connManagers()[0] - manager.on('disconnected', () => { - disconnects++ - expect(disconnects).to.be.most(PEER_COUNT - 2) - manager.removeAllListeners('disconnected') - done() - }) - - prepare.tryConnectAll((err) => { - expect(err).to.not.exist() - }) - }) -}) diff --git a/test/connection-manager/max-peers.js b/test/connection-manager/max-peers.js deleted file mode 100644 index 751286f6..00000000 --- a/test/connection-manager/max-peers.js +++ /dev/null @@ -1,35 +0,0 @@ -/* eslint-env mocha */ -'use strict' - -const chai = require('chai') -chai.use(require('dirty-chai')) -const expect = chai.expect - -const Prepare = require('./utils/prepare') - -const PEER_COUNT = 3 - -describe('maxPeers', function () { - const prepare = Prepare(PEER_COUNT, [{ - maxPeers: 1 - }]) - before(prepare.create) - after(prepare.after) - - it('kicks out peers in excess', function (done) { - this.timeout(10000) - - let disconnects = 0 - const manager = prepare.connManagers()[0] - manager.on('disconnected', () => { - disconnects++ - expect(disconnects).to.be.most(PEER_COUNT - 2) - manager.removeAllListeners('disconnected') - done() - }) - - prepare.tryConnectAll((err, eachNodeConnections) => { - expect(err).to.not.exist() - }) - }) -}) diff --git a/test/connection-manager/max-received-data.js b/test/connection-manager/max-received-data.js deleted file mode 100644 index 908b720f..00000000 --- a/test/connection-manager/max-received-data.js +++ /dev/null @@ -1,36 +0,0 @@ -/* eslint-env mocha */ -'use strict' - -const chai = require('chai') -chai.use(require('dirty-chai')) -const expect = chai.expect - -const Prepare = require('./utils/prepare') - -const PEER_COUNT = 3 - -describe('maxReceivedData', function () { - const prepare = Prepare(PEER_COUNT, { - maxReceivedData: 50, - minPeers: 1 - }) - before(prepare.create) - after(prepare.after) - - it('kicks out peer after maxReceivedData reached', function (done) { - this.timeout(10000) - - let disconnects = 0 - const manager = prepare.connManagers()[0] - manager.on('disconnected', () => { - disconnects++ - expect(disconnects).to.be.most(PEER_COUNT - 2) - manager.removeAllListeners('disconnected') - done() - }) - - prepare.tryConnectAll((err, eachNodeConnections) => { - expect(err).to.not.exist() - }) - }) -}) diff --git a/test/connection-manager/max-sent-data.js b/test/connection-manager/max-sent-data.js deleted file mode 100644 index ac1885b6..00000000 --- a/test/connection-manager/max-sent-data.js +++ /dev/null @@ -1,36 +0,0 @@ -/* eslint-env mocha */ -'use strict' - -const chai = require('chai') -chai.use(require('dirty-chai')) -const expect = chai.expect - -const Prepare = require('./utils/prepare') - -const PEER_COUNT = 3 - -describe('maxSentData', function () { - const prepare = Prepare(PEER_COUNT, [{ - maxSentData: 50, - minPeers: 1 - }]) - before(prepare.create) - after(prepare.after) - - it('kicks out peer after maxSentData reached', function (done) { - this.timeout(10000) - - let disconnects = 0 - const manager = prepare.connManagers()[0] - manager.on('disconnected', () => { - disconnects++ - expect(disconnects).to.be.most(PEER_COUNT - 2) - manager.removeAllListeners('disconnected') - done() - }) - - prepare.tryConnectAll((err, eachNodeConnections) => { - expect(err).to.not.exist() - }) - }) -}) diff --git a/test/connection-manager/node.js b/test/connection-manager/node.js deleted file mode 100644 index a307d490..00000000 --- a/test/connection-manager/node.js +++ /dev/null @@ -1,10 +0,0 @@ -'use strict' - -require('./default') -require('./max-data') -require('./max-event-loop-delay') -require('./max-peer-per-protocol') -require('./max-peers') -require('./max-received-data') -require('./max-sent-data') -require('./set-peer-value') diff --git a/test/connection-manager/set-peer-value.js b/test/connection-manager/set-peer-value.js deleted file mode 100644 index c0adb91c..00000000 --- a/test/connection-manager/set-peer-value.js +++ /dev/null @@ -1,44 +0,0 @@ -/* eslint-env mocha */ -'use strict' - -const chai = require('chai') -chai.use(require('dirty-chai')) -const expect = chai.expect - -const Prepare = require('./utils/prepare') - -const PEER_COUNT = 3 - -describe('setPeerValue', function () { - const prepare = Prepare(PEER_COUNT, [{ - maxPeers: 1, - defaultPeerValue: 0 - }]) - before(prepare.create) - after(prepare.after) - - it('kicks out lower valued peer first', function (done) { - let disconnects = 0 - let firstConnectedPeer - const manager = prepare.connManagers()[0] - - manager.once('connected', (peerId) => { - if (!firstConnectedPeer) { - firstConnectedPeer = peerId - manager.setPeerValue(peerId, 1) - } - }) - - manager.on('disconnected', (peerId) => { - disconnects++ - expect(disconnects).to.be.most(PEER_COUNT - 2) - expect(peerId).to.not.be.equal(firstConnectedPeer) - manager.removeAllListeners('disconnected') - done() - }) - - prepare.tryConnectAll((err) => { - expect(err).to.not.exist() - }) - }) -}) diff --git a/test/connection-manager/utils/connect-all.js b/test/connection-manager/utils/connect-all.js deleted file mode 100644 index e89f3fc8..00000000 --- a/test/connection-manager/utils/connect-all.js +++ /dev/null @@ -1,17 +0,0 @@ -'use strict' - -const eachSeries = require('async/eachSeries') - -module.exports = (nodes, callback) => { - eachSeries( - nodes, - (node, cb) => { - eachSeries( - nodes.filter(n => node !== n), - (otherNode, cb) => node.dial(otherNode.peerInfo, cb), - cb - ) - }, - callback - ) -} diff --git a/test/connection-manager/utils/create-libp2p-node.js b/test/connection-manager/utils/create-libp2p-node.js deleted file mode 100644 index 0fcdab89..00000000 --- a/test/connection-manager/utils/create-libp2p-node.js +++ /dev/null @@ -1,50 +0,0 @@ -'use strict' - -const TCP = require('libp2p-tcp') -const Multiplex = require('libp2p-mplex') -const SECIO = require('libp2p-secio') -const libp2p = require('../../../src') -const waterfall = require('async/waterfall') -const PeerInfo = require('peer-info') -const PeerId = require('peer-id') - -const ConnManager = require('../../../src/connection-manager') - -class Node extends libp2p { - constructor (peerInfo) { - const modules = { - transport: [TCP], - streamMuxer: [Multiplex], - connEncryption: [SECIO] - } - - super({ - peerInfo, - modules, - config: { - peerDiscovery: { - autoDial: false - } - } - }) - } -} - -function createLibp2pNode (options, callback) { - let node - - waterfall([ - (cb) => PeerId.create({ bits: 1024 }, cb), - (id, cb) => PeerInfo.create(id, cb), - (peerInfo, cb) => { - peerInfo.multiaddrs.add('/ip4/127.0.0.1/tcp/0') - node = new Node(peerInfo) - // Replace the connection manager so we use source code instead of dep code - node.connectionManager = new ConnManager(node, options) - node.start(cb) - } - ], (err) => callback(err, node)) -} - -exports = module.exports = createLibp2pNode -exports.bundle = Node diff --git a/test/connection-manager/utils/prepare.js b/test/connection-manager/utils/prepare.js deleted file mode 100644 index 84c305a2..00000000 --- a/test/connection-manager/utils/prepare.js +++ /dev/null @@ -1,83 +0,0 @@ -'use strict' - -const chai = require('chai') -chai.use(require('dirty-chai')) -const expect = chai.expect - -const series = require('async/series') -const each = require('async/each') - -const createLibp2pNode = require('./create-libp2p-node') -const connectAll = require('./connect-all') -const tryConnectAll = require('./try-connect-all') - -module.exports = (count, options) => { - let nodes - - if (!Array.isArray(options)) { - const opts = options - options = [] - for (let n = 0; n < count; n++) { - options[n] = opts - } - } - - const create = (done) => { - const tasks = [] - for (let i = 0; i < count; i++) { - tasks.push((cb) => createLibp2pNode(options.shift() || {}, cb)) - } - - series(tasks, (err, things) => { - if (!err) { - nodes = things - expect(things.length).to.equal(count) - } - done(err) - }) - } - - const connect = function (done) { - if (this && this.timeout) { - this.timeout(10000) - } - connectAll(nodes, done) - } - - const tryConnectAllFn = function (done) { - if (this && this.timeout) { - this.timeout(10000) - } - tryConnectAll(nodes, done) - } - - const before = (done) => { - if (this && this.timeout) { - this.timeout(10000) - } - series([create, connect], done) - } - - const after = function (done) { - if (this && this.timeout) { - this.timeout(10000) - } - if (!nodes) { return done() } - - each(nodes, (node, cb) => { - series([ - (cb) => node.stop(cb) - ], cb) - }, done) - } - - return { - create, - connect, - tryConnectAll: tryConnectAllFn, - before, - after, - things: () => nodes, - connManagers: () => nodes.map((node) => node.connectionManager) - } -} diff --git a/test/connection-manager/utils/try-connect-all.js b/test/connection-manager/utils/try-connect-all.js deleted file mode 100644 index 2d755c87..00000000 --- a/test/connection-manager/utils/try-connect-all.js +++ /dev/null @@ -1,27 +0,0 @@ -'use strict' - -const mapSeries = require('async/mapSeries') -const eachSeries = require('async/eachSeries') - -module.exports = (nodes, callback) => { - mapSeries( - nodes, - (node, cb) => { - const connectedTo = [] - eachSeries( - nodes.filter(n => node !== n), - (otherNode, cb) => { - const otherNodePeerInfo = otherNode.peerInfo - node.dial(otherNodePeerInfo, (err) => { - if (!err) { - connectedTo.push(otherNodePeerInfo.id.toB58String()) - } - cb() - }) - }, - (err) => cb(err, connectedTo) - ) - }, - callback - ) -} diff --git a/test/content-routing.node.js b/test/content-routing.node.js deleted file mode 100644 index 43534f12..00000000 --- a/test/content-routing.node.js +++ /dev/null @@ -1,404 +0,0 @@ -/* eslint-env mocha */ -/* eslint max-nested-callbacks: ["error", 8] */ - -'use strict' - -const chai = require('chai') -chai.use(require('dirty-chai')) -const expect = chai.expect -const parallel = require('async/parallel') -const waterfall = require('async/waterfall') -const _times = require('lodash.times') -const CID = require('cids') -const DelegatedContentRouter = require('libp2p-delegated-content-routing') -const sinon = require('sinon') -const nock = require('nock') -const ma = require('multiaddr') -const Node = require('./utils/bundle-nodejs') - -const createNode = require('./utils/create-node') -const createPeerInfo = createNode.createPeerInfo - -describe('.contentRouting', () => { - describe('via the dht', () => { - let nodeA - let nodeB - let nodeC - let nodeD - let nodeE - - before(function (done) { - this.timeout(5 * 1000) - const tasks = _times(5, () => (cb) => { - createNode('/ip4/0.0.0.0/tcp/0', (err, node) => { - expect(err).to.not.exist() - node.start((err) => cb(err, node)) - }) - }) - - parallel(tasks, (err, nodes) => { - expect(err).to.not.exist() - nodeA = nodes[0] - nodeB = nodes[1] - nodeC = nodes[2] - nodeD = nodes[3] - nodeE = nodes[4] - - parallel([ - (cb) => nodeA.dial(nodeB.peerInfo, cb), - (cb) => nodeB.dial(nodeC.peerInfo, cb), - (cb) => nodeC.dial(nodeD.peerInfo, cb), - (cb) => nodeD.dial(nodeE.peerInfo, cb), - (cb) => nodeE.dial(nodeA.peerInfo, cb) - ], done) - }) - }) - - after((done) => { - parallel([ - (cb) => nodeA.stop(cb), - (cb) => nodeB.stop(cb), - (cb) => nodeC.stop(cb), - (cb) => nodeD.stop(cb), - (cb) => nodeE.stop(cb) - ], done) - }) - - it('should use the nodes dht to provide', (done) => { - const stub = sinon.stub(nodeA._dht, 'provide').callsFake(() => { - stub.restore() - done() - }) - - nodeA.contentRouting.provide() - }) - - it('should use the nodes dht to find providers', (done) => { - const stub = sinon.stub(nodeA._dht, 'findProviders').callsFake(() => { - stub.restore() - done() - }) - - nodeA.contentRouting.findProviders() - }) - - describe('le ring', () => { - const cid = new CID('QmTp9VkYvnHyrqKQuFPiuZkiX9gPcqj6x5LJ1rmWuSySnL') - - it('let kbucket get filled', (done) => { - setTimeout(() => done(), 250) - }) - - it('nodeA.contentRouting.provide', (done) => { - nodeA.contentRouting.provide(cid, done) - }) - - it('nodeE.contentRouting.findProviders for existing record', (done) => { - nodeE.contentRouting.findProviders(cid, { maxTimeout: 5000 }, (err, providers) => { - expect(err).to.not.exist() - expect(providers).to.have.length.above(0) - done() - }) - }) - - it('nodeE.contentRouting.findProviders with limited number of providers', (done) => { - parallel([ - (cb) => nodeA.contentRouting.provide(cid, cb), - (cb) => nodeB.contentRouting.provide(cid, cb), - (cb) => nodeC.contentRouting.provide(cid, cb) - ], (err) => { - expect(err).to.not.exist() - - nodeE.contentRouting.findProviders(cid, { maxNumProviders: 2 }, (err, providers) => { - expect(err).to.not.exist() - expect(providers).to.have.length(2) - done() - }) - }) - }) - - it('nodeC.contentRouting.findProviders for non existing record (timeout)', (done) => { - const cid = new CID('QmTp9VkYvnHyrqKQuFPiuZkiX9gPcqj6x5LJ1rmWuSnnnn') - - nodeE.contentRouting.findProviders(cid, { maxTimeout: 5000 }, (err, providers) => { - expect(err).to.exist() - expect(err.code).to.eql('ERR_NOT_FOUND') - expect(providers).to.not.exist() - done() - }) - }) - }) - }) - - describe('via a delegate', () => { - let nodeA - let delegate - - before((done) => { - waterfall([ - (cb) => { - createPeerInfo(cb) - }, - // Create the node using the delegate - (peerInfo, cb) => { - delegate = new DelegatedContentRouter(peerInfo.id, { - host: '0.0.0.0', - protocol: 'http', - port: 60197 - }, [ - ma('/ip4/0.0.0.0/tcp/60194') - ]) - nodeA = new Node({ - peerInfo, - modules: { - contentRouting: [delegate] - }, - config: { - dht: { - enabled: false - }, - relay: { - enabled: true, - hop: { - enabled: true, - active: false - } - } - } - }) - nodeA.start(cb) - } - ], done) - }) - - after((done) => nodeA.stop(done)) - afterEach(() => nock.cleanAll()) - - describe('provide', () => { - it('should use the delegate router to provide', (done) => { - const stub = sinon.stub(delegate, 'provide').callsFake(() => { - stub.restore() - done() - }) - nodeA.contentRouting.provide() - }) - - it('should be able to register as a provider', (done) => { - const cid = new CID('QmU621oD8AhHw6t25vVyfYKmL9VV3PTgc52FngEhTGACFB') - const mockApi = nock('http://0.0.0.0:60197') - // mock the refs call - .post('/api/v0/refs') - .query({ - recursive: false, - arg: cid.toBaseEncodedString(), - 'stream-channels': true - }) - .reply(200, null, [ - 'Content-Type', 'application/json', - 'X-Chunked-Output', '1' - ]) - - nodeA.contentRouting.provide(cid, (err) => { - expect(err).to.not.exist() - expect(mockApi.isDone()).to.equal(true) - done() - }) - }) - - it('should handle errors when registering as a provider', (done) => { - const cid = new CID('QmU621oD8AhHw6t25vVyfYKmL9VV3PTgc52FngEhTGACFB') - const mockApi = nock('http://0.0.0.0:60197') - // mock the refs call - .post('/api/v0/refs') - .query({ - recursive: false, - arg: cid.toBaseEncodedString(), - 'stream-channels': true - }) - .reply(502, 'Bad Gateway', ['Content-Type', 'application/json']) - - nodeA.contentRouting.provide(cid, (err) => { - expect(err).to.exist() - expect(mockApi.isDone()).to.equal(true) - done() - }) - }) - }) - - describe('find providers', () => { - it('should use the delegate router to find providers', (done) => { - const stub = sinon.stub(delegate, 'findProviders').callsFake(() => { - stub.restore() - done() - }) - nodeA.contentRouting.findProviders() - }) - - it('should be able to find providers', (done) => { - const cid = new CID('QmU621oD8AhHw6t25vVyfYKmL9VV3PTgc52FngEhTGACFB') - const provider = 'QmZNgCqZCvTsi3B4Vt7gsSqpkqDpE7M2Y9TDmEhbDb4ceF' - const mockApi = nock('http://0.0.0.0:60197') - .post('/api/v0/dht/findprovs') - .query({ - arg: cid.toBaseEncodedString(), - timeout: '1000ms', - 'stream-channels': true - }) - .reply(200, `{"Extra":"","ID":"QmWKqWXCtRXEeCQTo3FoZ7g4AfnGiauYYiczvNxFCHicbB","Responses":[{"Addrs":["/ip4/0.0.0.0/tcp/0"],"ID":"${provider}"}],"Type":4}\n`, [ - 'Content-Type', 'application/json', - 'X-Chunked-Output', '1' - ]) - - nodeA.contentRouting.findProviders(cid, 1000, (err, response) => { - expect(err).to.not.exist() - expect(response).to.have.length(1) - expect(response[0].id.toB58String()).to.equal(provider) - expect(mockApi.isDone()).to.equal(true) - done() - }) - }) - - it('should handle errors when finding providers', (done) => { - const cid = new CID('QmU621oD8AhHw6t25vVyfYKmL9VV3PTgc52FngEhTGACFB') - const mockApi = nock('http://0.0.0.0:60197') - .post('/api/v0/dht/findprovs') - .query({ - arg: cid.toBaseEncodedString(), - timeout: '30000ms', - 'stream-channels': true - }) - .reply(502, 'Bad Gateway', [ - 'X-Chunked-Output', '1' - ]) - - nodeA.contentRouting.findProviders(cid, (err) => { - expect(err).to.exist() - expect(mockApi.isDone()).to.equal(true) - done() - }) - }) - }) - }) - - describe('via the dht and a delegate', () => { - let nodeA - let delegate - - before((done) => { - waterfall([ - (cb) => { - createPeerInfo(cb) - }, - // Create the node using the delegate - (peerInfo, cb) => { - delegate = new DelegatedContentRouter(peerInfo.id, { - host: '0.0.0.0', - protocol: 'http', - port: 60197 - }, [ - ma('/ip4/0.0.0.0/tcp/60194') - ]) - nodeA = new Node({ - peerInfo, - modules: { - contentRouting: [delegate] - }, - config: { - relay: { - enabled: true, - hop: { - enabled: true, - active: false - } - } - } - }) - nodeA.start(cb) - } - ], done) - }) - - after((done) => nodeA.stop(done)) - - describe('provide', () => { - it('should use both the dht and delegate router to provide', (done) => { - const dhtStub = sinon.stub(nodeA._dht, 'provide').callsFake(() => {}) - const delegateStub = sinon.stub(delegate, 'provide').callsFake(() => { - expect(dhtStub.calledOnce).to.equal(true) - expect(delegateStub.calledOnce).to.equal(true) - delegateStub.restore() - dhtStub.restore() - done() - }) - nodeA.contentRouting.provide() - }) - }) - - describe('findProviders', () => { - it('should only use the dht if it finds providers', (done) => { - const results = [true] - const dhtStub = sinon.stub(nodeA._dht, 'findProviders').callsArgWith(2, null, results) - const delegateStub = sinon.stub(delegate, 'findProviders').throws(() => { - return new Error('the delegate should not have been called') - }) - - nodeA.contentRouting.findProviders('a cid', { maxTimeout: 5000 }, (err, results) => { - expect(err).to.not.exist() - expect(results).to.equal(results) - expect(dhtStub.calledOnce).to.equal(true) - expect(delegateStub.notCalled).to.equal(true) - delegateStub.restore() - dhtStub.restore() - done() - }) - }) - - it('should use the delegate if the dht fails to find providers', (done) => { - const results = [true] - const dhtStub = sinon.stub(nodeA._dht, 'findProviders').callsArgWith(2, null, []) - const delegateStub = sinon.stub(delegate, 'findProviders').callsArgWith(2, null, results) - - nodeA.contentRouting.findProviders('a cid', { maxTimeout: 5000 }, (err, results) => { - expect(err).to.not.exist() - expect(results).to.deep.equal(results) - expect(dhtStub.calledOnce).to.equal(true) - expect(delegateStub.calledOnce).to.equal(true) - delegateStub.restore() - dhtStub.restore() - done() - }) - }) - }) - }) - - describe('no routers', () => { - let nodeA - before((done) => { - createNode('/ip4/0.0.0.0/tcp/0', { - config: { - dht: { - enabled: false - } - } - }, (err, node) => { - expect(err).to.not.exist() - nodeA = node - done() - }) - }) - - it('.findProviders should return an error with no options', (done) => { - nodeA.contentRouting.findProviders('a cid', (err) => { - expect(err).to.exist() - done() - }) - }) - - it('.findProviders should return an error with options', (done) => { - nodeA.contentRouting.findProviders('a cid', { maxTimeout: 5000 }, (err) => { - expect(err).to.exist() - done() - }) - }) - }) -}) diff --git a/test/create.spec.js b/test/create.spec.js deleted file mode 100644 index d73582a6..00000000 --- a/test/create.spec.js +++ /dev/null @@ -1,143 +0,0 @@ -/* eslint-env mocha */ -'use strict' - -const chai = require('chai') -chai.use(require('dirty-chai')) -const expect = chai.expect -const series = require('async/series') -const createNode = require('./utils/create-node') -const sinon = require('sinon') -const { createLibp2p } = require('../src') -const WS = require('libp2p-websockets') -const PeerInfo = require('peer-info') - -describe('libp2p creation', () => { - afterEach(() => { - sinon.restore() - }) - - it('should be able to start and stop successfully', (done) => { - createNode([], { - config: { - pubsub: { - enabled: true - }, - dht: { - enabled: true - } - } - }, (err, node) => { - expect(err).to.not.exist() - - const sw = node._switch - const cm = node.connectionManager - const dht = node._dht - const pub = node.pubsub - - sinon.spy(sw, 'start') - sinon.spy(cm, 'start') - sinon.spy(dht, 'start') - sinon.spy(dht.randomWalk, 'start') - sinon.spy(pub, 'start') - sinon.spy(sw, 'stop') - sinon.spy(cm, 'stop') - sinon.spy(dht, 'stop') - sinon.spy(dht.randomWalk, 'stop') - sinon.spy(pub, 'stop') - sinon.spy(node, 'emit') - - series([ - (cb) => node.start(cb), - (cb) => { - expect(sw.start.calledOnce).to.equal(true) - expect(cm.start.calledOnce).to.equal(true) - expect(dht.start.calledOnce).to.equal(true) - expect(dht.randomWalk.start.calledOnce).to.equal(true) - expect(pub.start.calledOnce).to.equal(true) - expect(node.emit.calledWith('start')).to.equal(true) - - cb() - }, - (cb) => node.stop(cb) - ], (err) => { - expect(err).to.not.exist() - - expect(sw.stop.calledOnce).to.equal(true) - expect(cm.stop.calledOnce).to.equal(true) - expect(dht.stop.calledOnce).to.equal(true) - expect(dht.randomWalk.stop.called).to.equal(true) - expect(pub.stop.calledOnce).to.equal(true) - expect(node.emit.calledWith('stop')).to.equal(true) - - done() - }) - }) - }) - - it('should not create disabled modules', (done) => { - createNode([], { - config: { - pubsub: { - enabled: false - } - } - }, (err, node) => { - expect(err).to.not.exist() - expect(node._pubsub).to.not.exist() - done() - }) - }) - - it('should not throw errors from switch if node has no error listeners', (done) => { - createNode([], {}, (err, node) => { - expect(err).to.not.exist() - - node._switch.emit('error', new Error('bad things')) - done() - }) - }) - - it('should emit errors from switch if node has error listeners', (done) => { - const error = new Error('bad things') - createNode([], {}, (err, node) => { - expect(err).to.not.exist() - node.once('error', (err) => { - expect(err).to.eql(error) - done() - }) - node._switch.emit('error', error) - }) - }) - - it('createLibp2p should create a peerInfo instance', function (done) { - this.timeout(10e3) - createLibp2p({ - modules: { - transport: [WS] - } - }, (err, libp2p) => { - expect(err).to.not.exist() - expect(libp2p).to.exist() - done() - }) - }) - - it('createLibp2p should allow for a provided peerInfo instance', function (done) { - this.timeout(10e3) - PeerInfo.create((err, peerInfo) => { - expect(err).to.not.exist() - sinon.spy(PeerInfo, 'create') - createLibp2p({ - peerInfo, - modules: { - transport: [WS] - } - }, (err, libp2p) => { - expect(err).to.not.exist() - expect(libp2p).to.exist() - expect(PeerInfo.create.callCount).to.eql(0) - done() - }) - }) - }) -}) diff --git a/test/dht.node.js b/test/dht.node.js deleted file mode 100644 index cecd7b72..00000000 --- a/test/dht.node.js +++ /dev/null @@ -1,168 +0,0 @@ -/* eslint-env mocha */ - -'use strict' - -const chai = require('chai') -chai.use(require('dirty-chai')) -const expect = chai.expect - -const MemoryStore = require('interface-datastore').MemoryDatastore - -const createNode = require('./utils/create-node') - -describe('.dht', () => { - describe('enabled', () => { - let nodeA - const datastore = new MemoryStore() - - before(function (done) { - createNode('/ip4/0.0.0.0/tcp/0', { - datastore - }, (err, node) => { - expect(err).to.not.exist() - nodeA = node - - // Rewrite validators - nodeA._dht.validators.v = { - func (key, publicKey, callback) { - setImmediate(callback) - }, - sign: false - } - - // Rewrite selectors - nodeA._dht.selectors.v = () => 0 - - // Start - nodeA.start(done) - }) - }) - - after((done) => { - nodeA.stop(done) - }) - - it('should be able to dht.put a value to the DHT', (done) => { - const key = Buffer.from('key') - const value = Buffer.from('value') - - nodeA.dht.put(key, value, (err) => { - expect(err).to.not.exist() - done() - }) - }) - - it('should be able to dht.get a value from the DHT with options', (done) => { - const key = Buffer.from('/v/hello') - const value = Buffer.from('world') - - nodeA.dht.put(key, value, (err) => { - expect(err).to.not.exist() - - nodeA.dht.get(key, { maxTimeout: 3000 }, (err, res) => { - expect(err).to.not.exist() - expect(res).to.eql(value) - done() - }) - }) - }) - - it('should be able to dht.get a value from the DHT with no options defined', (done) => { - const key = Buffer.from('/v/hello') - const value = Buffer.from('world') - - nodeA.dht.put(key, value, (err) => { - expect(err).to.not.exist() - - nodeA.dht.get(key, (err, res) => { - expect(err).to.not.exist() - expect(res).to.eql(value) - done() - }) - }) - }) - - it('should be able to dht.getMany a value from the DHT with options', (done) => { - const key = Buffer.from('/v/hello') - const value = Buffer.from('world') - - nodeA.dht.put(key, value, (err) => { - expect(err).to.not.exist() - - nodeA.dht.getMany(key, 1, { maxTimeout: 3000 }, (err, res) => { - expect(err).to.not.exist() - expect(res).to.exist() - done() - }) - }) - }) - - it('should be able to dht.getMany a value from the DHT with no options defined', (done) => { - const key = Buffer.from('/v/hello') - const value = Buffer.from('world') - - nodeA.dht.put(key, value, (err) => { - expect(err).to.not.exist() - - nodeA.dht.getMany(key, 1, (err, res) => { - expect(err).to.not.exist() - expect(res).to.exist() - done() - }) - }) - }) - }) - - describe('disabled', () => { - let nodeA - - before(function (done) { - createNode('/ip4/0.0.0.0/tcp/0', { - config: { - dht: { - enabled: false - } - } - }, (err, node) => { - expect(err).to.not.exist() - nodeA = node - nodeA.start(done) - }) - }) - - after((done) => { - nodeA.stop(done) - }) - - it('should receive an error on dht.put if the dht is disabled', (done) => { - const key = Buffer.from('key') - const value = Buffer.from('value') - - nodeA.dht.put(key, value, (err) => { - expect(err).to.exist() - expect(err.code).to.equal('ERR_DHT_DISABLED') - done() - }) - }) - - it('should receive an error on dht.get if the dht is disabled', (done) => { - const key = Buffer.from('key') - - nodeA.dht.get(key, (err) => { - expect(err).to.exist() - expect(err.code).to.equal('ERR_DHT_DISABLED') - done() - }) - }) - - it('should receive an error on dht.getMany if the dht is disabled', (done) => { - const key = Buffer.from('key') - - nodeA.dht.getMany(key, 10, (err) => { - expect(err).to.exist() - expect(err.code).to.equal('ERR_DHT_DISABLED') - done() - }) - }) - }) -}) diff --git a/test/fixtures/browser.js b/test/fixtures/browser.js new file mode 100644 index 00000000..57c9628a --- /dev/null +++ b/test/fixtures/browser.js @@ -0,0 +1,7 @@ +'use strict' + +const multiaddr = require('multiaddr') + +module.exports.MULTIADDRS_WEBSOCKETS = [ + multiaddr('/ip4/127.0.0.1/tcp/15001/ws') +] diff --git a/test/fixtures/peers.js b/test/fixtures/peers.js new file mode 100644 index 00000000..fad0d23e --- /dev/null +++ b/test/fixtures/peers.js @@ -0,0 +1,27 @@ +'use strict' + +module.exports = [{ + id: 'QmNMMAqSxPetRS1cVMmutW5BCN1qQQyEr4u98kUvZjcfEw', + privKey: 'CAASpQkwggShAgEAAoIBAQDPek2aeHMa0blL42RTKd6xgtkk4Zkldvq4LHxzcag5uXepiQzWANEUvoD3KcUTmMRmx14PvsxdLCNst7S2JSa0R2n5wSRs14zGy6892lx4H4tLBD1KSpQlJ6vabYM1CJhIQRG90BtzDPrJ/X1iJ2HA0PPDz0Mflam2QUMDDrU0IuV2m7gSCJ5r4EmMs3U0xnH/1gShkVx4ir0WUdoWf5KQUJOmLn1clTRHYPv4KL9A/E38+imNAXfkH3c2T7DrCcYRkZSpK+WecjMsH1dCX15hhhggNqfp3iulO1tGPxHjm7PDGTPUjpCWKpD5e50sLqsUwexac1ja6ktMfszIR+FPAgMBAAECggEAB2H2uPRoRCAKU+T3gO4QeoiJaYKNjIO7UCplE0aMEeHDnEjAKC1HQ1G0DRdzZ8sb0fxuIGlNpFMZv5iZ2ZFg2zFfV//DaAwTek9tIOpQOAYHUtgHxkj5FIlg2BjlflGb+ZY3J2XsVB+2HNHkUEXOeKn2wpTxcoJE07NmywkO8Zfr1OL5oPxOPlRN1gI4ffYH2LbfaQVtRhwONR2+fs5ISfubk5iKso6BX4moMYkxubYwZbpucvKKi/rIjUA3SK86wdCUnno1KbDfdXSgCiUlvxt/IbRFXFURQoTV6BOi3sP5crBLw8OiVubMr9/8WE6KzJ0R7hPd5+eeWvYiYnWj4QKBgQD6jRlAFo/MgPO5NZ/HRAk6LUG+fdEWexA+GGV7CwJI61W/Dpbn9ZswPDhRJKo3rquyDFVZPdd7+RlXYg1wpmp1k54z++L1srsgj72vlg4I8wkZ4YLBg0+zVgHlQ0kxnp16DvQdOgiRFvMUUMEgetsoIx1CQWTd67hTExGsW+WAZQKBgQDT/WaHWvwyq9oaZ8G7F/tfeuXvNTk3HIJdfbWGgRXB7lJ7Gf6FsX4x7PeERfL5a67JLV6JdiLLVuYC2CBhipqLqC2DB962aKMvxobQpSljBBZvZyqP1IGPoKskrSo+2mqpYkeCLbDMuJ1nujgMP7gqVjabs2zj6ACKmmpYH/oNowJ/T0ZVtvFsjkg+1VsiMupUARRQuPUWMwa9HOibM1NIZcoQV2NGXB5Z++kR6JqxQO0DZlKArrviclderUdY+UuuY4VRiSEprpPeoW7ZlbTku/Ap8QZpWNEzZorQDro7bnfBW91fX9/81ets/gCPGrfEn+58U3pdb9oleCOQc/ifpQKBgBTYGbi9bYbd9vgZs6bd2M2um+VFanbMytS+g5bSIn2LHXkVOT2UEkB+eGf9KML1n54QY/dIMmukA8HL1oNAyalpw+/aWj+9Ui5kauUhGEywHjSeBEVYM9UXizxz+m9rsoktLLLUI0o97NxCJzitG0Kub3gn0FEogsUeIc7AdinZAoGBANnM1vcteSQDs7x94TDEnvvqwSkA2UWyLidD2jXgE0PG4V6tTkK//QPBmC9eq6TIqXkzYlsErSw4XeKO91knFofmdBzzVh/ddgx/NufJV4tXF+a2iTpqYBUJiz9wpIKgf43/Ob+P1EA99GAhSdxz1ess9O2aTqf3ANzn6v6g62Pv', + pubKey: 'CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDPek2aeHMa0blL42RTKd6xgtkk4Zkldvq4LHxzcag5uXepiQzWANEUvoD3KcUTmMRmx14PvsxdLCNst7S2JSa0R2n5wSRs14zGy6892lx4H4tLBD1KSpQlJ6vabYM1CJhIQRG90BtzDPrJ/X1iJ2HA0PPDz0Mflam2QUMDDrU0IuV2m7gSCJ5r4EmMs3U0xnH/1gShkVx4ir0WUdoWf5KQUJOmLn1clTRHYPv4KL9A/E38+imNAXfkH3c2T7DrCcYRkZSpK+WecjMsH1dCX15hhhggNqfp3iulO1tGPxHjm7PDGTPUjpCWKpD5e50sLqsUwexac1ja6ktMfszIR+FPAgMBAAE=' +}, { + id: 'QmW8rAgaaA6sRydK1k6vonShQME47aDxaFidbtMevWs73t', + privKey: 'CAASpwkwggSjAgEAAoIBAQCTU3gVDv3SRXLOsFln9GEf1nJ/uCEDhOG10eC0H9l9IPpVxjuPT1ep+ykFUdvefq3D3q+W3hbmiHm81o8dYv26RxZIEioToUWp7Ec5M2B/niYoE93za9/ZDwJdl7eh2hNKwAdxTmdbXUPjkIU4vLyHKRFbJIn9X8w9djldz8hoUvC1BK4L1XrT6F2l0ruJXErH2ZwI1youfSzo87TdXIoFKdrQLuW6hOtDCGKTiS+ab/DkMODc6zl8N47Oczv7vjzoWOJMUJs1Pg0ZsD1zmISY38P0y/QyEhatZn0B8BmSWxlLQuukatzOepQI6k+HtfyAAjn4UEqnMaXTP1uwLldVAgMBAAECggEAHq2f8MqpYjLiAFZKl9IUs3uFZkEiZsgx9BmbMAb91Aec+WWJG4OLHrNVTG1KWp+IcaQablEa9bBvoToQnS7y5OpOon1d066egg7Ymfmv24NEMM5KRpktCNcOSA0CySpPIB6yrg6EiUr3ixiaFUGABKkxmwgVz/Q15IqM0ZMmCUsC174PMAz1COFZxD0ZX0zgHblOJQW3dc0X3XSzhht8vU02SMoVObQHQfeXEHv3K/RiVj/Ax0bTc5JVkT8dm8xksTtsFCNOzRBqFS6MYqX6U/u0Onz3Jm5Jt7fLWb5n97gZR4SleyGrqxYNb46d9X7mP0ie7E6bzFW0DsWBIeAqVQKBgQDW0We2L1n44yOvJaMs3evpj0nps13jWidt2I3RlZXjWzWHiYQfvhWUWqps/xZBnAYgnN/38xbKzHZeRNhrqOo+VB0WK1IYl0lZVE4l6TNKCsLsUfQzsb1pePkd1eRZA+TSqsi+I/IOQlQU7HA0bMrah/5FYyUBP0jYvCOvYTlZuwKBgQCvkcVRydVlzjUgv7lY5lYvT8IHV5iYO4Qkk2q6Wjv9VUKAJZauurMdiy05PboWfs5kbETdwFybXMBcknIvZO4ihxmwL8mcoNwDVZHI4bXapIKMTCyHgUKvJ9SeTcKGC7ZuQJ8mslRmYox/HloTOXEJgQgPRxXcwa3amzvdZI+6LwKBgQCLsnQqgxKUi0m6bdR2qf7vzTH4258z6X34rjpT0F5AEyF1edVFOz0XU/q+lQhpNEi7zqjLuvbYfSyA026WXKuwSsz7jMJ/oWqev/duKgAjp2npesY/E9gkjfobD+zGgoS9BzkyhXe1FCdP0A6L2S/1+zg88WOwMvJxl6/xLl24XwKBgCm60xSajX8yIQyUpWBM9yUtpueJ2Xotgz4ST+bVNbcEAddll8gWFiaqgug9FLLuFu5lkYTHiPtgc1RNdphvO+62/9MRuLDixwh/2TPO+iNqwKDKJjda8Nei9vVddCPaOtU/xNQ0xLzFJbG9LBmvqH9izOCcu8SJwGHaTcNUeJj/AoGADCJ26cY30c13F/8awAAmFYpZWCuTP5ppTsRmjd63ixlrqgkeLGpJ7kYb5fXkcTycRGYgP0e1kssBGcmE7DuG955fx3ZJESX3GQZ+XfMHvYGONwF1EiK1f0p6+GReC2VlQ7PIkoD9o0hojM6SnWvv9EXNjCPALEbfPFFvcniKVsE=', + pubKey: 'CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCTU3gVDv3SRXLOsFln9GEf1nJ/uCEDhOG10eC0H9l9IPpVxjuPT1ep+ykFUdvefq3D3q+W3hbmiHm81o8dYv26RxZIEioToUWp7Ec5M2B/niYoE93za9/ZDwJdl7eh2hNKwAdxTmdbXUPjkIU4vLyHKRFbJIn9X8w9djldz8hoUvC1BK4L1XrT6F2l0ruJXErH2ZwI1youfSzo87TdXIoFKdrQLuW6hOtDCGKTiS+ab/DkMODc6zl8N47Oczv7vjzoWOJMUJs1Pg0ZsD1zmISY38P0y/QyEhatZn0B8BmSWxlLQuukatzOepQI6k+HtfyAAjn4UEqnMaXTP1uwLldVAgMBAAE=' +}, { + id: 'QmZqCdSzgpsmB3Qweb9s4fojAoqELWzqku21UVrqtVSKi4', + privKey: 'CAASpgkwggSiAgEAAoIBAQCdbSEsTmw7lp5HagRcx57DaLiSUEkh4iBcKc7Y+jHICEIA8NIVi9FlfGEZj9G21FpiTR4Cy+BLVEuf8Nm90bym4iV+cSumeS21fvD8xGTEbeKGljs6OYHy3M45JhWF85gqHQJOqZufI2NRDuRgMZEO2+qGEXmSlv9mMXba/+9ecze8nSpB7bG2Z2pnKDeYwhF9Cz+ElMyn7TBWDjJERGVgFbTpdM3rBnbhB/TGpvs732QqZmIBlxnDb/Jn0l1gNZCgkEDcJ/0NDMBJTQ8vbvcdmaw3eaMPLkn1ix4wdu9QWCA0IBtuY1R7vSUtf4irnLJG7DnAw2GfM5QrF3xF1GLXAgMBAAECggEAQ1N0qHoxl5pmvqv8iaFlqLSUmx5y6GbI6CGJMQpvV9kQQU68yjItr3VuIXx8d/CBZyEMAK4oko7OeOyMcr3MLKLy3gyQWnXgsopDjhZ/8fH8uwps8g2+IZuFJrO+6LaxEPGvFu06fOiphPUVfn40R2KN/iBjGeox+AaXijmCqaV2vEdNJJPpMfz6VKZBDLTrbiqvo/3GN1U99PUqfPWpOWR29oAhh/Au6blSqvqTUPXB2+D/X6e1JXv31mxMPK68atDHSUjZWKB9lE4FMK1bkSKJRbyXmNIlbZ9V8X4/0r8/6T7JnW7ZT8ugRkquohmwgG7KkDXB1YsOCKXYUqzVYQKBgQDtnopFXWYl7XUyePJ/2MA5i7eoko9jmF44L31irqmHc5unNf6JlNBjlxTNx3WyfzhUzrn3c18psnGkqtow0tkBj5hmqn8/WaPbc5UA/5R1FNaNf8W5khn7MDm6KtYRPjN9djqTDiVHyC6ljONYd+5S+MqyKVWZ3t/xvG60sw85qwKBgQCpmpDtL+2JBwkfeUr3LyDcQxvbfzcv8lXj2otopWxWiLiZF1HzcqgAa2CIwu9kCGEt9Zr+9E4uINbe1To0b01/FhvR6xKO/ukceGA/mBB3vsKDcRmvpBUp+3SmnhY0nOk+ArQl4DhJ34k8pDM3EDPrixPf8SfVdU/8IM32lsdHhQKBgHLgpvCKCwxjFLnmBzcPzz8C8TOqR3BbBZIcQ34l+wflOGdKj1hsfaLoM8KYn6pAHzfBCd88A9Hg11hI0VuxVACRL5jS7NnvuGwsIOluppNEE8Ys86aXn7/0vLPoab3EWJhbRE48FIHzobmft3nZ4XpzlWs02JGfUp1IAC2UM9QpAoGAeWy3pZhSr2/iEC5+hUmwdQF2yEbj8+fDpkWo2VrVnX506uXPPkQwE1zM2Bz31t5I9OaJ+U5fSpcoPpDaAwBMs1fYwwlRWB8YNdHY1q6/23svN3uZsC4BGPV2JnO34iMUudilsRg+NGVdk5TbNejbwx7nM8Urh59djFzQGGMKeSECgYA0QMCARPpdMY50Mf2xQaCP7HfMJhESSPaBq9V3xY6ToEOEnXgAR5pNjnU85wnspHp+82r5XrKfEQlFxGpj2YA4DRRmn239sjDa29qP42UNAFg1+C3OvXTht1d5oOabaGhU0udwKmkEKUbb0bG5xPQJ5qeSJ5T1gLzLk3SIP0GlSw==', + pubKey: 'CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCdbSEsTmw7lp5HagRcx57DaLiSUEkh4iBcKc7Y+jHICEIA8NIVi9FlfGEZj9G21FpiTR4Cy+BLVEuf8Nm90bym4iV+cSumeS21fvD8xGTEbeKGljs6OYHy3M45JhWF85gqHQJOqZufI2NRDuRgMZEO2+qGEXmSlv9mMXba/+9ecze8nSpB7bG2Z2pnKDeYwhF9Cz+ElMyn7TBWDjJERGVgFbTpdM3rBnbhB/TGpvs732QqZmIBlxnDb/Jn0l1gNZCgkEDcJ/0NDMBJTQ8vbvcdmaw3eaMPLkn1ix4wdu9QWCA0IBtuY1R7vSUtf4irnLJG7DnAw2GfM5QrF3xF1GLXAgMBAAE=' +}, { + id: 'QmR5VwgsL7jyfZHAGyp66tguVrQhCRQuRc3NokocsCZ3fA', + privKey: 'CAASpwkwggSjAgEAAoIBAQCGXYU+uc2nn1zuJhfdFOl34upztnrD1gpHu58ousgHdGlGgYgbqLBAvIAauXdEL0+e30HofjA634SQxE+9nV+0FQBam1DDzHQlXsuwHV+2SKvSDkk4bVllMFpu2SJtts6VH+OXC/2ANJOm+eTALykQPYXgLIBxrhp/eD+Jz5r6wW2nq3k6OmYyK/4pgGzFjo5UyX+fa/171AJ68UPboFpDy6BZCcUjS0ondxPvD7cv5jMNqqMKIB/7rpi8n+Q3oeccRqVL56wH+FE3/QLjwYHwY6ILNRyvNXRqHjwBEXB2R5moXN0AFUWTw9rt3KhFiEjR1U81BTw5/xS7W2Iu0FgZAgMBAAECggEAS64HK8JZfE09eYGJNWPe8ECmD1C7quw21BpwVe+GVPSTizvQHswPohbKDMNj0srXDMPxCnNw1OgqcaOwyjsGuZaOoXoTroTM8nOHRIX27+PUqzaStS6aCG2IsiCozKUHjGTuupftS7XRaF4eIsUtWtFcQ1ytZ9pJYHypRQTi5NMSrTze5ThjnWxtHilK7gnBXik+aR0mYEVfSn13czQEC4rMOs+b9RAc/iibDNoLopfIdvmCCvfxzmySnR7Cu1iSUAONkir7PB+2Mt/qRFCH6P+jMamtCgQ8AmifXgVmDUlun+4MnKg3KrPd6ZjOEKhVe9mCHtGozk65RDREShfDdQKBgQDi+x2MuRa9peEMOHnOyXTS+v+MFcfmG0InsO08rFNBKZChLB+c9UHBdIvexpfBHigSyERfuDye4z6lxi8ZnierWMYJP30nxmrnxwTGTk1MQquhfs1A0kpmDnPsjlOS/drEIEIssNx2WbfJ7YtMxLWBtp+BJzGpQmr0LKC+NHRSrwKBgQCXiy2kJESIUkIs2ihV55hhT6/bZo1B1O5DPA2nkjOBXqXF6fvijzMDX82JjLd07lQZlI0n1Q/Hw0p4iYi9YVd2bLkLXF5UIb2qOeHj76enVFOrPHUSkC9Y2g/0Xs+60Ths2xRd8RrrfQU3kl5iVpBywkCIrb2M5+wRnNTk1W3TtwKBgQCvplyrteAfSurpJhs9JzE8w/hWU9SqAZYkWQp91W1oE95Um2yrbjBAoQxMjaqKS+f/APPIjy56Vqj4aHGyhW11b/Fw3qzfxvCcBKtxOs8eoMlo5FO6QgJJEA4tlcafDcvp0nzjUMqK28safLU7503+33B35fjMXxWdd5u9FaKfCQKBgC4W6j6tuRosymuRvgrCcRnHfpify/5loEFallyMnpWOD6Tt0OnK25z/GifnYDRz96gAAh5HMpFy18dpLOlMHamqz2yhHx8/U8vd5tHIJZlCkF/X91M5/uxrBccwvsT2tM6Got8fYSyVzWxlW8dUxIHiinYHQUsFjkqdBDLEpq5pAoGASoTw5RBEWFM0GuAZdXsyNyxU+4S+grkTS7WdW/Ymkukh+bJZbnvF9a6MkSehqXnknthmufonds2AFNS//63gixENsoOhzT5+2cdfc6tJECvJ9xXVXkf85AoQ6T/RrXF0W4m9yQyCngNJUrKUOIH3oDIfdZITlYzOC3u1ojj7VuQ=', + pubKey: 'CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCGXYU+uc2nn1zuJhfdFOl34upztnrD1gpHu58ousgHdGlGgYgbqLBAvIAauXdEL0+e30HofjA634SQxE+9nV+0FQBam1DDzHQlXsuwHV+2SKvSDkk4bVllMFpu2SJtts6VH+OXC/2ANJOm+eTALykQPYXgLIBxrhp/eD+Jz5r6wW2nq3k6OmYyK/4pgGzFjo5UyX+fa/171AJ68UPboFpDy6BZCcUjS0ondxPvD7cv5jMNqqMKIB/7rpi8n+Q3oeccRqVL56wH+FE3/QLjwYHwY6ILNRyvNXRqHjwBEXB2R5moXN0AFUWTw9rt3KhFiEjR1U81BTw5/xS7W2Iu0FgZAgMBAAE=' +}, { + id: 'QmScLDqRg7H6ipCYxm9fVk152UWavQFKscTdoT4YNHxgqp', + privKey: 'CAASpwkwggSjAgEAAoIBAQCWEHaTZ6LBLFP5OPrUqjDM/cF4b2zrfh1Zm3kd02ZtgQB3iYtZqRPJT5ctT3A7WdVF/7dCxPGOCkJlLekTx4Y4gD8JtjA+EfN9fR/2RBKbti2N3CD4vkGp9ss4hbBFcXIhl8zuD/ELHutbV6b8b4QXJGnxfp/B+1kNPnyd7SJznS0QyvI8OLI1nAkVKdYLDRW8kPKeHyx1xhdNDuTQVTFyAjRGQ4e3UYFB7bYIHW3E6kCtCoJDlj+JPC02Yt1LHzIzZVLvPvNFnYY2mag6OiGFuh/oMBIqvnPc1zRZ3eLUqeGZjQVaoR0kdgZUKz7Q2TBeNldxK/s6XO0DnkQTlelNAgMBAAECggEAdmt1dyswR2p4tdIeNpY7Pnj9JNIhTNDPznefI0dArCdBvBMhkVaYk6MoNIxcj6l7YOrDroAF8sXr0TZimMY6B/pERKCt/z1hPWTxRQBBAvnHhwvwRPq2jK6BfhAZoyM8IoBNKowP9mum5QUNdGV4Al8s73KyFX0IsCfgZSvNpRdlt+DzPh+hu/CyoZaMpRchJc1UmK8Fyk3KfO+m0DZNfHP5P08lXNfM6MZLgTJVVgERHyG+vBOzTd2RElMe19nVCzHwb3dPPRZSQ7Fnz3rA+GeLqsM2Zi4HNhfbD1OcD9C4wDj5tYL6hWTkdz4IlfVcjCeUHxgIOhdDV2K+OwbuAQKBgQD0FjUZ09UW2FQ/fitbvIB5f1SkXWPxTF9l6mAeuXhoGv2EtQUO4vq/PK6N08RjrZdWQy6UsqHgffi7lVQ8o3hvCKdbtf4sP+cM92OrY0WZV89os79ndj4tyvmnP8WojwRjt/2XEfgdoWcgWxW9DiYINTOQVimZX+X/3on4s8hEgQKBgQCdY3kOMbyQeLTRkqHXjVTY4ddO+v4S4wOUa1l4rTqAbq1W3JYWwoDQgFuIu3limIHmjnSJpCD4EioXFsM7p6csenoc20sHxsaHnJ6Mn5Te41UYmY9EW0otkQ0C3KbXM0hwQkjyplnEmZawGKmjEHW8DJ3vRYTv9TUCgYKxDHgOzQKBgB4A/NYH7BG61eBYKgxEx6YnuMfbkwV+Vdu5S8d7FQn3B2LgvZZu4FPRqcNVXLbEB+5ao8czjiKCWaj1Wj15+rvrXGcxn+Tglg5J+r5+nXeUC7LbJZQaPNp0MOwWMr3dlrSLUWjYlJ9Pz9VyXOG4c4Rexc/gR4zK9QLW4C7qKpwBAoGAZzyUb0cYlPtYQA+asTU3bnvVKy1f8yuNcZFowst+EDiI4u0WVh+HNzy6zdmLKa03p+/RaWeLaK0hhrubnEnAUmCUMNF3ScaM+u804LDcicc8TkKLwx7ObU0z56isl4RAA8K27tNHFrpYKXJD834cfBkaj5ReOrfw6Y/iFhhDuBECgYEA8gbC76uz7LSHhW30DSRTcqOzTyoe2oYKQaxuxYNp7vSSOkcdRen+mrdflDvud2q/zN2QdL4pgqdldHlR35M/lJ0f0B6zp74jlzbO9700wzsOqreezGc5eWiroDL100U9uIZ50BKb8CKtixIHpinUSPIUcVDkSAZ2y7mbfCxQwqQ=', + pubKey: 'CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCWEHaTZ6LBLFP5OPrUqjDM/cF4b2zrfh1Zm3kd02ZtgQB3iYtZqRPJT5ctT3A7WdVF/7dCxPGOCkJlLekTx4Y4gD8JtjA+EfN9fR/2RBKbti2N3CD4vkGp9ss4hbBFcXIhl8zuD/ELHutbV6b8b4QXJGnxfp/B+1kNPnyd7SJznS0QyvI8OLI1nAkVKdYLDRW8kPKeHyx1xhdNDuTQVTFyAjRGQ4e3UYFB7bYIHW3E6kCtCoJDlj+JPC02Yt1LHzIzZVLvPvNFnYY2mag6OiGFuh/oMBIqvnPc1zRZ3eLUqeGZjQVaoR0kdgZUKz7Q2TBeNldxK/s6XO0DnkQTlelNAgMBAAE=' +}, { + id: 'QmckxVrJw1Yo8LqvmDJNUmdAsKtSbiKWmrXJFyKmUraBoN', + privKey: 'CAASpwkwggSjAgEAAoIBAQC1/GFud/7xutux7qRfMj1sIdMRh99/chR6HqVj6LQqrgk4jil0mdN/LCk/tqPqmDtObHdmEhCoybzuhLbCKgUqryKDwO6yBJHSKWY9QqrKZtLJ37SgKwGjE3+NUD4r1dJHhtQrICFdOdSCBzs/v8gi+J+KZLHo7+Nms4z09ysy7qZh94Pd7cW4gmSMergqUeANLD9C0ERw1NXolswOW7Bi7UGr7yuBxejICLO3nkxe0OtpQBrYrqdCD9vs3t/HQZbPWVoiRj4VO7fxkAPKLl30HzcIfxj/ayg8NHcH59d08D+N2v5Sdh28gsiYKIPE9CXvuw//HUY2WVRY5fDC5JglAgMBAAECggEBAKb5aN/1w3pBqz/HqRMbQpYLNuD33M3PexBNPAy+P0iFpDo63bh5Rz+A4lvuFNmzUX70MFz7qENlzi6+n/zolxMB29YtWBUH8k904rTEjXXl//NviQgITZk106tx+4k2x5gPEm57LYGfBOdFAUzNhzDnE2LkXwRNzkS161f7zKwOEsaGWRscj6UvhO4MIFxjb32CVwt5eK4yOVqtyMs9u30K4Og+AZYTlhtm+bHg6ndCCBO6CQurCQ3jD6YOkT+L3MotKqt1kORpvzIB0ujZRf49Um8wlcjC5G9aexBeGriXaVdPF62zm7GA7RMsbQM/6aRbA1fEQXvJhHUNF9UFeaECgYEA8wCjKqQA7UQnHjRwTsktdwG6szfxd7z+5MTqHHTWhWzgcQLgdh5/dO/zanEoOThadMk5C1Bqjq96gH2xim8dg5XQofSVtV3Ui0dDa+XRB3E3fyY4D3RF5hHv85O0GcvQc6DIb+Ja1oOhvHowFB1C+CT3yEgwzX/EK9xpe+KtYAkCgYEAv7hCnj/DcZFU3fAfS+unBLuVoVJT/drxv66P686s7J8UM6tW+39yDBZ1IcwY9vHFepBvxY2fFfEeLI02QFM+lZXVhNGzFkP90agNHK01psGgrmIufl9zAo8WOKgkLgbYbSHzkkDeqyjEPU+B0QSsZOCE+qLCHSdsnTmo/TjQhj0CgYAz1+j3yfGgrS+jVBC53lXi0+2fGspbf2jqKdDArXSvFqFzuudki/EpY6AND4NDYfB6hguzjD6PnoSGMUrVfAtR7X6LbwEZpqEX7eZGeMt1yQPMDr1bHrVi9mS5FMQR1NfuM1lP9Xzn00GIUpE7WVrWUhzDEBPJY/7YVLf0hFH08QKBgDWBRQZJIVBmkNrHktRrVddaSq4U/d/Q5LrsCrpymYwH8WliHgpeTQPWmKXwAd+ZJdXIzYjCt202N4eTeVqGYOb6Q/anV2WVYBbM4avpIxoA28kPGY6nML+8EyWIt2ApBOmgGgvtEreNzwaVU9NzjHEyv6n7FlVwlT1jxCe3XWq5AoGASYPKQoPeDlW+NmRG7z9EJXJRPVtmLL40fmGgtju9QIjLnjuK8XaczjAWT+ySI93Whu+Eujf2Uj7Q+NfUjvAEzJgwzuOd3jlQvoALq11kuaxlNQTn7rx0A1QhBgUJE8AkvShPC9FEnA4j/CLJU0re9H/8VvyN6qE0Mho0+YbjpP8=', + pubKey: 'CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC1/GFud/7xutux7qRfMj1sIdMRh99/chR6HqVj6LQqrgk4jil0mdN/LCk/tqPqmDtObHdmEhCoybzuhLbCKgUqryKDwO6yBJHSKWY9QqrKZtLJ37SgKwGjE3+NUD4r1dJHhtQrICFdOdSCBzs/v8gi+J+KZLHo7+Nms4z09ysy7qZh94Pd7cW4gmSMergqUeANLD9C0ERw1NXolswOW7Bi7UGr7yuBxejICLO3nkxe0OtpQBrYrqdCD9vs3t/HQZbPWVoiRj4VO7fxkAPKLl30HzcIfxj/ayg8NHcH59d08D+N2v5Sdh28gsiYKIPE9CXvuw//HUY2WVRY5fDC5JglAgMBAAE=' +}] diff --git a/test/fixtures/test-data/test-id.json b/test/fixtures/test-data/test-id.json deleted file mode 100644 index 0b37a6cc..00000000 --- a/test/fixtures/test-data/test-id.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "id": "QmaG17D4kfTB2RNUCr16bSfVvUVt2Xn3rPYeqQDvnVcXFr", - "privKey": "CAASqAkwggSkAgEAAoIBAQDBpXRrSLoVhP8C4YI0nm+YTb7UIe+xT9dwaMzKcGsH2zzz1lfxl54e1XNO+6Ut+If5jswpydgHhn9nGPod53sUIR2m+BiHOAH/Blgfa1nUKUkspts1MH3z5ZaO6Xo336Y0Uaw7UqfeIzKliTM6bpev2XIHyu0v/VJ2mylzfbDLMWqZs/shE3xwCJCD/PxoVpTlr/SzzF7MdgDMxkyiC3iLZ5Zkm+baPbi3mpKM0ue25Thedcc0KFjhQrjBfy5FPamrsMn5fnnoHwnQl9u7UWigzeC+7X+38EML1sVrV37ExxHPtM6882Ivjc7VN6zFHOHD2c9eVfbShkIf8YkVQUcFAgMBAAECggEAVE1mgGo58LJknml0WNn8tS5rfEiF5AhhPyOwvBTy04nDYFgZEykxgjTkrSbqgzfmYmOjSDICJUyNXGHISYqDz4CXOyBY9U0RuWeWp58BjVan75N4bRB+VNbHk9HbDkYEQlSoCW9ze0aRfvVa4v5QdRLSDMhwN+stokrsYcX/WIWYTM2e2jW+qQOzS8SJl7wYsgtd3WikrxwXkRL3sCMHEcgcPhoKacoD5Yr9cB0IC5vzhu4t/WMa+N2UEndcKGAbXsh8kA7BPFM6lqnEpOHpWEVEAYasAwFGUvUN9GwhtqpaNNS2sG6Nrz95cC99Nqx58uIXcTAJm3Fh/WfKJ6I1xQKBgQD+g7A5OSWw+i/zhTKVPJg93/eohViL0dGZT9Tf0/VslsFl00FwnZmBKA6BJ6ZL3hD00OcqIL3m6EzZ4q38U97XZxf2OUsPPJtl+Avqtlk16AHRHB9I17LGXJ30xZRkxL665oLms0D2T4NIZZX/uVMoS18lRvCZj1aEYQFCrZYgowKBgQDCxtA695S0vl6E3Q4G6MrDZK+2JqjaGL0XTnpHWiAjnk2lnV2CCZnWpEHT+ebF2fWx5nYQo5sugc6bS+4k9jRNUgxf2sQieZYCBjbnjCEVrPTm/dPTkaw1CQ/ox5/R1/Elbw8vteF9uUAvR0FL8Ss1Dqw6B2SxdTowxMy6qQ7sNwKBgG2N3eMj2DeP2egm45kdliK8L2yYyX6V+HTXyjf2kuQFGIZuIvMIw7S2u1eY65ooon/fFEIsCdJFGB+J1X6R05BAzi2sh8StP+7qkKadi1UK4w1R352JS2jbIRrlmXSuw7LL2njXnBTqMQaOw7xp14O2vePb32EaNBGTd+ltsvulAoGBALGIc4370oA4MIDb2Ag2MXKNmJbnf+piuB/BOTVGEZtFlDKLUArR43W/+/xRgKX/97FyhVS/OxfV21Kzj9oCy0NasMrB5RojRraLoYnFsPZH0mWlIGlsEtG4c9bR9XtYX4WmR+pN1r04mCc/xGWK6b4PpK2zxXT2i9ad2pmctGxbAoGBAIcp0UML5QCqvLmcob2/6PCRaYAxJBb9lDqOHredMgQih2hGnHFCyKk9eBAbFf/KN0guJTBDaAJRclcxsLLn7rV6grMNt+0EUepm7tWT0z5j8gNGbGGhuGDdqcmfJTc2EMdQrfhzYDN3rL1v3l+Ujwla2khL2ozE7SQ/KVeA1saY", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDBpXRrSLoVhP8C4YI0nm+YTb7UIe+xT9dwaMzKcGsH2zzz1lfxl54e1XNO+6Ut+If5jswpydgHhn9nGPod53sUIR2m+BiHOAH/Blgfa1nUKUkspts1MH3z5ZaO6Xo336Y0Uaw7UqfeIzKliTM6bpev2XIHyu0v/VJ2mylzfbDLMWqZs/shE3xwCJCD/PxoVpTlr/SzzF7MdgDMxkyiC3iLZ5Zkm+baPbi3mpKM0ue25Thedcc0KFjhQrjBfy5FPamrsMn5fnnoHwnQl9u7UWigzeC+7X+38EML1sVrV37ExxHPtM6882Ivjc7VN6zFHOHD2c9eVfbShkIf8YkVQUcFAgMBAAE=" -} diff --git a/test/fixtures/test-peer.json b/test/fixtures/test-peer.json deleted file mode 100644 index ba789ca3..00000000 --- a/test/fixtures/test-peer.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "id": "Qmex1SSsueWFsUfjdkugJ5zhcnjddAt8TxcnDLUXKD9Sx7", - "privKey": "CAASqAkwggSkAgEAAoIBAQCXzV127CvVHOGMzvsn/U+/32JM58KA6k0FSCCeNFzNowiDS/vV5eezGN5AFoxsF6icWLoaczz7l9RdVD+I/t6PEt9X7XUdrDCtSS8WmAcCgvZWSSf7yAd3jT4GSZDUIgIEeRZsERDt/yVqTLwsZ1G9dMIeh8sbf2zwjTXZIWaRM6o4lq3DYFfzLvJUXlJodxPogU7l7nLkITPUv+yQAMcVHizbNwJvwiETKYeUj73/m/wEPAlnFESexDstxNiIwE/FH8Ao50QPZRO6E6Jb0hhYSI/4CLRdrzDFm/Vzplei3Wr2DokSROaNyeG37VAueyA+pDqn84um+L9uXLwbv5FbAgMBAAECggEAdBUzV/GaQ0nmoQrWvOnUxmFIho7kCjkh1NwnNVPNc+Msa1r7pcI9wJNPwap8j1w4L/cZuYhOJgcg+o2mWFiuULKZ4F9Ro/M89gZ038457g2/2pPu43c/Xoi/2YcAHXg0Gr+OCe2zCIyITBWKAFqyAzL6DubAxrJW2Ezj1LrZ+EZgMyzbh/go/eEGSJaaGkINeAkY144DqDWWWvzyhKhryipsGkZGEkVy9xJgMEI3ipVvuPez2XAvoyyeuinBBLe+Z2vY5G50XXzbIMhIQGLncHf9MwTv6wt1ilyOSLOXK0BoQbB76J3R3is5dSULXXP9r8VocjLBEkmBuf4FXAKzoQKBgQDNNS4F1XE1gxD8LPkL+aB/hi6eVHVPhr+w0I/9ATikcLGeUfBM2Gd6cZRPFtNVrv1p6ZF1D1UyGDknGbDBSQd9wLUgb0fDoo3jKYMGWq6G+VvaP5rzWQeBV8YV2EhSmUk1i6kiYe2ZE8WyrPie7iwpQIY60e2A8Ly0GKZiBZUcHQKBgQC9YDAVsGnEHFVFkTDpvw5HwEzCgTb2A3NgkGY3rTYZ7L6AFjqCYmUwFB8Fmbyc4kdFWNh8wfmq5Qrvl49NtaeukiqWKUUlB8uPdztB1P0IahA2ks0owStZlRifmwfgYyMd4xE17lhaOgQQJZZPxmP0F6mdOvb3YJafNURCdMS51wKBgEvvIM+h0tmFXXSjQ6kNvzlRMtD92ccKysYn9xAdMpOO6/r0wSH+dhQWEVZO0PcE4NsfReb2PIVj90ojtIdhebcr5xpQc1LORQjJJKXmSmzBux6AqNrhl+hhzXfp56FA/Zkly/lgGWaqrV5XqUxOP+Mn8EO1yNgMvRc7g94DyNB1AoGBAKLBuXHalXwDsdHBUB2Eo3xNLGt6bEcRfia+0+sEBdxQGQWylQScFkU09dh1YaIf44sZKa5HdBFJGpYCVxo9hmjFnK5Dt/Z0daHOonIY4INLzLVqg8KECoLKXkhGEIXsDjFQhukn+G1LMVTDSSU055DQiWjlVX4UWD9qo0jOXIkvAoGBAMP50p2X6PsWWZUuuR7i1JOJHRyQZPWdHh9p8SSLnCtEpHYZfJr4INXNmhnSiB/3TUnHix2vVKjosjMTCk/CjfzXV2H41WPOLZ2/Pi3SxCicWIRj4kCcWhkEuIF2jGkg1+jmNiCl/zNMaBOAIP3QbDPtqOWbYlPd2YIzdj6WQ6R4", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCXzV127CvVHOGMzvsn/U+/32JM58KA6k0FSCCeNFzNowiDS/vV5eezGN5AFoxsF6icWLoaczz7l9RdVD+I/t6PEt9X7XUdrDCtSS8WmAcCgvZWSSf7yAd3jT4GSZDUIgIEeRZsERDt/yVqTLwsZ1G9dMIeh8sbf2zwjTXZIWaRM6o4lq3DYFfzLvJUXlJodxPogU7l7nLkITPUv+yQAMcVHizbNwJvwiETKYeUj73/m/wEPAlnFESexDstxNiIwE/FH8Ao50QPZRO6E6Jb0hhYSI/4CLRdrzDFm/Vzplei3Wr2DokSROaNyeG37VAueyA+pDqn84um+L9uXLwbv5FbAgMBAAE=" -} diff --git a/test/fsm.spec.js b/test/fsm.spec.js deleted file mode 100644 index 63328509..00000000 --- a/test/fsm.spec.js +++ /dev/null @@ -1,168 +0,0 @@ -/* eslint-env mocha */ -'use strict' - -const chai = require('chai') -chai.use(require('dirty-chai')) -chai.use(require('chai-checkmark')) -const expect = chai.expect -const sinon = require('sinon') -const series = require('async/series') -const createNode = require('./utils/create-node') - -describe('libp2p state machine (fsm)', () => { - describe('starting and stopping', () => { - let node - beforeEach((done) => { - createNode([], { - config: { - dht: { - enabled: false - } - } - }, (err, _node) => { - node = _node - done(err) - }) - }) - afterEach(() => { - node.removeAllListeners() - sinon.restore() - }) - after((done) => { - node.stop(done) - node = null - }) - - it('should be able to start and stop several times', (done) => { - node.on('start', (err) => { - expect(err).to.not.exist().mark() - }) - node.on('stop', (err) => { - expect(err).to.not.exist().mark() - }) - - expect(4).checks(done) - - series([ - (cb) => node.start(cb), - (cb) => node.stop(cb), - (cb) => node.start(cb), - (cb) => node.stop(cb) - ], () => {}) - }) - - it('should noop when stopping a stopped node', (done) => { - node.once('start', node.stop) - node.once('stop', () => { - node.state.on('STOPPING', () => { - throw new Error('should not stop a stopped node') - }) - node.once('stop', done) - - // stop the stopped node - node.stop(() => {}) - }) - node.start(() => {}) - }) - - it('should callback with an error when it occurs on stop', (done) => { - const error = new Error('some error starting') - node.once('start', () => { - node.once('error', (err) => { - expect(err).to.eql(error).mark() - }) - node.stop((err) => { - expect(err).to.eql(error).mark() - }) - }) - - expect(2).checks(done) - - sinon.stub(node._switch, 'stop').callsArgWith(0, error) - node.start(() => {}) - }) - - it('should noop when starting a started node', (done) => { - node.once('start', () => { - node.state.on('STARTING', () => { - throw new Error('should not start a started node') - }) - node.once('start', () => { - node.once('stop', done) - node.stop(() => {}) - }) - - // start the started node - node.start(() => {}) - }) - node.start(() => {}) - }) - - it('should error on start with no transports', (done) => { - const transports = node._modules.transport - node._modules.transport = null - - node.on('stop', () => { - node._modules.transport = transports - expect(node._modules.transport).to.exist().mark() - }) - node.on('error', (err) => { - expect(err).to.exist().mark() - }) - node.on('start', () => { - throw new Error('should not start') - }) - - expect(2).checks(done) - - node.start(() => {}) - }) - - it('should not start if the switch fails to start', (done) => { - const error = new Error('switch didnt start') - const stub = sinon.stub(node._switch, 'start') - .callsArgWith(0, error) - - node.on('stop', () => { - expect(stub.calledOnce).to.eql(true).mark() - stub.restore() - }) - node.on('error', (err) => { - expect(err).to.eql(error).mark() - }) - node.on('start', () => { - throw new Error('should not start') - }) - - expect(3).checks(done) - - node.start((err) => { - expect(err).to.eql(error).mark() - }) - }) - - it('should not dial when the node is stopped', (done) => { - node.on('stop', () => { - node.dial(null, (err) => { - expect(err).to.exist() - expect(err.code).to.eql('ERR_NODE_NOT_STARTED') - done() - }) - }) - - node.stop(() => {}) - }) - - it('should not dial (fsm) when the node is stopped', (done) => { - node.on('stop', () => { - node.dialFSM(null, null, (err) => { - expect(err).to.exist() - expect(err.code).to.eql('ERR_NODE_NOT_STARTED') - done() - }) - }) - - node.stop(() => {}) - }) - }) -}) diff --git a/test/get-peer-info.spec.js b/test/get-peer-info.spec.js deleted file mode 100644 index e550cb46..00000000 --- a/test/get-peer-info.spec.js +++ /dev/null @@ -1,134 +0,0 @@ -/* eslint-env mocha */ -'use strict' - -const chai = require('chai') -chai.use(require('dirty-chai')) -const expect = chai.expect -const PeerBook = require('peer-book') -const PeerInfo = require('peer-info') -const PeerId = require('peer-id') -const MultiAddr = require('multiaddr') -const TestPeerInfos = require('./switch/test-data/ids.json').infos - -const { getPeerInfo, getPeerInfoRemote } = require('../src/get-peer-info') - -describe('Get Peer Info', () => { - describe('getPeerInfo', () => { - let peerBook - let peerInfoA - let multiaddrA - let peerIdA - - before((done) => { - peerBook = new PeerBook() - PeerId.createFromJSON(TestPeerInfos[0].id, (err, id) => { - peerIdA = id - peerInfoA = new PeerInfo(peerIdA) - multiaddrA = MultiAddr('/ipfs/QmdWYwTywvXBeLKWthrVNjkq9SafEDn1PbAZdz4xZW7Jd9') - peerInfoA.multiaddrs.add(multiaddrA) - peerBook.put(peerInfoA) - done(err) - }) - }) - - it('should be able get peer info from multiaddr', () => { - const _peerInfo = getPeerInfo(multiaddrA, peerBook) - expect(peerBook.has(_peerInfo)).to.equal(true) - expect(peerInfoA).to.deep.equal(_peerInfo) - }) - - it('should return a new PeerInfo with a multiAddr not in the PeerBook', () => { - const wrongMultiAddr = MultiAddr('/ipfs/QmckZzdVd72h9QUFuJJpQqhsZqGLwjhh81qSvZ9BhB2FQi') - const _peerInfo = getPeerInfo(wrongMultiAddr, peerBook) - expect(PeerInfo.isPeerInfo(_peerInfo)).to.equal(true) - }) - - it('should be able get peer info from peer id', () => { - const _peerInfo = getPeerInfo(multiaddrA, peerBook) - expect(peerBook.has(_peerInfo)).to.equal(true) - expect(peerInfoA).to.deep.equal(_peerInfo) - }) - - it('should add a peerInfo to the book', (done) => { - PeerId.createFromJSON(TestPeerInfos[1].id, (err, id) => { - const peerInfo = new PeerInfo(id) - expect(peerBook.has(peerInfo.id.toB58String())).to.eql(false) - - expect(getPeerInfo(peerInfo, peerBook)).to.exist() - expect(peerBook.has(peerInfo.id.toB58String())).to.eql(true) - done(err) - }) - }) - - it('should return the most up to date version of the peer', (done) => { - const ma1 = MultiAddr('/ip4/0.0.0.0/tcp/8080') - const ma2 = MultiAddr('/ip6/::/tcp/8080') - PeerId.createFromJSON(TestPeerInfos[1].id, (err, id) => { - const peerInfo = new PeerInfo(id) - peerInfo.multiaddrs.add(ma1) - expect(getPeerInfo(peerInfo, peerBook)).to.exist() - - const peerInfo2 = new PeerInfo(id) - peerInfo2.multiaddrs.add(ma2) - const returnedPeerInfo = getPeerInfo(peerInfo2, peerBook) - expect(returnedPeerInfo.multiaddrs.toArray()).to.contain.members([ - ma1, ma2 - ]) - done(err) - }) - }) - - it('an invalid peer type should throw an error', () => { - let error - try { - getPeerInfo('/ip4/127.0.0.1/tcp/1234', peerBook) - } catch (err) { - error = err - } - - expect(error.code).to.eql('ERR_INVALID_MULTIADDR') - }) - }) - - describe('getPeerInfoRemote', () => { - it('should callback with error for invalid string multiaddr', async () => { - let error - try { - await getPeerInfoRemote('INVALID MULTIADDR') - } catch (err) { - error = err - } - - expect(error.code).to.eql('ERR_INVALID_PEER_TYPE') - }) - - it('should callback with error for invalid non-peer multiaddr', async () => { - let error - try { - await getPeerInfoRemote('/ip4/8.8.8.8/tcp/1080') - } catch (err) { - error = err - } - - expect(error.code).to.eql('ERR_INVALID_PEER_TYPE') - }) - - it('should callback with error for invalid non-peer multiaddr', async () => { - let error - try { - await getPeerInfoRemote(undefined) - } catch (err) { - error = err - } - - expect(error.code).to.eql('ERR_INVALID_PEER_TYPE') - }) - - it('should callback with error for invalid non-peer multiaddr (promise)', () => { - return getPeerInfoRemote(undefined) - .then(expect.fail, (err) => { - expect(err.code).to.eql('ERR_INVALID_PEER_TYPE') - }) - }) - }) -}) diff --git a/test/identify/basic.spec.js b/test/identify/basic.spec.js deleted file mode 100644 index 6fb95dbf..00000000 --- a/test/identify/basic.spec.js +++ /dev/null @@ -1,15 +0,0 @@ -/* eslint-env mocha */ -'use strict' - -const chai = require('chai') -const dirtyChai = require('dirty-chai') -const expect = chai.expect -chai.use(dirtyChai) - -const identify = require('../../src/identify') - -describe('basic', () => { - it('multicodec', () => { - expect(identify.multicodec).to.eql('/ipfs/id/1.0.0') - }) -}) diff --git a/test/identify/dialer.spec.js b/test/identify/dialer.spec.js deleted file mode 100644 index 31bd036e..00000000 --- a/test/identify/dialer.spec.js +++ /dev/null @@ -1,192 +0,0 @@ -/* eslint-env mocha */ -'use strict' - -const pull = require('pull-stream/pull') -const values = require('pull-stream/sources/values') -const chai = require('chai') -const dirtyChai = require('dirty-chai') -const expect = chai.expect -chai.use(dirtyChai) -const pair = require('pull-pair/duplex') -const PeerInfo = require('peer-info') -const lp = require('pull-length-prefixed') -const multiaddr = require('multiaddr') - -const identify = require('../../src/identify') -const msg = identify.message - -describe('identify.dialer', () => { - let original - - before(function (done) { - this.timeout(20 * 1000) - - PeerInfo.create((err, info) => { - if (err) { - return done(err) - } - - original = info - done() - }) - }) - - afterEach(() => { - original.multiaddrs.clear() - original.protocols.clear() - }) - - it('works', (done) => { - const p = pair() - original.multiaddrs.add(multiaddr('/ip4/127.0.0.1/tcp/5002')) - original.protocols.add('/echo/1.0.0') - original.protocols.add('/ping/1.0.0') - - const input = msg.encode({ - protocolVersion: 'ipfs/0.1.0', - agentVersion: 'na', - publicKey: original.id.pubKey.bytes, - listenAddrs: [multiaddr('/ip4/127.0.0.1/tcp/5002').buffer], - observedAddr: multiaddr('/ip4/127.0.0.1/tcp/5001').buffer, - protocols: Array.from(original.protocols) - }) - - pull( - values([input]), - lp.encode(), - p[0] - ) - - identify.dialer(p[1], (err, info, observedAddrs) => { - expect(err).to.not.exist() - expect(info.id.pubKey.bytes) - .to.eql(original.id.pubKey.bytes) - - expect(info.multiaddrs.has(original.multiaddrs.toArray()[0])) - .to.eql(true) - - expect(multiaddr('/ip4/127.0.0.1/tcp/5001').equals(observedAddrs[0])) - .to.eql(true) - - expect(info.protocols).to.eql(original.protocols) - - done() - }) - }) - - it('should handle missing protocols', (done) => { - const p = pair() - original.multiaddrs.add(multiaddr('/ip4/127.0.0.1/tcp/5002')) - - const input = msg.encode({ - protocolVersion: 'ipfs/0.1.0', - agentVersion: 'na', - publicKey: original.id.pubKey.bytes, - listenAddrs: [multiaddr('/ip4/127.0.0.1/tcp/5002').buffer], - observedAddr: multiaddr('/ip4/127.0.0.1/tcp/5001').buffer, - protocols: Array.from(original.protocols) - }) - - pull( - values([input]), - lp.encode(), - p[0] - ) - - identify.dialer(p[1], (err, info, observedAddrs) => { - expect(err).to.not.exist() - expect(info.id.pubKey.bytes) - .to.eql(original.id.pubKey.bytes) - - expect(info.multiaddrs.has(original.multiaddrs.toArray()[0])) - .to.eql(true) - - expect(multiaddr('/ip4/127.0.0.1/tcp/5001').equals(observedAddrs[0])) - .to.eql(true) - - expect(Array.from(info.protocols)).to.eql([]) - - done() - }) - }) - - it('does not crash with invalid listen addresses', (done) => { - const p = pair() - original.multiaddrs.add(multiaddr('/ip4/127.0.0.1/tcp/5002')) - const input = msg.encode({ - protocolVersion: 'ipfs/0.1.0', - agentVersion: 'na', - publicKey: original.id.pubKey.bytes, - listenAddrs: [Buffer.from('ffac010203')], - observedAddr: Buffer.from('ffac010203') - }) - - pull( - values([input]), - lp.encode(), - p[0] - ) - - identify.dialer(p[1], (err, info, observedAddrs) => { - expect(err).to.exist() - - done() - }) - }) - - it('does not crash with invalid observed address', (done) => { - const p = pair() - original.multiaddrs.add(multiaddr('/ip4/127.0.0.1/tcp/5002')) - const input = msg.encode({ - protocolVersion: 'ipfs/0.1.0', - agentVersion: 'na', - publicKey: original.id.pubKey.bytes, - listenAddrs: [multiaddr('/ip4/127.0.0.1/tcp/5002').buffer], - observedAddr: Buffer.from('ffac010203') - }) - - pull( - values([input]), - lp.encode(), - p[0] - ) - - identify.dialer(p[1], (err, info, observedAddrs) => { - expect(err).to.exist() - - done() - }) - }) - - it('should return an error with mismatched peerInfo data', function (done) { - this.timeout(10e3) - - const p = pair() - original.multiaddrs.add(multiaddr('/ip4/127.0.0.1/tcp/5002')) - const input = msg.encode({ - protocolVersion: 'ipfs/0.1.0', - agentVersion: 'na', - publicKey: original.id.pubKey.bytes, - listenAddrs: [multiaddr('/ip4/127.0.0.1/tcp/5002').buffer], - observedAddr: multiaddr('/ip4/127.0.0.1/tcp/5001').buffer - }) - - PeerInfo.create((err, info) => { - if (err) { - return done(err) - } - - pull( - values([input]), - lp.encode(), - p[0] - ) - - identify.dialer(p[1], info, (err, peerInfo) => { - expect(err).to.exist() - expect(peerInfo).to.not.exist() - done() - }) - }) - }) -}) diff --git a/test/identify/listener.spec.js b/test/identify/listener.spec.js deleted file mode 100644 index ac4b3abc..00000000 --- a/test/identify/listener.spec.js +++ /dev/null @@ -1,70 +0,0 @@ -/* eslint-env mocha */ -'use strict' - -const pull = require('pull-stream/pull') -const collect = require('pull-stream/sinks/collect') -const chai = require('chai') -const dirtyChai = require('dirty-chai') -const expect = chai.expect -chai.use(dirtyChai) -const pair = require('pull-pair/duplex') -const PeerInfo = require('peer-info') -const lp = require('pull-length-prefixed') -const multiaddr = require('multiaddr') - -const identify = require('../../src/identify') -const msg = identify.message - -describe('identify.listener', () => { - let info - - beforeEach(function (done) { - this.timeout(20 * 1000) - - PeerInfo.create((err, _info) => { - if (err) { - return done(err) - } - - _info.protocols.add('/echo/1.0.0') - _info.protocols.add('/chat/1.0.0') - - info = _info - done() - }) - }) - - it('works', (done) => { - const p = pair() - - info.multiaddrs.add(multiaddr('/ip4/127.0.0.1/tcp/5002')) - - pull( - p[1], - lp.decode(), - collect((err, result) => { - expect(err).to.not.exist() - - const input = msg.decode(result[0]) - expect( - input - ).to.be.eql({ - protocolVersion: 'ipfs/0.1.0', - agentVersion: 'na', - publicKey: info.id.pubKey.bytes, - listenAddrs: [multiaddr('/ip4/127.0.0.1/tcp/5002').buffer], - observedAddr: multiaddr('/ip4/127.0.0.1/tcp/5001').buffer, - protocols: ['/echo/1.0.0', '/chat/1.0.0'] - }) - done() - }) - ) - - const conn = p[0] - conn.getObservedAddrs = (cb) => { - cb(null, [multiaddr('/ip4/127.0.0.1/tcp/5001')]) - } - - identify.listener(conn, info) - }) -}) diff --git a/test/multiaddr-trim.node.js b/test/multiaddr-trim.node.js deleted file mode 100644 index 4367f3cb..00000000 --- a/test/multiaddr-trim.node.js +++ /dev/null @@ -1,41 +0,0 @@ -/* eslint-env mocha */ -'use strict' - -const chai = require('chai') -chai.use(require('dirty-chai')) -const expect = chai.expect -const series = require('async/series') - -const createNode = require('./utils/create-node') - -describe('multiaddr trim', () => { - it('non used multiaddrs get trimmed', (done) => { - let node - - series([ - (cb) => createNode([ - '/ip4/0.0.0.0/tcp/999/wss/p2p-webrtc-direct', - '/ip4/127.0.0.1/tcp/55555/ws', - '/ip4/0.0.0.0/tcp/0/' - ], (err, _node) => { - expect(err).to.not.exist() - node = _node - const multiaddrs = node.peerInfo.multiaddrs.toArray() - expect(multiaddrs).to.have.length(3) - cb() - }), - (cb) => node.start(cb) - ], (err) => { - expect(err).to.not.exist() - - const multiaddrs = node.peerInfo.multiaddrs.toArray() - expect(multiaddrs.length).to.be.at.least(2) - // ensure the p2p-webrtc-direct address has been trimmed - multiaddrs.forEach((addr) => { - expect(() => addr.decapsulate('/ip4/0.0.0.0/tcp/999/wss/p2p-webrtc-direct')).to.throw() - }) - - node.stop(done) - }) - }) -}) diff --git a/test/node.js b/test/node.js index ced3b401..fa27625f 100644 --- a/test/node.js +++ b/test/node.js @@ -1,18 +1,3 @@ 'use strict' -require('./pnet.node') -require('./transports.node') -require('./stream-muxing.node') -require('./peer-discovery.node') -require('./peer-routing.node') -require('./ping.node') -require('./promisify.node') -require('./pubsub.node') -require('./content-routing.node') -require('./circuit-relay.node') -require('./multiaddr-trim.node') -require('./stats') -require('./dht.node') - -require('./ping/node') -require('./switch/node') +require('./transports/transport-manager.node') diff --git a/test/peer-discovery.node.js b/test/peer-discovery.node.js deleted file mode 100644 index cc3075e3..00000000 --- a/test/peer-discovery.node.js +++ /dev/null @@ -1,494 +0,0 @@ -/* eslint-env mocha */ -'use strict' - -const chai = require('chai') -chai.use(require('dirty-chai')) -const expect = chai.expect -const sinon = require('sinon') -const parallel = require('async/parallel') -const crypto = require('crypto') - -const createNode = require('./utils/create-node') -const echo = require('./utils/echo') -const { WRTC_RENDEZVOUS_MULTIADDR } = require('./utils/constants') - -describe('peer discovery', () => { - let nodeA - let nodeB - let nodeC - - function setup (options) { - before((done) => { - parallel([ - (cb) => createNode([ - '/ip4/0.0.0.0/tcp/0', - `${WRTC_RENDEZVOUS_MULTIADDR.toString()}/p2p-webrtc-star` - ], options, (err, node) => { - expect(err).to.not.exist() - nodeA = node - node.handle('/echo/1.0.0', echo) - node.start(cb) - }), - (cb) => createNode([ - '/ip4/0.0.0.0/tcp/0', - `${WRTC_RENDEZVOUS_MULTIADDR.toString()}/p2p-webrtc-star` - ], options, (err, node) => { - expect(err).to.not.exist() - nodeB = node - node.handle('/echo/1.0.0', echo) - node.start(cb) - }), - (cb) => createNode([ - '/ip4/0.0.0.0/tcp/0', - `${WRTC_RENDEZVOUS_MULTIADDR.toString()}/p2p-webrtc-star` - ], options, (err, node) => { - expect(err).to.not.exist() - nodeC = node - node.handle('/echo/1.0.0', echo) - node.start(cb) - }) - ], done) - }) - - after((done) => { - parallel([ - (cb) => nodeA.stop(cb), - (cb) => nodeB.stop(cb), - (cb) => nodeC.stop(cb) - ], done) - }) - - afterEach(() => { - sinon.restore() - }) - } - - describe('module registration', () => { - it('should enable by default a module passed as an object', (done) => { - const mockDiscovery = { - on: sinon.stub(), - removeListener: sinon.stub(), - start: sinon.stub().callsArg(0), - stop: sinon.stub().callsArg(0) - } - - const options = { modules: { peerDiscovery: [mockDiscovery] } } - - createNode(['/ip4/0.0.0.0/tcp/0'], options, (err, node) => { - expect(err).to.not.exist() - - node.start((err) => { - expect(err).to.not.exist() - expect(mockDiscovery.start.called).to.be.true() - node.stop(done) - }) - }) - }) - - it('should enable by default a module passed as a function', (done) => { - const mockDiscovery = { - on: sinon.stub(), - removeListener: sinon.stub(), - start: sinon.stub().callsArg(0), - stop: sinon.stub().callsArg(0) - } - - const MockDiscovery = sinon.stub().returns(mockDiscovery) - - const options = { modules: { peerDiscovery: [MockDiscovery] } } - - createNode(['/ip4/0.0.0.0/tcp/0'], options, (err, node) => { - expect(err).to.not.exist() - - node.start((err) => { - expect(err).to.not.exist() - expect(mockDiscovery.start.called).to.be.true() - node.stop(done) - }) - }) - }) - - it('should enable module by configutation', (done) => { - const mockDiscovery = { - on: sinon.stub(), - removeListener: sinon.stub(), - start: sinon.stub().callsArg(0), - stop: sinon.stub().callsArg(0), - tag: 'mockDiscovery' - } - - const enabled = sinon.stub().returns(true) - - const options = { - modules: { peerDiscovery: [mockDiscovery] }, - config: { - peerDiscovery: { - mockDiscovery: { - get enabled () { - return enabled() - } - } - } - } - } - - createNode(['/ip4/0.0.0.0/tcp/0'], options, (err, node) => { - expect(err).to.not.exist() - - node.start((err) => { - expect(err).to.not.exist() - expect(mockDiscovery.start.called).to.be.true() - expect(enabled.called).to.be.true() - node.stop(done) - }) - }) - }) - - it('should disable module by configutation', (done) => { - const mockDiscovery = { - on: sinon.stub(), - removeListener: sinon.stub(), - start: sinon.stub().callsArg(0), - stop: sinon.stub().callsArg(0), - tag: 'mockDiscovery' - } - - const disabled = sinon.stub().returns(false) - - const options = { - modules: { peerDiscovery: [mockDiscovery] }, - config: { - peerDiscovery: { - mockDiscovery: { - get enabled () { - return disabled() - } - } - } - } - } - - createNode(['/ip4/0.0.0.0/tcp/0'], options, (err, node) => { - expect(err).to.not.exist() - - node.start((err) => { - expect(err).to.not.exist() - expect(mockDiscovery.start.called).to.be.false() - expect(disabled.called).to.be.true() - node.stop(done) - }) - }) - }) - - it('should register module passed as function', (done) => { - const mockDiscovery = { - on: sinon.stub(), - removeListener: sinon.stub(), - start: sinon.stub().callsArg(0), - stop: sinon.stub().callsArg(0) - } - - const MockDiscovery = sinon.stub().returns(mockDiscovery) - MockDiscovery.tag = 'mockDiscovery' - - const options = { - modules: { peerDiscovery: [MockDiscovery] }, - config: { - peerDiscovery: { - mockDiscovery: { - enabled: true, - time: Date.now() - } - } - } - } - - createNode(['/ip4/0.0.0.0/tcp/0'], options, (err, node) => { - expect(err).to.not.exist() - - node.start((err) => { - expect(err).to.not.exist() - expect(mockDiscovery.start.called).to.be.true() - expect(MockDiscovery.called).to.be.true() - // Ensure configuration was passed - expect(MockDiscovery.firstCall.args[0]) - .to.deep.include(options.config.peerDiscovery.mockDiscovery) - node.stop(done) - }) - }) - }) - - it('should register module passed as object', (done) => { - const mockDiscovery = { - on: sinon.stub(), - removeListener: sinon.stub(), - start: sinon.stub().callsArg(0), - stop: sinon.stub().callsArg(0), - tag: 'mockDiscovery' - } - - const options = { - modules: { peerDiscovery: [mockDiscovery] }, - config: { - peerDiscovery: { - mockDiscovery: { enabled: true } - } - } - } - - createNode(['/ip4/0.0.0.0/tcp/0'], options, (err, node) => { - expect(err).to.not.exist() - - node.start((err) => { - expect(err).to.not.exist() - expect(mockDiscovery.start.called).to.be.true() - node.stop(done) - }) - }) - }) - }) - - describe('discovery scenarios', () => { - setup({ - config: { - dht: { - enabled: false - }, - peerDiscovery: { - autoDial: false, - bootstrap: { - enabled: true, - list: [] - } - } - } - }) - - it('should ignore self on discovery', function () { - const discoverySpy = sinon.spy() - nodeA.on('peer:discovery', discoverySpy) - nodeA._discovery[0].emit('peer', nodeA.peerInfo) - - expect(discoverySpy.called).to.eql(false) - expect(nodeA.peerBook.getAllArray()).to.have.length(0) - expect() - }) - }) - - describe('MulticastDNS', () => { - setup({ - config: { - dht: { - enabled: false - }, - peerDiscovery: { - autoDial: true, - mdns: { - enabled: true, - interval: 200, // discover quickly - // use a random tag to prevent CI collision - serviceTag: crypto.randomBytes(10).toString('hex') - } - } - } - }) - - it('find peers', function (done) { - const expectedPeers = new Set([ - nodeB.peerInfo.id.toB58String(), - nodeC.peerInfo.id.toB58String() - ]) - - function finish () { - nodeA.removeAllListeners('peer:discovery') - expect(expectedPeers.size).to.eql(0) - done() - } - - nodeA.on('peer:discovery', (peerInfo) => { - expectedPeers.delete(peerInfo.id.toB58String()) - if (expectedPeers.size === 0) { - finish() - } - }) - }) - }) - - // TODO needs a delay (this test is already long) - describe.skip('WebRTCStar', () => { - setup({ - config: { - dht: { - enabled: false - }, - peerDiscovery: { - autoDial: true, - webRTCStar: { - enabled: true - } - } - } - }) - - it('find peers', function (done) { - this.timeout(20e3) - const expectedPeers = new Set([ - nodeB.peerInfo.id.toB58String(), - nodeC.peerInfo.id.toB58String() - ]) - - function finish () { - nodeA.removeAllListeners('peer:discovery') - expect(expectedPeers.size).to.eql(0) - done() - } - - nodeA.on('peer:discovery', (peerInfo) => { - expectedPeers.delete(peerInfo.id.toB58String()) - if (expectedPeers.size === 0) { - finish() - } - }) - }) - }) - - describe('MulticastDNS + WebRTCStar', () => { - setup({ - config: { - dht: { - enabled: false - }, - peerDiscovery: { - autoDial: true, - mdns: { - enabled: true, - interval: 200, // discovery quickly - // use a random tag to prevent CI collision - serviceTag: crypto.randomBytes(10).toString('hex') - }, - webRTCStar: { - enabled: true - } - } - } - }) - - it('find peers', function (done) { - const expectedPeers = new Set([ - nodeB.peerInfo.id.toB58String(), - nodeC.peerInfo.id.toB58String() - ]) - - function finish () { - nodeA.removeAllListeners('peer:discovery') - expect(expectedPeers.size).to.eql(0) - done() - } - - nodeA.on('peer:discovery', (peerInfo) => { - expectedPeers.delete(peerInfo.id.toB58String()) - if (expectedPeers.size === 0) { - finish() - } - }) - }) - }) - - describe('dht', () => { - setup({ - config: { - peerDiscovery: { - autoDial: true, - mdns: { - enabled: false - }, - webRTCStar: { - enabled: false - } - }, - dht: { - enabled: true, - kBucketSize: 20, - randomWalk: { - enabled: true, - queriesPerPeriod: 1, - delay: 100, - interval: 200, // start the query sooner - timeout: 3000 - } - } - } - }) - - it('find peers through the dht', function (done) { - const expectedPeers = new Set([ - nodeB.peerInfo.id.toB58String(), - nodeC.peerInfo.id.toB58String() - ]) - - function finish () { - nodeA.removeAllListeners('peer:discovery') - expect(expectedPeers.size).to.eql(0) - done() - } - - nodeA.on('peer:discovery', (peerInfo) => { - expectedPeers.delete(peerInfo.id.toB58String()) - if (expectedPeers.size === 0) { - finish() - } - }) - - // Topology: - // A -> B - // C -> B - nodeA.dial(nodeB.peerInfo, (err) => { - expect(err).to.not.exist() - }) - nodeC.dial(nodeB.peerInfo, (err) => { - expect(err).to.not.exist() - }) - }) - }) - - describe('auto dial', () => { - setup({ - connectionManager: { - minPeers: 1 - }, - config: { - peerDiscovery: { - autoDial: true, - mdns: { - enabled: false - }, - webRTCStar: { - enabled: false - }, - bootstrap: { - enabled: true, - list: [] - } - }, - dht: { - enabled: false - } - } - }) - - it('should only dial when the peer count is below the low watermark', (done) => { - const bootstrap = nodeA._discovery[0] - sinon.stub(nodeA._switch.dialer, 'connect').callsFake((peerInfo) => { - nodeA._switch.connection.connections[peerInfo.id.toB58String()] = [] - }) - - bootstrap.emit('peer', nodeB.peerInfo) - bootstrap.emit('peer', nodeC.peerInfo) - - // Only nodeB should get dialed - expect(nodeA._switch.dialer.connect.callCount).to.eql(1) - expect(nodeA._switch.dialer.connect.getCall(0).args[0]).to.eql(nodeB.peerInfo) - done() - }) - }) -}) diff --git a/test/peer-routing.node.js b/test/peer-routing.node.js deleted file mode 100644 index 16e08b8f..00000000 --- a/test/peer-routing.node.js +++ /dev/null @@ -1,294 +0,0 @@ -/* eslint-env mocha */ -/* eslint max-nested-callbacks: ["error", 8] */ - -'use strict' - -const chai = require('chai') -chai.use(require('dirty-chai')) -const expect = chai.expect -const parallel = require('async/parallel') -const _times = require('lodash.times') -const DelegatedPeerRouter = require('libp2p-delegated-peer-routing') -const sinon = require('sinon') -const nock = require('nock') - -const createNode = require('./utils/create-node') - -describe('.peerRouting', () => { - describe('via the dht', () => { - let nodeA - let nodeB - let nodeC - let nodeD - let nodeE - - before('create the outer ring of connections', (done) => { - const tasks = _times(5, () => (cb) => { - createNode('/ip4/0.0.0.0/tcp/0', (err, node) => { - expect(err).to.not.exist() - node.start((err) => cb(err, node)) - }) - }) - - parallel(tasks, (err, nodes) => { - expect(err).to.not.exist() - nodeA = nodes[0] - nodeB = nodes[1] - nodeC = nodes[2] - nodeD = nodes[3] - nodeE = nodes[4] - - parallel([ - (cb) => nodeA.dial(nodeB.peerInfo, cb), - (cb) => nodeB.dial(nodeC.peerInfo, cb), - (cb) => nodeC.dial(nodeD.peerInfo, cb), - (cb) => nodeD.dial(nodeE.peerInfo, cb), - (cb) => nodeE.dial(nodeA.peerInfo, cb) - ], (err) => { - expect(err).to.not.exist() - // Give the kbucket time to fill in the dht - setTimeout(done, 250) - }) - }) - }) - - after((done) => { - parallel([ - (cb) => nodeA.stop(cb), - (cb) => nodeB.stop(cb), - (cb) => nodeC.stop(cb), - (cb) => nodeD.stop(cb), - (cb) => nodeE.stop(cb) - ], done) - }) - - it('should use the nodes dht', (done) => { - const stub = sinon.stub(nodeA._dht, 'findPeer').callsFake(() => { - stub.restore() - done() - }) - - nodeA.peerRouting.findPeer() - }) - - describe('connected in an el ring', () => { - it('should be able to find a peer we are not directly connected to', (done) => { - parallel([ - (cb) => nodeA.dial(nodeC.peerInfo.id, cb), - (cb) => nodeB.dial(nodeD.peerInfo.id, cb), - (cb) => nodeC.dial(nodeE.peerInfo.id, cb) - ], (err) => { - if (err) throw err - expect(err).to.not.exist() - nodeB.peerRouting.findPeer(nodeE.peerInfo.id, (err, peerInfo) => { - expect(err).to.not.exist() - expect(nodeE.peerInfo.id.toB58String()).to.equal(peerInfo.id.toB58String()) - done() - }) - }) - }) - }) - }) - - describe('via a delegate', () => { - let nodeA - let delegate - - before((done) => { - parallel([ - // Create the node using the delegate - (cb) => { - delegate = new DelegatedPeerRouter({ - host: 'ipfs.io', - protocol: 'https', - port: '443' - }) - createNode('/ip4/0.0.0.0/tcp/0', { - modules: { - peerRouting: [delegate] - }, - config: { - dht: { - enabled: false - } - } - }, (err, node) => { - expect(err).to.not.exist() - nodeA = node - nodeA.start(cb) - }) - } - ], done) - }) - - after((done) => nodeA.stop(done)) - afterEach(() => nock.cleanAll()) - - it('should use the delegate router to find peers', (done) => { - const stub = sinon.stub(delegate, 'findPeer').callsFake(() => { - stub.restore() - done() - }) - nodeA.peerRouting.findPeer() - }) - - it('should be able to find a peer', (done) => { - const peerKey = 'QmTp9VkYvnHyrqKQuFPiuZkiX9gPcqj6x5LJ1rmWuSySnL' - const mockApi = nock('https://ipfs.io') - .post('/api/v0/dht/findpeer') - .query({ - arg: peerKey, - timeout: '30000ms', - 'stream-channels': true - }) - .reply(200, `{"Extra":"","ID":"some other id","Responses":null,"Type":0}\n{"Extra":"","ID":"","Responses":[{"Addrs":["/ip4/127.0.0.1/tcp/4001"],"ID":"${peerKey}"}],"Type":2}\n`, [ - 'Content-Type', 'application/json', - 'X-Chunked-Output', '1' - ]) - - nodeA.peerRouting.findPeer(peerKey, (err, peerInfo) => { - expect(err).to.not.exist() - expect(peerInfo.id.toB58String()).to.equal(peerKey) - expect(mockApi.isDone()).to.equal(true) - done() - }) - }) - - it('should error when a peer cannot be found', (done) => { - const peerKey = 'key of a peer not on the network' - const mockApi = nock('https://ipfs.io') - .post('/api/v0/dht/findpeer') - .query({ - arg: peerKey, - timeout: '30000ms', - 'stream-channels': true - }) - .reply(200, '{"Extra":"","ID":"some other id","Responses":null,"Type":6}\n{"Extra":"","ID":"yet another id","Responses":null,"Type":0}\n{"Extra":"routing:not found","ID":"","Responses":null,"Type":3}\n', [ - 'Content-Type', 'application/json', - 'X-Chunked-Output', '1' - ]) - - nodeA.peerRouting.findPeer(peerKey, (err, peerInfo) => { - expect(err).to.exist() - expect(peerInfo).to.not.exist() - expect(mockApi.isDone()).to.equal(true) - done() - }) - }) - - it('should handle errors from the api', (done) => { - const peerKey = 'key of a peer not on the network' - const mockApi = nock('https://ipfs.io') - .post('/api/v0/dht/findpeer') - .query({ - arg: peerKey, - timeout: '30000ms', - 'stream-channels': true - }) - .reply(502) - - nodeA.peerRouting.findPeer(peerKey, (err, peerInfo) => { - expect(err).to.exist() - expect(peerInfo).to.not.exist() - expect(mockApi.isDone()).to.equal(true) - done() - }) - }) - }) - - describe('via the dht and a delegate', () => { - let nodeA - let delegate - - before((done) => { - parallel([ - // Create the node using the delegate - (cb) => { - delegate = new DelegatedPeerRouter({ - host: 'ipfs.io', - protocol: 'https', - port: '443' - }) - createNode('/ip4/0.0.0.0/tcp/0', { - modules: { - peerRouting: [delegate] - } - }, (err, node) => { - expect(err).to.not.exist() - nodeA = node - nodeA.start(cb) - }) - } - ], done) - }) - - after((done) => nodeA.stop(done)) - - describe('findPeer', () => { - it('should only use the dht if it finds the peer', (done) => { - const results = [true] - const dhtStub = sinon.stub(nodeA._dht, 'findPeer').callsArgWith(2, null, results) - const delegateStub = sinon.stub(delegate, 'findPeer').throws(() => { - return new Error('the delegate should not have been called') - }) - - nodeA.peerRouting.findPeer('a peer id', (err, results) => { - expect(err).to.not.exist() - expect(results).to.equal(results) - expect(dhtStub.calledOnce).to.equal(true) - expect(delegateStub.notCalled).to.equal(true) - delegateStub.restore() - dhtStub.restore() - done() - }) - }) - - it('should use the delegate if the dht fails to find the peer', (done) => { - const results = [true] - const dhtStub = sinon.stub(nodeA._dht, 'findPeer').callsArgWith(2, null, undefined) - const delegateStub = sinon.stub(delegate, 'findPeer').callsArgWith(2, null, results) - - nodeA.peerRouting.findPeer('a peer id', (err, results) => { - expect(err).to.not.exist() - expect(results).to.deep.equal(results) - expect(dhtStub.calledOnce).to.equal(true) - expect(delegateStub.calledOnce).to.equal(true) - delegateStub.restore() - dhtStub.restore() - done() - }) - }) - }) - }) - - describe('no routers', () => { - let nodeA - before((done) => { - createNode('/ip4/0.0.0.0/tcp/0', { - config: { - dht: { - enabled: false - } - } - }, (err, node) => { - expect(err).to.not.exist() - nodeA = node - done() - }) - }) - - it('.findPeer should return an error with no options', (done) => { - nodeA.peerRouting.findPeer('a cid', (err) => { - expect(err).to.exist() - done() - }) - }) - - it('.findPeer should return an error with options', (done) => { - nodeA.peerRouting.findPeer('a cid', { maxTimeout: 5000 }, (err) => { - expect(err).to.exist() - done() - }) - }) - }) -}) diff --git a/test/ping.node.js b/test/ping.node.js deleted file mode 100644 index b677ab6a..00000000 --- a/test/ping.node.js +++ /dev/null @@ -1,61 +0,0 @@ -/* eslint-env mocha */ -'use strict' - -const chai = require('chai') -chai.use(require('dirty-chai')) -const expect = chai.expect -const parallel = require('async/parallel') - -const createNode = require('./utils/create-node.js') -const echo = require('./utils/echo') - -describe('ping', () => { - let nodeA - let nodeB - - before((done) => { - parallel([ - (cb) => createNode('/ip4/0.0.0.0/tcp/0', (err, node) => { - expect(err).to.not.exist() - nodeA = node - node.handle('/echo/1.0.0', echo) - node.start(cb) - }), - (cb) => createNode('/ip4/0.0.0.0/tcp/0', (err, node) => { - expect(err).to.not.exist() - nodeB = node - node.handle('/echo/1.0.0', echo) - node.start(cb) - }) - ], done) - }) - - after((done) => { - parallel([ - (cb) => nodeA.stop(cb), - (cb) => nodeB.stop(cb) - ], done) - }) - - it('should be able to ping another node', (done) => { - nodeA.ping(nodeB.peerInfo, (err, ping) => { - expect(err).to.not.exist() - ping.once('ping', (time) => { - expect(time).to.exist() - ping.stop() - done() - }) - - ping.start() - }) - }) - - it('should be not be able to ping when stopped', (done) => { - nodeA.stop(() => { - nodeA.ping(nodeB.peerInfo, (err) => { - expect(err).to.exist() - done() - }) - }) - }) -}) diff --git a/test/ping/node.js b/test/ping/node.js deleted file mode 100644 index 55396510..00000000 --- a/test/ping/node.js +++ /dev/null @@ -1,3 +0,0 @@ -'use strict' - -require('./test-ping.js') diff --git a/test/ping/test-ping.js b/test/ping/test-ping.js deleted file mode 100644 index 0dc1f9e1..00000000 --- a/test/ping/test-ping.js +++ /dev/null @@ -1,118 +0,0 @@ -/* eslint-env mocha */ -'use strict' - -const chai = require('chai') -const dirtyChai = require('dirty-chai') -const expect = chai.expect -chai.use(dirtyChai) -const PeerInfo = require('peer-info') -const PeerBook = require('peer-book') - -const Swarm = require('../../src/switch') -const TCP = require('libp2p-tcp') -const series = require('async/series') -const parallel = require('async/parallel') - -const Ping = require('../../src/ping') - -describe('libp2p ping', () => { - let swarmA - let swarmB - let peerA - let peerB - - before(function (done) { - this.timeout(20 * 1000) - series([ - (cb) => PeerInfo.create((err, peerInfo) => { - expect(err).to.not.exist() - peerA = peerInfo - peerA.multiaddrs.add('/ip4/127.0.0.1/tcp/0') - cb() - }), - (cb) => PeerInfo.create((err, peerInfo) => { - expect(err).to.not.exist() - peerB = peerInfo - peerB.multiaddrs.add('/ip4/127.0.0.1/tcp/0') - cb() - }), - (cb) => { - swarmA = new Swarm(peerA, new PeerBook()) - swarmB = new Swarm(peerB, new PeerBook()) - swarmA.transport.add('tcp', new TCP()) - swarmB.transport.add('tcp', new TCP()) - cb() - }, - (cb) => swarmA.start(cb), - (cb) => swarmB.start(cb), - (cb) => { - Ping.mount(swarmA) - Ping.mount(swarmB) - cb() - } - ], done) - }) - - after((done) => { - parallel([ - (cb) => swarmA.stop(cb), - (cb) => swarmB.stop(cb) - ], done) - }) - - it('ping once from peerA to peerB', (done) => { - const p = new Ping(swarmA, peerB) - - p.on('error', (err) => { - expect(err).to.not.exist() - }) - - p.on('ping', (time) => { - expect(time).to.be.a('Number') - p.stop() - done() - }) - - p.start() - }) - - it('ping 5 times from peerB to peerA', (done) => { - const p = new Ping(swarmB, peerA) - - p.on('error', (err) => { - expect(err).to.not.exist() - }) - - let counter = 0 - - p.on('ping', (time) => { - expect(time).to.be.a('Number') - if (++counter === 5) { - p.stop() - done() - } - }) - - p.start() - }) - - it('cannot ping itself', (done) => { - const p = new Ping(swarmA, peerA) - - p.on('error', (err) => { - expect(err).to.exist() - done() - }) - - p.on('ping', () => { - expect.fail('should not be called') - }) - - p.start() - }) - - it('unmount PING protocol', () => { - Ping.unmount(swarmA) - Ping.unmount(swarmB) - }) -}) diff --git a/test/pnet.node.js b/test/pnet.node.js deleted file mode 100644 index 450bbbb6..00000000 --- a/test/pnet.node.js +++ /dev/null @@ -1,92 +0,0 @@ -/* eslint-env mocha */ -'use strict' - -const chai = require('chai') -chai.use(require('dirty-chai')) -const expect = chai.expect -const PeerInfo = require('peer-info') -const PeerId = require('peer-id') -const waterfall = require('async/waterfall') -const WS = require('libp2p-websockets') -const defaultsDeep = require('@nodeutils/defaults-deep') -const DHT = require('libp2p-kad-dht') - -const Libp2p = require('../src') - -describe('private network', () => { - let config - - before((done) => { - waterfall([ - (cb) => PeerId.create({ bits: 512 }, cb), - (peerId, cb) => PeerInfo.create(peerId, cb), - (peerInfo, cb) => { - config = { - peerInfo, - modules: { - transport: [WS], - dht: DHT - } - } - cb() - } - ], () => done()) - }) - - describe('enforced network protection', () => { - before(() => { - process.env.LIBP2P_FORCE_PNET = 1 - }) - - after(() => { - delete process.env.LIBP2P_FORCE_PNET - }) - - it('should throw an error without a provided protector', () => { - expect(() => { - return new Libp2p(config) - }).to.throw('Private network is enforced, but no protector was provided') - }) - - it('should create a libp2p node with a provided protector', () => { - let node - const protector = { - psk: '123', - tag: '/psk/1.0.0', - protect: () => { } - } - - expect(() => { - const options = defaultsDeep(config, { - modules: { - connProtector: protector - } - }) - - node = new Libp2p(options) - return node - }).to.not.throw() - expect(node._switch.protector).to.deep.equal(protector) - }) - - it('should throw an error if the protector does not have a protect method', () => { - expect(() => { - const options = defaultsDeep(config, { - modules: { - connProtector: { } - } - }) - - return new Libp2p(options) - }).to.throw() - }) - }) - - describe('network protection not enforced', () => { - it('should not throw an error with no provided protector', () => { - expect(() => { - return new Libp2p(config) - }).to.not.throw() - }) - }) -}) diff --git a/test/pnet/fixtures/peer-a.json b/test/pnet/fixtures/peer-a.json deleted file mode 100644 index 107c8944..00000000 --- a/test/pnet/fixtures/peer-a.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "id": "QmeS1ou3mrjCFGoFtRx3MwrGDzqKD6xbuYJU1CKtMrtFFu", - "privKey": "CAASqAkwggSkAgEAAoIBAQChwzYwCNIyUkzEK3sILqq9ChAKZ9eU+ribY+B/xwAwDKPfvuqHq0hjauJBMcjiQyKAWz9xEBR3WupOM7h9M8oU+/e0xJUTt/CDOrtJ0PCgUXeBLkqsApbBoXW3yomHEDHxYttKzrtoTimiP1bhrxurcpVNC4CUYD+q8gw3sRZlsrqpeYYAfU04kS0BM75W/sUT90znnHvOxFXrEdMMdenEFhZOsDyEK9ENzwhkKgOGb18MBY4kN5DoW4bVd4ItfZnNwdkQtpP/X99tMWJxO4yqpngbywZGnkfirLeuRwt/xRGFVbLOigjBpTVpcbBqe1t2Flhuf/bfWYX4FbyElA5FAgMBAAECggEAJnDTcbrG6LpyD7QdeqZMYLwBb9eZfYfPUu37LaJGwyRd1Q/zf+YOP8HonoGMMWiuzD3i56Vgl7R9NbRIxUgHX9E43jZRDuyJNUZBt5r1c8OoWIR9rj63QLBz3wc8g2Iv3CMX5cEW/ASHFE1lAiCwvJ9wJ2zyU1BEEQWQLbPhlKzw7SLhr4fee45/7pnrKZMllt5vwC9pM6lrpIkICO5gUu0OWu5wfzzlTvfmCgfTb11VqKESEPbDBMUtpJibRqegE4xvipLklJ8VV8jz7NFs9bhgCpNM74Ngt5vGHcddeqtj//86UsClEw5YgWAdRe29ZjMApWvKIkginLjZEO8eiQKBgQDoDWii0rmlgBl1/8fENUSWxYvknGmWO7eWjVqMjDvA+waWUVDpTE+eHT1QAaPofM+nFz5PG+SpB55o4rXdxDesq+DqnaRAI9WtSHdgRtjgETyqoBAiahQ0zGWmSEYHGDB+xGctTMr8GxdhZxqZjjfyptp6oXXqZkmxgcogrx+WTwKBgQCydNDmCDpeH0kSvhAPxaNx5c9WkFEFSA0OCZOx57Y+Mt0MVamRILFrUrcMz095w8BQZkjlHjSHfsRgKa/b2eOd+3BhoMLZVtxRqBdpdqq1KTAcRRG4yA2KA39rttpVzaTV5SPfdDf3tsVlBtV784W63gVpN9gNfajyyrpeffiBKwKBgDnDrLprbl8uZigjhdznza0ie9JqxTXqo6bMhS/bcLx3QIqGr3eD0YXwjWSvI9gpyZ80gAQ9U0xoYxyE4vTTdXB8UL7Wgx6cTQKXuW+z8yTD5bArrBiFA4apItyjvRrjAJ9t0KlMJnNfYxCSE+MJrg+vTU+dhbbVw552SpScQ2atAoGBAKMu3rb6XyUiRpe05MsHVuYX1vi5Dt1dfVKQv1W3JJbLvAZDbsMeuh4BjRFRoMMflQPwBEg+zpn3+WpVtFG9dL5J5gHgF0zWeLDSnFX8BS2TdELlhccKaBcEC8hbdFtxqIFO/vaeN2902hv/m8e0b1zpGNmWDyKG/a7GYpV1a3/xAoGBAJtgGANDVk6qqcWGEVk56FH1ZksvgF3SPXWaXpzbZ5KLCcV5ooRyhowylKUZBBPowMeZ46tem2xwJbraB5kDg6WiSjBsXcbN95ivb8AuoRa6gDqAszjokQUSdpY7FTgMaL046AuihrKsQSly1jrQqbQu8JBgmnnBzus3s77inL/j", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQChwzYwCNIyUkzEK3sILqq9ChAKZ9eU+ribY+B/xwAwDKPfvuqHq0hjauJBMcjiQyKAWz9xEBR3WupOM7h9M8oU+/e0xJUTt/CDOrtJ0PCgUXeBLkqsApbBoXW3yomHEDHxYttKzrtoTimiP1bhrxurcpVNC4CUYD+q8gw3sRZlsrqpeYYAfU04kS0BM75W/sUT90znnHvOxFXrEdMMdenEFhZOsDyEK9ENzwhkKgOGb18MBY4kN5DoW4bVd4ItfZnNwdkQtpP/X99tMWJxO4yqpngbywZGnkfirLeuRwt/xRGFVbLOigjBpTVpcbBqe1t2Flhuf/bfWYX4FbyElA5FAgMBAAE=" -} diff --git a/test/pnet/fixtures/peer-b.json b/test/pnet/fixtures/peer-b.json deleted file mode 100644 index 4c52c6ea..00000000 --- a/test/pnet/fixtures/peer-b.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "id": "QmYWHGZ9y1Bzx59bBzn85JsJxwmpBy5bpXDWDfwMfsHsxz", - "privKey": "CAASqQkwggSlAgEAAoIBAQDLVaPqWFA8WgK6ixuPvhTHeQfBblmEFLEmraLlIDSWbMUPva6aJ1V/hi2I5QLXNeeiig5sco+nF+RKhGnzQ9NpgHRVZ7Ze+LWq3Q4YxONdzFeNUjTvJrDSKgkubA5EKC/LI6pU33WZbjyKkomGo+Gzuqvlj4Rx1dLVXRIOjxUYcIQw3vpLQgwPpiz52eWCeoCpzn06DcsF6aNPjhlp9uJRZCRxZ4yeiwh/A0xxiQtnB4fdZuUPmia1r62+oaxrDl4hUwR7kzHYl0YGfXxAW9GT17KGtjES2yO4kAUgquelNh0hgBKZRvny9imwsObG7ntw5ZG7H62sP7UySIUJqoNRAgMBAAECggEBAKLVU25BCQg7wQGokwra2wMfPoG+IDuw4mkqFlBNKS/prSo86c2TgFmel2qQk2TLS1OUIZbha38RmAXA4qQohe5wKzmV06tcmwdY/YgCbF5aXSbUVYXLQ0Ea3r1pVUdps1SHnElZpnCXoi4Kyc2kAgSPkkdFVnhfFvc9EE/Ob8NgMkdFhlosE5WVNqm4BKQ+mqONddSz4JDbDOApPs/rRpgYm7pJKc3vkrYwniPjyQGYb5EoSbSWuu31RzIcn3Bhte3wKtfMMlpn8MMpPiYo2WJ2eVG6hlUOxhHgS93Y6czCfAgsDtD3C2JpteewuBjg8N0d6WRArKxny83J34q0qy0CgYEA6YSo5UDEq1TF8sbtSVYg6MKSX92NO5MQI/8fTjU4tEwxn/yxpGsnqUu0WGYIc2qVaZuxtcnk2CQxEilxQTbWSIxKuTt7qofEcpSjLLQ4f4chk4DpPsba+S8zSUdWdjthPHZT9IYzobylGBLfbPxyXXiYn1VuqAJfFy8iV9XqmdcCgYEA3ukROQQZCJcgsNTc5uFAKUeQvzv1iae3fGawgJmIJW3Bl8+4dSm1diqG3ZXP1WU31no2aX50PqOZjoIpbl1ggT76cnBDuu3pItR3dNJFQyMEpQOWOjO+NBWF7sRswCvlqbyjofWkzsdd0BioL7vWMjPftiusyyAFA55HRoeStxcCgYEA0tP7rKdSKKFr6inhl+GT6rGod7bOSSgYXXd7qx9v55AXCauaMqiv8TAxTdIo9RMYfHWd91OlMeNTDmOuJcO9qVhIKn5iw266VPyPac/4ZmL5VHQBobTlhC4yLomirTIlMvJeEBmNygtIPrjjUUGGe49itA/szPD/Ky5Z4lV27pcCgYAWU3mqIELxnVFk5K0LYtwuRkC1Jqg9FVNHXnGnL7l3JjsRnXh4I6lNII1JfEvIr86b6LmybzvtWi1zHI5Rw4B68XfcJmpiOpnzJxyf0r+lLci1Tlqpka0nQlCbzYim5r6l9YLeIeBT5Zv7z7xoq4OUm6V4dX9lCNv3tM6mvcVwGQKBgQC9hhjD64/VKXL8wYKZyTAOVO5xYCcqylrpI39qdzl+sS8oqmLUbXnKsGY4If9U61XdULld41BJCRlv6CsKreynm6ZN41j9YRuWWLu8STJcniV9Ef9uVl1M1zo8kfnCHMCym9LkTfJY+Ow/kYhqPukJJL6ve1CVmIuA4rnZlshjbg==", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDLVaPqWFA8WgK6ixuPvhTHeQfBblmEFLEmraLlIDSWbMUPva6aJ1V/hi2I5QLXNeeiig5sco+nF+RKhGnzQ9NpgHRVZ7Ze+LWq3Q4YxONdzFeNUjTvJrDSKgkubA5EKC/LI6pU33WZbjyKkomGo+Gzuqvlj4Rx1dLVXRIOjxUYcIQw3vpLQgwPpiz52eWCeoCpzn06DcsF6aNPjhlp9uJRZCRxZ4yeiwh/A0xxiQtnB4fdZuUPmia1r62+oaxrDl4hUwR7kzHYl0YGfXxAW9GT17KGtjES2yO4kAUgquelNh0hgBKZRvny9imwsObG7ntw5ZG7H62sP7UySIUJqoNRAgMBAAE=" -} diff --git a/test/pnet/pnet.spec.js b/test/pnet/pnet.spec.js deleted file mode 100644 index c7f254fa..00000000 --- a/test/pnet/pnet.spec.js +++ /dev/null @@ -1,105 +0,0 @@ -/* eslint max-nested-callbacks: ["error", 8] */ -/* eslint-env mocha */ -'use strict' - -const chai = require('chai') -const dirtyChai = require('dirty-chai') -chai.use(dirtyChai) -const expect = chai.expect -const parallel = require('async/parallel') -const PeerId = require('peer-id') -const Connection = require('interface-connection').Connection -const pair = require('pull-pair/duplex') -const pull = require('pull-stream') - -const Protector = require('../../src/pnet') -const Errors = Protector.errors -const generate = Protector.generate - -const swarmKeyBuffer = Buffer.alloc(95) -const wrongSwarmKeyBuffer = Buffer.alloc(95) - -// Write new psk files to the buffers -generate(swarmKeyBuffer) -generate(wrongSwarmKeyBuffer) - -describe('private network', () => { - before((done) => { - parallel([ - (cb) => PeerId.createFromJSON(require('./fixtures/peer-a'), cb), - (cb) => PeerId.createFromJSON(require('./fixtures/peer-b'), cb) - ], (err) => { - expect(err).to.not.exist() - done() - }) - }) - - it('should accept a valid psk buffer', () => { - const protector = new Protector(swarmKeyBuffer) - - expect(protector.tag).to.equal('/key/swarm/psk/1.0.0/') - expect(protector.psk.byteLength).to.equal(32) - }) - - it('should protect a simple connection', (done) => { - const p = pair() - const protector = new Protector(swarmKeyBuffer) - - const aToB = protector.protect(new Connection(p[0]), (err) => { - expect(err).to.not.exist() - }) - const bToA = protector.protect(new Connection(p[1]), (err) => { - expect(err).to.not.exist() - }) - - pull( - pull.values([Buffer.from('hello world'), Buffer.from('doo dah')]), - aToB - ) - - pull( - bToA, - pull.collect((err, chunks) => { - expect(err).to.not.exist() - expect(chunks).to.eql([Buffer.from('hello world'), Buffer.from('doo dah')]) - done() - }) - ) - }) - - it('should not connect to a peer with a different key', (done) => { - const p = pair() - const protector = new Protector(swarmKeyBuffer) - const protectorB = new Protector(wrongSwarmKeyBuffer) - - const aToB = protector.protect(new Connection(p[0]), () => { }) - const bToA = protectorB.protect(new Connection(p[1]), () => { }) - - pull( - pull.values([Buffer.from('hello world'), Buffer.from('doo dah')]), - aToB - ) - - pull( - bToA, - pull.collect((values) => { - expect(values).to.equal(null) - done() - }) - ) - }) - - describe('invalid psks', () => { - it('should not accept a bad psk', () => { - expect(() => { - return new Protector(Buffer.from('not-a-key')) - }).to.throw(Errors.INVALID_PSK) - }) - - it('should not accept a psk of incorrect length', () => { - expect(() => { - return new Protector(Buffer.from('/key/swarm/psk/1.0.0/\n/base16/\ndffb7e')) - }).to.throw(Errors.INVALID_PSK) - }) - }) -}) diff --git a/test/promisify.node.js b/test/promisify.node.js deleted file mode 100644 index 16868a4a..00000000 --- a/test/promisify.node.js +++ /dev/null @@ -1,87 +0,0 @@ -/* eslint-env mocha */ -'use strict' - -/** - * This test suite is intended to validate compatability of - * the promisified api, until libp2p has been fully migrated to - * async/await. Once the migration is complete and all tests - * are using async/await, this file can be removed. - */ - -const chai = require('chai') -chai.use(require('dirty-chai')) -const expect = chai.expect -const promisify = require('promisify-es6') -const createNode = promisify(require('./utils/create-node')) -const { createPeerInfo } = require('./utils/create-node') -const Node = require('./utils/bundle-nodejs') -const pull = require('pull-stream') -const Ping = require('../src/ping') - -/** - * As libp2p is currently promisified, when extending libp2p, - * method arguments must be passed to `super` to ensure the - * promisify callbacks are properly resolved - */ -class AsyncLibp2p extends Node { - async start (...args) { - await super.start(...args) - } - - async stop (...args) { - await super.start(...args) - } -} - -async function createAsyncNode () { - const peerInfo = await promisify(createPeerInfo)() - peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0') - return new AsyncLibp2p({ peerInfo }) -} - -describe('promisified libp2p', () => { - let libp2p - let otherNode - const ECHO_PROTO = '/echo/1.0.0' - - before('Create and Start', async () => { - [libp2p, otherNode] = await Promise.all([ - createNode('/ip4/0.0.0.0/tcp/0'), - createAsyncNode() - ]) - - return [libp2p, otherNode].map(node => { - node.handle(ECHO_PROTO, (_, conn) => pull(conn, conn)) - return node.start() - }) - }) - - after('Stop', () => { - return [libp2p, otherNode].map(node => node.stop()) - }) - - afterEach('Hang up', () => { - return libp2p.hangUp(otherNode.peerInfo) - }) - - it('dial', async () => { - const stream = await libp2p.dial(otherNode.peerInfo) - expect(stream).to.not.exist() - expect(libp2p._switch.connection.getAll()).to.have.length(1) - }) - - it('dialFSM', async () => { - const connectionFSM = await libp2p.dialFSM(otherNode.peerInfo, ECHO_PROTO) - expect(connectionFSM).to.exist() - }) - - it('dialProtocol', async () => { - const stream = await libp2p.dialProtocol(otherNode.peerInfo, ECHO_PROTO) - expect(stream).to.exist() - }) - - it('ping', async () => { - const ping = await libp2p.ping(otherNode.peerInfo) - expect(ping).to.be.an.instanceOf(Ping) - }) -}) diff --git a/test/pubsub.node.js b/test/pubsub.node.js deleted file mode 100644 index 2e380ae4..00000000 --- a/test/pubsub.node.js +++ /dev/null @@ -1,467 +0,0 @@ -/* eslint-env mocha */ -/* eslint max-nested-callbacks: ["error", 8] */ - -'use strict' - -const chai = require('chai') -chai.use(require('dirty-chai')) -chai.use(require('chai-checkmark')) -const expect = chai.expect -const parallel = require('async/parallel') -const series = require('async/series') -const _times = require('lodash.times') -const promisify = require('promisify-es6') -const delay = require('delay') -const Floodsub = require('libp2p-floodsub') -const mergeOptions = require('merge-options') - -const { codes } = require('../src/errors') -const createNode = require('./utils/create-node') - -function startTwo (options, callback) { - if (typeof options === 'function') { - callback = options - options = {} - } - - const tasks = _times(2, () => (cb) => { - createNode('/ip4/0.0.0.0/tcp/0', mergeOptions({ - config: { - peerDiscovery: { - mdns: { - enabled: false - } - }, - pubsub: { - enabled: true - } - } - }, options), (err, node) => { - expect(err).to.not.exist() - node.start((err) => cb(err, node)) - }) - }) - - parallel(tasks, (err, nodes) => { - expect(err).to.not.exist() - - nodes[0].dial(nodes[1].peerInfo, (err) => callback(err, nodes)) - }) -} - -function stopTwo (nodes, callback) { - parallel([ - (cb) => nodes[0].stop(cb), - (cb) => nodes[1].stop(cb) - ], callback) -} - -describe('.pubsub', () => { - describe('.pubsub on (default)', () => { - it('start two nodes and send one message, then unsubscribe', (done) => { - // Check the final series error, and the publish handler - expect(2).checks(done) - - let nodes - const data = 'test' - const handler = (msg) => { - // verify the data is correct and mark the expect - expect(msg.data.toString()).to.eql(data).mark() - } - - series([ - // Start the nodes - (cb) => startTwo((err, _nodes) => { - nodes = _nodes - cb(err) - }), - // subscribe on the first - (cb) => nodes[0].pubsub.subscribe('pubsub', handler, null, cb), - // Wait a moment before publishing - (cb) => setTimeout(cb, 500), - // publish on the second - (cb) => nodes[1].pubsub.publish('pubsub', data, cb), - // Wait a moment before unsubscribing - (cb) => setTimeout(cb, 500), - // unsubscribe on the first - (cb) => nodes[0].pubsub.unsubscribe('pubsub', handler, cb), - // Stop both nodes - (cb) => stopTwo(nodes, cb) - ], (err) => { - // Verify there was no error, and mark the expect - expect(err).to.not.exist().mark() - }) - }) - it('start two nodes and send one message, then unsubscribe without handler', (done) => { - // Check the final series error, and the publish handler - expect(3).checks(done) - - let nodes - const data = Buffer.from('test') - const handler = (msg) => { - // verify the data is correct and mark the expect - expect(msg.data).to.eql(data).mark() - } - - series([ - // Start the nodes - (cb) => startTwo((err, _nodes) => { - nodes = _nodes - cb(err) - }), - // subscribe on the first - (cb) => nodes[0].pubsub.subscribe('pubsub', handler, {}, cb), - // Wait a moment before publishing - (cb) => setTimeout(cb, 500), - // publish on the second - (cb) => nodes[1].pubsub.publish('pubsub', data, cb), - // ls subscripts - (cb) => nodes[1].pubsub.ls(cb), - // get subscribed peers - (cb) => nodes[1].pubsub.peers('pubsub', cb), - // Wait a moment before unsubscribing - (cb) => setTimeout(cb, 500), - // unsubscribe from all - (cb) => nodes[0].pubsub.unsubscribe('pubsub', null, cb), - // Verify unsubscribed - (cb) => { - nodes[0].pubsub.ls((err, topics) => { - expect(topics.length).to.eql(0).mark() - cb(err) - }) - }, - // Stop both nodes - (cb) => stopTwo(nodes, cb) - ], (err) => { - // Verify there was no error, and mark the expect - expect(err).to.not.exist().mark() - }) - }) - it('publish should fail if data is not a buffer nor a string', (done) => { - createNode('/ip4/0.0.0.0/tcp/0', { - config: { - peerDiscovery: { - mdns: { - enabled: false - } - }, - pubsub: { - enabled: true - } - } - }, (err, node) => { - expect(err).to.not.exist() - - node.start((err) => { - expect(err).to.not.exist() - - node.pubsub.publish('pubsub', 10, (err) => { - expect(err).to.exist() - expect(err.code).to.equal('ERR_DATA_IS_NOT_VALID') - - done() - }) - }) - }) - }) - }) - - describe('.pubsub on using floodsub', () => { - it('start two nodes and send one message, then unsubscribe', (done) => { - // Check the final series error, and the publish handler - expect(2).checks(done) - - let nodes - const data = Buffer.from('test') - const handler = (msg) => { - // verify the data is correct and mark the expect - expect(msg.data).to.eql(data).mark() - } - - series([ - // Start the nodes - (cb) => startTwo({ - modules: { - pubsub: Floodsub - } - }, (err, _nodes) => { - nodes = _nodes - cb(err) - }), - // subscribe on the first - (cb) => nodes[0].pubsub.subscribe('pubsub', handler, cb), - // Wait a moment before publishing - (cb) => setTimeout(cb, 500), - // publish on the second - (cb) => nodes[1].pubsub.publish('pubsub', data, cb), - // Wait a moment before unsubscribing - (cb) => setTimeout(cb, 500), - // unsubscribe on the first - (cb) => nodes[0].pubsub.unsubscribe('pubsub', handler, cb), - // Stop both nodes - (cb) => stopTwo(nodes, cb) - ], (err) => { - // Verify there was no error, and mark the expect - expect(err).to.not.exist().mark() - }) - }) - it('start two nodes and send one message, then unsubscribe (promises)', async () => { - let messageRecieved - const data = Buffer.from('test') - const handler = (msg) => { - expect(msg.data).to.eql(data) - messageRecieved = true - } - - // Start the nodes - const nodes = await promisify(startTwo)({ - modules: { - pubsub: Floodsub - } - }) - - // subscribe on the first - await nodes[0].pubsub.subscribe('pubsub', handler) - // Wait a moment before publishing - await delay(500) - // publish on the second - await nodes[1].pubsub.publish('pubsub', data) - // Wait a moment before unsubscribing - await delay(500) - // unsubscribe on the first - await nodes[0].pubsub.unsubscribe('pubsub', handler) - // Stop both nodes - await promisify(stopTwo)(nodes) - - expect(messageRecieved).to.be.true() - }) - it('start two nodes and send one message, then unsubscribe without handler', (done) => { - // Check the final series error, and the publish handler - expect(3).checks(done) - - let nodes - const data = Buffer.from('test') - const handler = (msg) => { - // verify the data is correct and mark the expect - expect(msg.data).to.eql(data).mark() - } - - series([ - // Start the nodes - (cb) => startTwo({ - modules: { - pubsub: Floodsub - } - }, (err, _nodes) => { - nodes = _nodes - cb(err) - }), - // subscribe on the first - (cb) => nodes[0].pubsub.subscribe('pubsub', handler, cb), - // Wait a moment before publishing - (cb) => setTimeout(cb, 500), - // publish on the second - (cb) => nodes[1].pubsub.publish('pubsub', data, cb), - // Wait a moment before unsubscribing - (cb) => setTimeout(cb, 500), - // unsubscribe from all - (cb) => nodes[0].pubsub.unsubscribe('pubsub', null, cb), - // Verify unsubscribed - (cb) => { - nodes[0].pubsub.ls((err, topics) => { - expect(topics.length).to.eql(0).mark() - cb(err) - }) - }, - // Stop both nodes - (cb) => stopTwo(nodes, cb) - ], (err) => { - // Verify there was no error, and mark the expect - expect(err).to.not.exist().mark() - }) - }) - it('publish should fail if data is not a buffer', (done) => { - createNode('/ip4/0.0.0.0/tcp/0', { - config: { - peerDiscovery: { - mdns: { - enabled: false - } - }, - pubsub: { - enabled: true - } - }, - modules: { - pubsub: Floodsub - } - }, (err, node) => { - expect(err).to.not.exist() - - node.start((err) => { - expect(err).to.not.exist() - - node.pubsub.publish('pubsub', 10, (err) => { - expect(err).to.exist() - expect(err.code).to.equal('ERR_DATA_IS_NOT_VALID') - - done() - }) - }) - }) - }) - }) - - describe('.pubsub off', () => { - it('fail to use pubsub if disabled', (done) => { - createNode('/ip4/0.0.0.0/tcp/0', { - config: { - peerDiscovery: { - mdns: { - enabled: false - } - } - } - }, (err, node) => { - expect(err).to.not.exist() - expect(node.pubsub).to.not.exist() - done() - }) - }) - }) - - describe('.pubsub on and node not started', () => { - let libp2pNode - - before(function (done) { - createNode('/ip4/0.0.0.0/tcp/0', { - config: { - peerDiscovery: { - mdns: { - enabled: false - } - }, - pubsub: { - enabled: true - } - } - }, (err, node) => { - expect(err).to.not.exist() - - libp2pNode = node - done() - }) - }) - - it('fail to subscribe if node not started yet', (done) => { - libp2pNode.pubsub.subscribe('pubsub', () => { }, (err) => { - expect(err).to.exist() - expect(err.code).to.equal(codes.PUBSUB_NOT_STARTED) - - done() - }) - }) - - it('fail to unsubscribe if node not started yet', (done) => { - libp2pNode.pubsub.unsubscribe('pubsub', () => { }, (err) => { - expect(err).to.exist() - expect(err.code).to.equal(codes.PUBSUB_NOT_STARTED) - - done() - }) - }) - - it('fail to publish if node not started yet', (done) => { - libp2pNode.pubsub.publish('pubsub', Buffer.from('data'), (err) => { - expect(err).to.exist() - expect(err.code).to.equal(codes.PUBSUB_NOT_STARTED) - - done() - }) - }) - - it('fail to ls if node not started yet', (done) => { - libp2pNode.pubsub.ls((err) => { - expect(err).to.exist() - expect(err.code).to.equal(codes.PUBSUB_NOT_STARTED) - - done() - }) - }) - - it('fail to get subscribed peers to a topic if node not started yet', (done) => { - libp2pNode.pubsub.peers('pubsub', (err) => { - expect(err).to.exist() - expect(err.code).to.equal(codes.PUBSUB_NOT_STARTED) - - done() - }) - }) - }) - - describe('.pubsub config', () => { - it('toggle all pubsub options off (except enabled)', done => { - expect(3).checks(done) - - class PubSubSpy { - constructor (node, config) { - expect(config).to.be.eql({ - enabled: true, - emitSelf: false, - signMessages: false, - strictSigning: false - }).mark() - } - } - - createNode('/ip4/0.0.0.0/tcp/0', { - modules: { - pubsub: PubSubSpy - }, - config: { - pubsub: { - enabled: true, - emitSelf: false, - signMessages: false, - strictSigning: false - } - } - }, (err, node) => { - expect(err).to.not.exist().mark() - expect(node).to.exist().mark() - }) - }) - - it('toggle all pubsub options on', done => { - expect(3).checks(done) - - class PubSubSpy { - constructor (node, config) { - expect(config).to.be.eql({ - enabled: true, - emitSelf: true, - signMessages: true, - strictSigning: true - }).mark() - } - } - - createNode('/ip4/0.0.0.0/tcp/0', { - modules: { - pubsub: PubSubSpy - }, - config: { - pubsub: { - enabled: true, - emitSelf: true, - signMessages: true, - strictSigning: true - } - } - }, (err, node) => { - expect(err).to.not.exist().mark() - expect(node).to.exist().mark() - }) - }) - }) -}) diff --git a/test/stats.js b/test/stats.js deleted file mode 100644 index 08e05b4f..00000000 --- a/test/stats.js +++ /dev/null @@ -1,29 +0,0 @@ -/* eslint-env mocha */ -'use strict' - -const chai = require('chai') -chai.use(require('dirty-chai')) -const expect = chai.expect - -const createNode = require('./utils/create-node') - -describe('libp2p', () => { - it('has stats', (done) => { - createNode('/ip4/127.0.0.1/tcp/0', { - config: { - peerDiscovery: { - mdns: { - enabled: false - } - } - } - }, (err, node) => { - expect(err).to.not.exist() - node.start((err) => { - expect(err).to.not.exist() - expect(node.stats).to.exist() - node.stop(done) - }) - }) - }) -}) diff --git a/test/stream-muxing.node.js b/test/stream-muxing.node.js deleted file mode 100644 index d559664e..00000000 --- a/test/stream-muxing.node.js +++ /dev/null @@ -1,338 +0,0 @@ -/* eslint-env mocha */ -'use strict' - -const chai = require('chai') -chai.use(require('dirty-chai')) -const expect = chai.expect -const parallel = require('async/parallel') -const series = require('async/series') -const pMplex = require('pull-mplex') -const Mplex = require('libp2p-mplex') -const SPDY = require('libp2p-spdy') -const createNode = require('./utils/create-node') -const tryEcho = require('./utils/try-echo') -const echo = require('./utils/echo') - -function test (nodeA, nodeB, callback) { - nodeA.dialProtocol(nodeB.peerInfo, '/echo/1.0.0', (err, conn) => { - expect(err).to.not.exist() - tryEcho(conn, callback) - }) -} - -function teardown (nodeA, nodeB, callback) { - parallel([ - (cb) => nodeA.stop(cb), - (cb) => nodeB.stop(cb) - ], callback) -} - -describe('stream muxing', () => { - it('spdy only', function (done) { - let nodeA - let nodeB - - function setup (callback) { - parallel([ - (cb) => createNode('/ip4/0.0.0.0/tcp/0', { - modules: { - streamMuxer: [SPDY] - } - }, (err, node) => { - expect(err).to.not.exist() - nodeA = node - node.handle('/echo/1.0.0', echo) - node.start(cb) - }), - (cb) => createNode('/ip4/0.0.0.0/tcp/0', { - modules: { - streamMuxer: [SPDY] - } - }, (err, node) => { - expect(err).to.not.exist() - nodeB = node - node.handle('/echo/1.0.0', echo) - node.start(cb) - }) - ], callback) - } - - series([ - (cb) => setup(cb), - (cb) => test(nodeA, nodeB, cb), - (cb) => teardown(nodeA, nodeB, cb) - ], done) - }) - - it('mplex only', (done) => { - let nodeA - let nodeB - - function setup (callback) { - parallel([ - (cb) => createNode('/ip4/0.0.0.0/tcp/0', { - modules: { - streamMuxer: [Mplex] - } - }, (err, node) => { - expect(err).to.not.exist() - nodeA = node - node.handle('/echo/1.0.0', echo) - node.start(cb) - }), - (cb) => createNode('/ip4/0.0.0.0/tcp/0', { - modules: { - streamMuxer: [Mplex] - } - }, (err, node) => { - expect(err).to.not.exist() - nodeB = node - node.handle('/echo/1.0.0', echo) - node.start(cb) - }) - ], callback) - } - - series([ - (cb) => setup(cb), - (cb) => test(nodeA, nodeB, cb), - (cb) => teardown(nodeA, nodeB, cb) - ], done) - }) - - it('pMplex only', (done) => { - let nodeA - let nodeB - - function setup (callback) { - parallel([ - (cb) => createNode('/ip4/0.0.0.0/tcp/0', { - modules: { - streamMuxer: [pMplex] - } - }, (err, node) => { - expect(err).to.not.exist() - nodeA = node - node.handle('/echo/1.0.0', echo) - node.start(cb) - }), - (cb) => createNode('/ip4/0.0.0.0/tcp/0', { - modules: { - streamMuxer: [pMplex] - } - }, (err, node) => { - expect(err).to.not.exist() - nodeB = node - node.handle('/echo/1.0.0', echo) - node.start(cb) - }) - ], callback) - } - - series([ - (cb) => setup(cb), - (cb) => test(nodeA, nodeB, cb), - (cb) => teardown(nodeA, nodeB, cb) - ], done) - }) - - it('spdy + mplex', function (done) { - this.timeout(5000) - - let nodeA - let nodeB - - function setup (callback) { - parallel([ - (cb) => createNode('/ip4/0.0.0.0/tcp/0', { - modules: { - streamMuxer: [Mplex] - } - }, (err, node) => { - expect(err).to.not.exist() - nodeA = node - node.handle('/echo/1.0.0', echo) - node.start(cb) - }), - (cb) => createNode('/ip4/0.0.0.0/tcp/0', { - modules: { - streamMuxer: [SPDY, Mplex] - } - }, (err, node) => { - expect(err).to.not.exist() - nodeB = node - node.handle('/echo/1.0.0', echo) - node.start(cb) - }) - ], callback) - } - - series([ - (cb) => setup(cb), - (cb) => test(nodeA, nodeB, cb), - (cb) => teardown(nodeA, nodeB, cb) - ], done) - }) - - it('mplex + pull-mplex', function (done) { - this.timeout(5000) - - let nodeA - let nodeB - - function setup (callback) { - parallel([ - (cb) => createNode('/ip4/0.0.0.0/tcp/0', { - modules: { - streamMuxer: [Mplex] - } - }, (err, node) => { - expect(err).to.not.exist() - nodeA = node - node.handle('/echo/1.0.0', echo) - node.start(cb) - }), - (cb) => createNode('/ip4/0.0.0.0/tcp/0', { - modules: { - streamMuxer: [pMplex] - } - }, (err, node) => { - expect(err).to.not.exist() - nodeB = node - node.handle('/echo/1.0.0', echo) - node.start(cb) - }) - ], callback) - } - - series([ - (cb) => setup(cb), - (cb) => test(nodeA, nodeB, cb), - (cb) => teardown(nodeA, nodeB, cb) - ], done) - }) - - it('spdy + mplex in reverse muxer order', function (done) { - this.timeout(5 * 1000) - - let nodeA - let nodeB - - function setup (callback) { - parallel([ - (cb) => createNode('/ip4/0.0.0.0/tcp/0', { - modules: { - streamMuxer: [SPDY, Mplex] - } - }, (err, node) => { - expect(err).to.not.exist() - nodeA = node - node.handle('/echo/1.0.0', echo) - node.start(cb) - }), - (cb) => createNode('/ip4/0.0.0.0/tcp/0', { - modules: { - streamMuxer: [Mplex, SPDY] - } - }, (err, node) => { - expect(err).to.not.exist() - nodeB = node - node.handle('/echo/1.0.0', echo) - node.start(cb) - }) - ], callback) - } - - series([ - (cb) => setup(cb), - (cb) => test(nodeA, nodeB, cb), - (cb) => teardown(nodeA, nodeB, cb) - ], done) - }) - - it('spdy + pull-mplex in reverse muxer order', function (done) { - this.timeout(5 * 1000) - - let nodeA - let nodeB - - function setup (callback) { - parallel([ - (cb) => createNode('/ip4/0.0.0.0/tcp/0', { - modules: { - streamMuxer: [SPDY, pMplex] - } - }, (err, node) => { - expect(err).to.not.exist() - nodeA = node - node.handle('/echo/1.0.0', echo) - node.start(cb) - }), - (cb) => createNode('/ip4/0.0.0.0/tcp/0', { - modules: { - streamMuxer: [pMplex, SPDY] - } - }, (err, node) => { - expect(err).to.not.exist() - nodeB = node - node.handle('/echo/1.0.0', echo) - node.start(cb) - }) - ], callback) - } - - series([ - (cb) => setup(cb), - (cb) => test(nodeA, nodeB, cb), - (cb) => teardown(nodeA, nodeB, cb) - ], done) - }) - - it('one without the other fails to establish a muxedConn', function (done) { - this.timeout(5 * 1000) - - let nodeA - let nodeB - - function setup (callback) { - parallel([ - (cb) => createNode('/ip4/0.0.0.0/tcp/0', { - modules: { - streamMuxer: [SPDY] - } - }, (err, node) => { - expect(err).to.not.exist() - nodeA = node - node.handle('/echo/1.0.0', echo) - node.start(cb) - }), - (cb) => createNode('/ip4/0.0.0.0/tcp/0', { - modules: { - streamMuxer: [Mplex] - } - }, (err, node) => { - expect(err).to.not.exist() - nodeB = node - node.handle('/echo/1.0.0', echo) - node.start(cb) - }) - ], callback) - } - - series([ - (cb) => setup(cb), - (cb) => { - // it will just 'warm up a conn' - expect(Object.keys(nodeA._switch.muxers)).to.have.length(1) - expect(Object.keys(nodeB._switch.muxers)).to.have.length(1) - - nodeA.dialFSM(nodeB.peerInfo, (err, connFSM) => { - expect(err).to.not.exist() - // The connection should fall back to 'unmuxed' - connFSM.once('unmuxed', () => cb()) - }) - }, - (cb) => teardown(nodeA, nodeB, cb) - ], done) - }) -}) diff --git a/test/switch/browser.js b/test/switch/browser.js deleted file mode 100644 index 5364fa17..00000000 --- a/test/switch/browser.js +++ /dev/null @@ -1,12 +0,0 @@ -/* eslint-env mocha */ -'use strict' - -const wrtcSupport = self.RTCPeerConnection && ('createDataChannel' in self.RTCPeerConnection.prototype) - -require('./transports.browser.js') -require('./swarm-muxing+websockets.browser') - -if (wrtcSupport) { - require('./t-webrtc-star.browser') - require('./swarm-muxing+webrtc-star.browser') -} diff --git a/test/switch/connection.node.js b/test/switch/connection.node.js deleted file mode 100644 index f8447c0c..00000000 --- a/test/switch/connection.node.js +++ /dev/null @@ -1,451 +0,0 @@ -/* eslint-env mocha */ -'use strict' - -const chai = require('chai') -const expect = chai.expect -chai.use(require('dirty-chai')) -chai.use(require('chai-checkmark')) -const sinon = require('sinon') -const PeerBook = require('peer-book') -const WS = require('libp2p-websockets') -const parallel = require('async/parallel') -const secio = require('libp2p-secio') -const pull = require('pull-stream') -const multiplex = require('pull-mplex') -const spdy = require('libp2p-spdy') -const Protector = require('../../src/pnet') -const generatePSK = Protector.generate - -const psk = Buffer.alloc(95) -generatePSK(psk) - -const ConnectionFSM = require('../../src/switch/connection') -const Switch = require('../../src/switch') -const createInfos = require('./utils').createInfos - -describe('ConnectionFSM', () => { - let spdySwitch - let listenerSwitch - let dialerSwitch - - before((done) => { - createInfos(3, (err, infos) => { - if (err) { - return done(err) - } - - dialerSwitch = new Switch(infos.shift(), new PeerBook()) - dialerSwitch._peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/15451/ws') - dialerSwitch.connection.crypto(secio.tag, secio.encrypt) - dialerSwitch.connection.addStreamMuxer(multiplex) - dialerSwitch.transport.add('ws', new WS()) - - listenerSwitch = new Switch(infos.shift(), new PeerBook()) - listenerSwitch._peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/15452/ws') - listenerSwitch.connection.crypto(secio.tag, secio.encrypt) - listenerSwitch.connection.addStreamMuxer(multiplex) - listenerSwitch.transport.add('ws', new WS()) - - spdySwitch = new Switch(infos.shift(), new PeerBook()) - spdySwitch._peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/15453/ws') - spdySwitch.connection.crypto(secio.tag, secio.encrypt) - spdySwitch.connection.addStreamMuxer(spdy) - spdySwitch.transport.add('ws', new WS()) - - parallel([ - (cb) => dialerSwitch.start(cb), - (cb) => listenerSwitch.start(cb), - (cb) => spdySwitch.start(cb) - ], (err) => { - done(err) - }) - }) - }) - - after((done) => { - parallel([ - (cb) => dialerSwitch.stop(cb), - (cb) => listenerSwitch.stop(cb), - (cb) => spdySwitch.stop(cb) - ], () => { - done() - }) - }) - - it('should have a default state of disconnected', () => { - const connection = new ConnectionFSM({ - _switch: dialerSwitch, - peerInfo: listenerSwitch._peerInfo - }) - - expect(connection.getState()).to.equal('DISCONNECTED') - }) - - it('should emit an error with an invalid transition', (done) => { - const connection = new ConnectionFSM({ - _switch: dialerSwitch, - peerInfo: listenerSwitch._peerInfo - }) - - expect(connection.getState()).to.equal('DISCONNECTED') - - connection.once('error', (err) => { - expect(err).to.have.property('code', 'INVALID_STATE_TRANSITION') - done() - }) - connection.upgrade() - }) - - it('.dial should create a basic connection', (done) => { - const connection = new ConnectionFSM({ - _switch: dialerSwitch, - peerInfo: listenerSwitch._peerInfo - }) - - connection.once('connected', (conn) => { - expect(conn).to.exist() - done() - }) - - connection.dial() - }) - - it('should be able to close with an error and not throw', (done) => { - const connection = new ConnectionFSM({ - _switch: dialerSwitch, - peerInfo: listenerSwitch._peerInfo - }) - - connection.once('connected', (conn) => { - expect(conn).to.exist() - expect(() => connection.close(new Error('shutting down'))).to.not.throw() - done() - }) - - connection.dial() - }) - - it('should emit warning on dial failed attempt', (done) => { - const connection = new ConnectionFSM({ - _switch: dialerSwitch, - peerInfo: listenerSwitch._peerInfo - }) - - const stub = sinon.stub(dialerSwitch.transport, 'dial').callsArgWith(2, [ - new Error('address in use') - ]) - - connection.once('error:connection_attempt_failed', (errors) => { - expect(errors).to.have.length(1).mark() - stub.restore() - }) - - connection.once('error', (err) => { - expect(err).to.exist().mark() - }) - - expect(2).checks(done) - - connection.dial() - }) - - it('should ignore concurrent dials', () => { - const connection = new ConnectionFSM({ - _switch: dialerSwitch, - peerInfo: listenerSwitch._peerInfo - }) - - const stub = sinon.stub(connection, '_onDialing') - - connection.dial() - connection.dial() - - expect(stub.callCount).to.equal(1) - }) - - it('should be able to encrypt a basic connection', (done) => { - const connection = new ConnectionFSM({ - _switch: dialerSwitch, - peerInfo: listenerSwitch._peerInfo - }) - - connection.once('connected', (conn) => { - expect(conn).to.exist() - connection.encrypt() - }) - connection.once('encrypted', (conn) => { - expect(conn).to.exist() - done() - }) - - connection.dial() - }) - - it('should disconnect on encryption failure', (done) => { - const connection = new ConnectionFSM({ - _switch: dialerSwitch, - peerInfo: listenerSwitch._peerInfo - }) - - const stub = sinon.stub(dialerSwitch.crypto, 'encrypt') - .callsArgWith(3, new Error('fail encrypt')) - - connection.once('connected', (conn) => { - expect(conn).to.exist() - connection.encrypt() - }) - connection.once('close', () => { - stub.restore() - done() - }) - connection.once('encrypted', () => { - throw new Error('should not encrypt') - }) - - connection.dial() - }) - - it('should be able to upgrade an encrypted connection', (done) => { - const connection = new ConnectionFSM({ - _switch: dialerSwitch, - peerInfo: listenerSwitch._peerInfo - }) - - connection.once('connected', (conn) => { - expect(conn).to.exist() - connection.encrypt() - }) - connection.once('encrypted', (conn) => { - expect(conn).to.exist() - connection.upgrade() - }) - connection.once('muxed', (conn) => { - expect(conn.multicodec).to.equal(multiplex.multicodec) - done() - }) - - connection.dial() - }) - - it('should fail to upgrade a connection with incompatible muxers', (done) => { - const connection = new ConnectionFSM({ - _switch: dialerSwitch, - peerInfo: spdySwitch._peerInfo - }) - - connection.once('connected', (conn) => { - expect(conn).to.exist() - connection.encrypt() - }) - connection.once('encrypted', (conn) => { - expect(conn).to.exist() - connection.upgrade() - }) - connection.once('error:upgrade_failed', (err) => { - expect(err).to.exist() - done() - }) - - connection.dial() - }) - - it('should be able to handshake a protocol over a muxed connection', (done) => { - const connection = new ConnectionFSM({ - _switch: dialerSwitch, - peerInfo: listenerSwitch._peerInfo - }) - - listenerSwitch.handle('/muxed-conn-test/1.0.0', (_, conn) => { - return pull(conn, conn) - }) - - connection.once('connected', (conn) => { - expect(conn).to.exist() - connection.encrypt() - }) - connection.once('encrypted', (conn) => { - expect(conn).to.exist() - connection.upgrade() - }) - connection.once('muxed', (conn) => { - expect(conn.multicodec).to.equal(multiplex.multicodec) - - connection.shake('/muxed-conn-test/1.0.0', (err, protocolConn) => { - expect(err).to.not.exist() - expect(protocolConn).to.exist() - done() - }) - }) - - connection.dial() - }) - - it('should not return a connection when handshaking with no protocol', (done) => { - const connection = new ConnectionFSM({ - _switch: dialerSwitch, - peerInfo: listenerSwitch._peerInfo - }) - - listenerSwitch.handle('/muxed-conn-test/1.0.0', (_, conn) => { - return pull(conn, conn) - }) - - connection.once('connected', (conn) => { - expect(conn).to.exist() - connection.encrypt() - }) - connection.once('encrypted', (conn) => { - expect(conn).to.exist() - connection.upgrade() - }) - connection.once('muxed', (conn) => { - expect(conn.multicodec).to.equal(multiplex.multicodec) - - connection.shake(null, (err, protocolConn) => { - expect(err).to.not.exist() - expect(protocolConn).to.not.exist() - done() - }) - }) - - connection.dial() - }) - - describe('with no muxers', () => { - let oldMuxers - before(() => { - oldMuxers = dialerSwitch.muxers - dialerSwitch.muxers = {} - }) - - after(() => { - dialerSwitch.muxers = oldMuxers - }) - - it('should be able to handshake a protocol over a basic connection', (done) => { - const connection = new ConnectionFSM({ - _switch: dialerSwitch, - peerInfo: listenerSwitch._peerInfo - }) - - listenerSwitch.handle('/unmuxed-conn-test/1.0.0', (_, conn) => { - return pull(conn, conn) - }) - - connection.once('connected', (conn) => { - expect(conn).to.exist() - connection.encrypt() - }) - connection.once('encrypted', (conn) => { - expect(conn).to.exist() - connection.upgrade() - }) - connection.once('muxed', () => { - throw new Error('connection shouldnt be muxed') - }) - connection.once('unmuxed', (conn) => { - expect(conn).to.exist() - - connection.shake('/unmuxed-conn-test/1.0.0', (err, protocolConn) => { - expect(err).to.not.exist() - expect(protocolConn).to.exist() - done() - }) - }) - - connection.dial() - }) - }) - - describe('with a protector', () => { - // Restart the switches with protectors - before((done) => { - parallel([ - (cb) => dialerSwitch.stop(cb), - (cb) => listenerSwitch.stop(cb) - ], () => { - dialerSwitch.protector = new Protector(psk) - listenerSwitch.protector = new Protector(psk) - - parallel([ - (cb) => dialerSwitch.start(cb), - (cb) => listenerSwitch.start(cb) - ], done) - }) - }) - - afterEach(() => { - sinon.restore() - }) - - it('should be able to protect a basic connection', (done) => { - const connection = new ConnectionFSM({ - _switch: dialerSwitch, - peerInfo: listenerSwitch._peerInfo - }) - - connection.once('private', (conn) => { - expect(conn).to.exist() - done() - }) - - connection.once('connected', (conn) => { - expect(conn).to.exist() - connection.protect() - }) - - connection.dial() - }) - - it('should close on failed protection', (done) => { - const connection = new ConnectionFSM({ - _switch: dialerSwitch, - peerInfo: listenerSwitch._peerInfo - }) - - const error = new Error('invalid key') - const stub = sinon.stub(dialerSwitch.protector, 'protect').callsFake((_, cb) => { - cb(error) - }) - - expect(3).check(done) - - connection.once('close', () => { - expect(stub.callCount).to.eql(1).mark() - }) - - connection.once('error', (err) => { - expect(err).to.eql(error).mark() - }) - - connection.once('connected', (conn) => { - expect(conn).to.exist().mark() - connection.protect() - }) - - connection.dial() - }) - - it('should be able to encrypt a protected connection', (done) => { - const connection = new ConnectionFSM({ - _switch: dialerSwitch, - peerInfo: listenerSwitch._peerInfo - }) - - connection.once('connected', (conn) => { - expect(conn).to.exist() - connection.protect() - }) - connection.once('private', (conn) => { - expect(conn).to.exist() - connection.encrypt() - }) - connection.once('encrypted', (conn) => { - expect(conn).to.exist() - done() - }) - - connection.dial() - }) - }) -}) diff --git a/test/switch/constructor.spec.js b/test/switch/constructor.spec.js deleted file mode 100644 index 7d932aeb..00000000 --- a/test/switch/constructor.spec.js +++ /dev/null @@ -1,15 +0,0 @@ -/* eslint-env mocha */ -'use strict' - -const chai = require('chai') -const dirtyChai = require('dirty-chai') -const expect = chai.expect -chai.use(dirtyChai) - -const Switch = require('../../src/switch') - -describe('create Switch instance', () => { - it('throws on missing peerInfo', () => { - expect(() => new Switch()).to.throw(/You must provide a `peerInfo`/) - }) -}) diff --git a/test/switch/dial-fsm.node.js b/test/switch/dial-fsm.node.js deleted file mode 100644 index 78faf6dc..00000000 --- a/test/switch/dial-fsm.node.js +++ /dev/null @@ -1,405 +0,0 @@ -/* eslint-env mocha */ -/* eslint max-nested-callbacks: ["error", 5] */ -'use strict' - -const chai = require('chai') -const dirtyChai = require('dirty-chai') -const expect = chai.expect -chai.use(require('chai-checkmark')) -chai.use(dirtyChai) -const sinon = require('sinon') -const PeerBook = require('peer-book') -const parallel = require('async/parallel') -const series = require('async/series') -const WS = require('libp2p-websockets') -const TCP = require('libp2p-tcp') -const secio = require('libp2p-secio') -const multiplex = require('pull-mplex') -const pull = require('pull-stream') -const identify = require('../../src/identify') - -const utils = require('./utils') -const createInfos = utils.createInfos -const Switch = require('../../src/switch') - -describe('dialFSM', () => { - let switchA - let switchB - let switchC - let switchDialOnly - let peerAId - let peerBId - let protocol - - before((done) => createInfos(4, (err, infos) => { - expect(err).to.not.exist() - - const peerA = infos[0] - const peerB = infos[1] - const peerC = infos[2] - const peerDialOnly = infos[3] - - peerAId = peerA.id.toB58String() - peerBId = peerB.id.toB58String() - - peerA.multiaddrs.add('/ip4/0.0.0.0/tcp/0') - peerB.multiaddrs.add('/ip4/0.0.0.0/tcp/0') - peerC.multiaddrs.add('/ip4/0.0.0.0/tcp/0/ws') - // Give peer C a tcp address we wont actually support - peerC.multiaddrs.add('/ip4/0.0.0.0/tcp/0') - - switchA = new Switch(peerA, new PeerBook()) - switchB = new Switch(peerB, new PeerBook()) - switchC = new Switch(peerC, new PeerBook()) - switchDialOnly = new Switch(peerDialOnly, new PeerBook()) - - switchA.transport.add('tcp', new TCP()) - switchB.transport.add('tcp', new TCP()) - switchC.transport.add('ws', new WS()) - switchDialOnly.transport.add('ws', new WS()) - - switchA.connection.crypto(secio.tag, secio.encrypt) - switchB.connection.crypto(secio.tag, secio.encrypt) - switchC.connection.crypto(secio.tag, secio.encrypt) - switchDialOnly.connection.crypto(secio.tag, secio.encrypt) - - switchA.connection.addStreamMuxer(multiplex) - switchB.connection.addStreamMuxer(multiplex) - switchC.connection.addStreamMuxer(multiplex) - switchDialOnly.connection.addStreamMuxer(multiplex) - - switchA.connection.reuse() - switchB.connection.reuse() - switchC.connection.reuse() - switchDialOnly.connection.reuse() - - parallel([ - (cb) => switchA.start(cb), - (cb) => switchB.start(cb), - (cb) => switchC.start(cb) - ], done) - })) - - after((done) => { - parallel([ - (cb) => switchA.stop(cb), - (cb) => switchB.stop(cb), - (cb) => switchC.stop(cb) - ], done) - }) - - afterEach(() => { - switchA.unhandle(protocol) - switchB.unhandle(protocol) - switchC.unhandle(protocol) - protocol = null - }) - - it('should emit `error:connection_attempt_failed` when a transport fails to dial', (done) => { - protocol = '/warn/1.0.0' - switchC.handle(protocol, () => { }) - - switchA.dialFSM(switchC._peerInfo, protocol, (err, connFSM) => { - expect(err).to.not.exist() - connFSM.once('error:connection_attempt_failed', (errors) => { - expect(errors).to.be.an('array') - expect(errors).to.have.length(1) - done() - }) - }) - }) - - it('should emit an `error` event when a it cannot dial a peer', (done) => { - protocol = '/error/1.0.0' - switchC.handle(protocol, () => { }) - - switchA.dialer.clearDenylist(switchC._peerInfo) - switchA.dialFSM(switchC._peerInfo, protocol, (err, connFSM) => { - expect(err).to.not.exist() - connFSM.once('error', (err) => { - expect(err).to.be.exist() - expect(err).to.have.property('code', 'CONNECTION_FAILED') - done() - }) - }) - }) - - it('should error when the peer is denylisted', (done) => { - protocol = '/error/1.0.0' - switchC.handle(protocol, () => { }) - - switchA.dialer.clearDenylist(switchC._peerInfo) - switchA.dialFSM(switchC._peerInfo, protocol, (err, connFSM) => { - expect(err).to.not.exist() - connFSM.once('error', () => { - // dial with the denylist - switchA.dialFSM(switchC._peerInfo, protocol, (err) => { - expect(err).to.exist() - expect(err.code).to.eql('ERR_DENIED') - done() - }) - }) - }) - }) - - it('should not denylist a peer that was successfully connected', (done) => { - protocol = '/nodenylist/1.0.0' - switchB.handle(protocol, () => { }) - - switchA.dialer.clearDenylist(switchB._peerInfo) - switchA.dialFSM(switchB._peerInfo, protocol, (err, connFSM) => { - expect(err).to.not.exist() - connFSM.once('connection', () => { - connFSM.once('close', () => { - // peer should not be denylisted - switchA.dialFSM(switchB._peerInfo, protocol, (err, conn) => { - expect(err).to.not.exist() - conn.once('close', done) - conn.close() - }) - }) - connFSM.close(new Error('bad things')) - }) - }) - }) - - it('should clear the denylist for a peer that connected to us', (done) => { - series([ - // Attempt to dial the peer that's not listening - (cb) => switchC.dial(switchDialOnly._peerInfo, (err) => { - expect(err).to.exist() - cb() - }), - // Dial from the dial only peer - (cb) => switchDialOnly.dial(switchC._peerInfo, (err) => { - expect(err).to.not.exist() - // allow time for muxing to occur - setTimeout(cb, 100) - }), - // "Dial" to the dial only peer, this should reuse the existing connection - (cb) => switchC.dial(switchDialOnly._peerInfo, (err) => { - expect(err).to.not.exist() - cb() - }) - ], (err) => { - expect(err).to.not.exist() - done() - }) - }) - - it('should emit a `closed` event when closed', (done) => { - protocol = '/closed/1.0.0' - switchB.handle(protocol, () => { }) - - switchA.dialFSM(switchB._peerInfo, protocol, (err, connFSM) => { - expect(err).to.not.exist() - - connFSM.once('close', () => { - expect(switchA.connection.getAllById(peerBId)).to.have.length(0) - done() - }) - - connFSM.once('muxed', () => { - expect(switchA.connection.getAllById(peerBId)).to.have.length(1) - connFSM.close() - }) - }) - }) - - it('should have the peers protocols once connected', (done) => { - protocol = '/lscheck/1.0.0' - switchB.handle(protocol, () => { }) - - expect(4).checks(done) - - switchB.once('peer-mux-established', (peerInfo) => { - const peerB = switchA._peerBook.get(switchB._peerInfo.id.toB58String()) - const peerA = switchB._peerBook.get(switchA._peerInfo.id.toB58String()) - // Verify the dialer knows the receiver's protocols - expect(Array.from(peerB.protocols)).to.eql([ - multiplex.multicodec, - identify.multicodec, - protocol - ]).mark() - // Verify the receiver knows the dialer's protocols - expect(Array.from(peerA.protocols)).to.eql([ - multiplex.multicodec, - identify.multicodec - ]).mark() - - switchA.hangUp(switchB._peerInfo) - }) - - switchA.dialFSM(switchB._peerInfo, protocol, (err, connFSM) => { - expect(err).to.not.exist().mark() - - connFSM.once('close', () => { - // Just mark that close was called - expect(true).to.eql(true).mark() - }) - }) - }) - - it('should close when the receiver closes', (done) => { - protocol = '/closed/1.0.0' - switchB.handle(protocol, () => { }) - - // wait for the expects to happen - expect(2).checks(() => { - done() - }) - - switchB.on('peer-mux-established', (peerInfo) => { - if (peerInfo.id.toB58String() === peerAId) { - switchB.removeAllListeners('peer-mux-established') - expect(switchB.connection.getAllById(peerAId)).to.have.length(1).mark() - switchB.connection.getOne(peerAId).close() - } - }) - - switchA.dialFSM(switchB._peerInfo, protocol, (err, connFSM) => { - expect(err).to.not.exist() - - connFSM.once('close', () => { - expect(switchA.connection.getAllById(peerBId)).to.have.length(0).mark() - }) - }) - }) - - it('parallel dials to the same peer should not create new connections', (done) => { - switchB.handle('/parallel/2.0.0', (_, conn) => { pull(conn, conn) }) - - parallel([ - (cb) => switchA.dialFSM(switchB._peerInfo, '/parallel/2.0.0', cb), - (cb) => switchA.dialFSM(switchB._peerInfo, '/parallel/2.0.0', cb) - ], (err, results) => { - expect(err).to.not.exist() - expect(results).to.have.length(2) - expect(switchA.connection.getAllById(peerBId)).to.have.length(1) - - switchA.hangUp(switchB._peerInfo, () => { - expect(switchA.connection.getAllById(peerBId)).to.have.length(0) - done() - }) - }) - }) - - it('parallel dials to one another should disconnect on hangup', function (done) { - this.timeout(10e3) - protocol = '/parallel/1.0.0' - - switchA.handle(protocol, (_, conn) => { pull(conn, conn) }) - switchB.handle(protocol, (_, conn) => { pull(conn, conn) }) - - expect(switchA.connection.getAllById(peerBId)).to.have.length(0) - - // Expect 4 `peer-mux-established` events - expect(4).checks(() => { - // Expect 2 `peer-mux-closed`, plus 1 hangup - expect(3).checks(() => { - switchA.removeAllListeners('peer-mux-closed') - switchB.removeAllListeners('peer-mux-closed') - switchA.removeAllListeners('peer-mux-established') - switchB.removeAllListeners('peer-mux-established') - done() - }) - - switchA.hangUp(switchB._peerInfo, (err) => { - expect(err).to.not.exist().mark() - }) - }) - - switchA.on('peer-mux-established', (peerInfo) => { - expect(peerInfo.id.toB58String()).to.eql(peerBId).mark() - }) - switchB.on('peer-mux-established', (peerInfo) => { - expect(peerInfo.id.toB58String()).to.eql(peerAId).mark() - }) - - switchA.on('peer-mux-closed', (peerInfo) => { - expect(peerInfo.id.toB58String()).to.eql(peerBId).mark() - }) - switchB.on('peer-mux-closed', (peerInfo) => { - expect(peerInfo.id.toB58String()).to.eql(peerAId).mark() - }) - - switchA.dialFSM(switchB._peerInfo, protocol, (err, connFSM) => { - expect(err).to.not.exist() - // Hold the dial from A, until switch B is done dialing to ensure - // we have both incoming and outgoing connections - connFSM._state.on('DIALING:leave', (cb) => { - switchB.dialFSM(switchA._peerInfo, protocol, (err, connB) => { - expect(err).to.not.exist() - connB.on('muxed', cb) - }) - }) - }) - }) - - it('parallel dials to one another should disconnect on stop', (done) => { - protocol = '/parallel/1.0.0' - switchA.handle(protocol, (_, conn) => { pull(conn, conn) }) - switchB.handle(protocol, (_, conn) => { pull(conn, conn) }) - - // 2 close checks and 1 hangup check - expect(2).checks(() => { - switchA.removeAllListeners('peer-mux-closed') - switchB.removeAllListeners('peer-mux-closed') - // restart the node for subsequent tests - switchA.start(done) - }) - - switchA.on('peer-mux-closed', (peerInfo) => { - expect(peerInfo.id.toB58String()).to.eql(peerBId).mark() - }) - switchB.on('peer-mux-closed', (peerInfo) => { - expect(peerInfo.id.toB58String()).to.eql(peerAId).mark() - }) - - switchA.dialFSM(switchB._peerInfo, '/parallel/1.0.0', (err, connFSM) => { - expect(err).to.not.exist() - // Hold the dial from A, until switch B is done dialing to ensure - // we have both incoming and outgoing connections - connFSM._state.on('DIALING:leave', (cb) => { - switchB.dialFSM(switchA._peerInfo, '/parallel/1.0.0', (err, connB) => { - expect(err).to.not.exist() - connB.on('muxed', cb) - }) - }) - - connFSM.on('connection', () => { - // Hangup and verify the connections are closed - switchA.stop((err) => { - expect(err).to.not.exist().mark() - }) - }) - }) - }) - - it('queued dials should be aborted on node stop', (done) => { - switchB.handle('/abort-queue/1.0.0', (_, conn) => { pull(conn, conn) }) - - switchA.dialFSM(switchB._peerInfo, '/abort-queue/1.0.0', (err, connFSM) => { - expect(err).to.not.exist() - // 2 conn aborts, 1 close, and 1 stop - expect(4).checks(done) - - connFSM.once('close', (err) => { - expect(err).to.not.exist().mark() - }) - - sinon.stub(connFSM, '_onUpgrading').callsFake(() => { - switchA.dialFSM(switchB._peerInfo, '/abort-queue/1.0.0', (err) => { - expect(err.code).to.eql('DIAL_ABORTED').mark() - }) - switchA.dialFSM(switchB._peerInfo, '/abort-queue/1.0.0', (err) => { - expect(err.code).to.eql('DIAL_ABORTED').mark() - }) - - switchA.stop((err) => { - expect(err).to.not.exist().mark() - }) - }) - }) - }) -}) diff --git a/test/switch/dialSelf.spec.js b/test/switch/dialSelf.spec.js deleted file mode 100644 index c0d543c2..00000000 --- a/test/switch/dialSelf.spec.js +++ /dev/null @@ -1,88 +0,0 @@ -'use strict' - -/* eslint-env mocha */ - -const chai = require('chai') -const dirtyChai = require('dirty-chai') -const expect = chai.expect -chai.use(dirtyChai) - -const { EventEmitter } = require('events') -const PeerBook = require('peer-book') -const Duplex = require('pull-pair/duplex') - -const utils = require('./utils') -const createInfos = utils.createInfos -const Swarm = require('../../src/switch') - -class MockTransport extends EventEmitter { - constructor () { - super() - this.conn = Duplex() - } - - dial (addr, cb) { - const c = this.conn[0] - this.emit('connection', this.conn[1]) - setImmediate(() => cb(null, c)) - return c - } - - listen (addr, cb) { - return cb() - } - - filter (mas) { - return Array.isArray(mas) ? mas : [mas] - } -} - -describe('dial self', () => { - let swarmA - let peerInfos - - before((done) => createInfos(2, (err, infos) => { - expect(err).to.not.exist() - - const peerA = infos.shift() - peerInfos = infos - - peerA.multiaddrs.add('/ip4/127.0.0.1/tcp/9001') - peerA.multiaddrs.add(`/ip4/127.0.0.1/tcp/9001/ipfs/${peerA.id.toB58String()}`) - peerA.multiaddrs.add(`/ip4/127.0.0.1/tcp/9001/p2p-circuit/ipfs/${peerA.id.toB58String()}`) - peerA.multiaddrs.add('/ip4/0.0.0.0/tcp/9001') - peerA.multiaddrs.add(`/ip4/0.0.0.0/tcp/9001/ipfs/${peerA.id.toB58String()}`) - peerA.multiaddrs.add(`/ip4/0.0.0.0/tcp/9001/p2p-circuit/ipfs/${peerA.id.toB58String()}`) - - swarmA = new Swarm(peerA, new PeerBook()) - - swarmA.transport.add('tcp', new MockTransport()) - - done() - })) - - after((done) => swarmA.stop(done)) - - it('node should not be able to dial itself', (done) => { - swarmA.dial(swarmA._peerInfo, (err, conn) => { - expect(err).to.exist() - expect(() => { throw err }).to.throw(/A node cannot dial itself/) - expect(conn).to.not.exist() - done() - }) - }) - - it('node should not be able to dial another peers address that matches its own', (done) => { - const peerB = peerInfos.shift() - peerB.multiaddrs.add('/ip4/127.0.0.1/tcp/9001') - peerB.multiaddrs.add('/ip4/0.0.0.0/tcp/9001') - peerB.multiaddrs.add(`/ip4/0.0.0.0/tcp/9001/ipfs/${peerB.id.toB58String()}`) - - swarmA.dial(peerB, (err, conn) => { - expect(err).to.exist() - expect(err.code).to.eql('CONNECTION_FAILED') - expect(conn).to.not.exist() - done() - }) - }) -}) diff --git a/test/switch/dialer.spec.js b/test/switch/dialer.spec.js deleted file mode 100644 index 95bba64e..00000000 --- a/test/switch/dialer.spec.js +++ /dev/null @@ -1,230 +0,0 @@ -/* eslint-env mocha */ -'use strict' - -const chai = require('chai') -const dirtyChai = require('dirty-chai') -const expect = chai.expect -chai.use(require('chai-checkmark')) -chai.use(dirtyChai) -const sinon = require('sinon') - -const PeerBook = require('peer-book') -const Queue = require('../../src/switch/dialer/queue') -const QueueManager = require('../../src/switch/dialer/queueManager') -const Switch = require('../../src/switch') -const { PRIORITY_HIGH, PRIORITY_LOW } = require('../../src/switch/constants') - -const utils = require('./utils') -const createInfos = utils.createInfos - -describe('dialer', () => { - let switchA - let switchB - - before((done) => createInfos(2, (err, infos) => { - expect(err).to.not.exist() - - switchA = new Switch(infos[0], new PeerBook()) - switchB = new Switch(infos[1], new PeerBook()) - - done() - })) - - afterEach(() => { - sinon.restore() - }) - - describe('connect', () => { - afterEach(() => { - switchA.dialer.clearDenylist(switchB._peerInfo) - }) - - it('should use default options', (done) => { - switchA.dialer.connect(switchB._peerInfo, (err) => { - expect(err).to.exist() - done() - }) - }) - - it('should be able to use custom options', (done) => { - switchA.dialer.connect(switchB._peerInfo, { useFSM: true, priority: PRIORITY_HIGH }, (err) => { - expect(err).to.exist() - done() - }) - }) - }) - - describe('queue', () => { - it('should denylist forever after 5 denylists', () => { - const queue = new Queue('QM', switchA) - for (var i = 0; i < 4; i++) { - queue.denylist() - expect(queue.denylisted).to.be.a('number') - expect(queue.denylisted).to.not.eql(Infinity) - } - - queue.denylist() - expect(queue.denylisted).to.eql(Infinity) - }) - }) - - describe('queue manager', () => { - let queueManager - before(() => { - queueManager = new QueueManager(switchA) - }) - - it('should abort cold calls when the queue is full', (done) => { - sinon.stub(queueManager._coldCallQueue, 'size').value(switchA.dialer.MAX_COLD_CALLS) - const dialRequest = { - peerInfo: { - id: { toB58String: () => 'QmA' } - }, - protocol: null, - options: { useFSM: true, priority: PRIORITY_LOW }, - callback: (err) => { - expect(err.code).to.eql('DIAL_ABORTED') - done() - } - } - - queueManager.add(dialRequest) - }) - - it('should add a protocol dial to the normal queue', () => { - const dialRequest = { - peerInfo: { - id: { toB58String: () => 'QmA' }, - isConnected: () => null - }, - protocol: '/echo/1.0.0', - options: { useFSM: true, priority: PRIORITY_HIGH }, - callback: () => {} - } - - const runSpy = sinon.stub(queueManager, 'run') - const addSpy = sinon.stub(queueManager._queue, 'add') - const deleteSpy = sinon.stub(queueManager._coldCallQueue, 'delete') - - queueManager.add(dialRequest) - - expect(runSpy.called).to.eql(true) - expect(addSpy.called).to.eql(true) - expect(addSpy.getCall(0).args[0]).to.eql('QmA') - expect(deleteSpy.called).to.eql(true) - expect(deleteSpy.getCall(0).args[0]).to.eql('QmA') - }) - - it('should add a cold call to the cold call queue', () => { - const dialRequest = { - peerInfo: { - id: { toB58String: () => 'QmA' }, - isConnected: () => null - }, - protocol: null, - options: { useFSM: true, priority: PRIORITY_LOW }, - callback: () => {} - } - - const runSpy = sinon.stub(queueManager, 'run') - const addSpy = sinon.stub(queueManager._coldCallQueue, 'add') - - queueManager.add(dialRequest) - - expect(runSpy.called).to.eql(true) - expect(addSpy.called).to.eql(true) - expect(addSpy.getCall(0).args[0]).to.eql('QmA') - }) - - it('should abort a cold call if it\'s in the normal queue', (done) => { - const dialRequest = { - peerInfo: { - id: { toB58String: () => 'QmA' }, - isConnected: () => null - }, - protocol: null, - options: { useFSM: true, priority: PRIORITY_LOW }, - callback: (err) => { - expect(runSpy.called).to.eql(false) - expect(hasSpy.called).to.eql(true) - expect(hasSpy.getCall(0).args[0]).to.eql('QmA') - expect(err.code).to.eql('DIAL_ABORTED') - done() - } - } - - const runSpy = sinon.stub(queueManager, 'run') - const hasSpy = sinon.stub(queueManager._queue, 'has').returns(true) - - queueManager.add(dialRequest) - }) - - it('should remove a queue that has reached max denylist', () => { - const queue = new Queue('QmA', switchA) - queue.denylisted = Infinity - - const abortSpy = sinon.spy(queue, 'abort') - const queueManager = new QueueManager(switchA) - queueManager._queues[queue.id] = queue - - queueManager._clean() - - expect(abortSpy.called).to.eql(true) - expect(queueManager._queues).to.eql({}) - }) - - it('should not remove a queue that is denylisted below max', () => { - const queue = new Queue('QmA', switchA) - queue.denylisted = Date.now() + 10e3 - - const abortSpy = sinon.spy(queue, 'abort') - const queueManager = new QueueManager(switchA) - queueManager._queues[queue.id] = queue - - queueManager._clean() - - expect(abortSpy.called).to.eql(false) - expect(queueManager._queues).to.eql({ - QmA: queue - }) - }) - - it('should remove a queue that is not running and the peer is not connected', () => { - const disconnectedPeer = { - id: { toB58String: () => 'QmA' }, - isConnected: () => null - } - const queue = new Queue(disconnectedPeer.id.toB58String(), switchA) - - const abortSpy = sinon.spy(queue, 'abort') - const queueManager = new QueueManager(switchA) - queueManager._queues[queue.id] = queue - - queueManager._clean() - - expect(abortSpy.called).to.eql(true) - expect(queueManager._queues).to.eql({}) - }) - - it('should not remove a queue that is not running but the peer is connected', () => { - const connectedPeer = { - id: { toB58String: () => 'QmA' }, - isConnected: () => true - } - const queue = new Queue(connectedPeer.id.toB58String(), switchA) - - switchA._peerBook.put(connectedPeer) - - const abortSpy = sinon.spy(queue, 'abort') - const queueManager = new QueueManager(switchA) - queueManager._queues[queue.id] = queue - - queueManager._clean() - - expect(abortSpy.called).to.eql(false) - expect(queueManager._queues).to.eql({ - QmA: queue - }) - }) - }) -}) diff --git a/test/switch/identify.node.js b/test/switch/identify.node.js deleted file mode 100644 index 4585de55..00000000 --- a/test/switch/identify.node.js +++ /dev/null @@ -1,173 +0,0 @@ -/* eslint-env mocha */ -'use strict' - -const chai = require('chai') -chai.use(require('dirty-chai')) -chai.use(require('chai-checkmark')) -const expect = chai.expect -const parallel = require('async/parallel') -const TCP = require('libp2p-tcp') -const multiplex = require('libp2p-mplex') -const pull = require('pull-stream') -const secio = require('libp2p-secio') -const PeerInfo = require('peer-info') -const PeerBook = require('peer-book') -const identify = require('../../src/identify') -const lp = require('pull-length-prefixed') -const sinon = require('sinon') - -const utils = require('./utils') -const createInfos = utils.createInfos -const Switch = require('../../src/switch') - -describe('Identify', () => { - let switchA - let switchB - let switchC - - before((done) => createInfos(3, (err, infos) => { - expect(err).to.not.exist() - - const peerA = infos[0] - const peerB = infos[1] - const peerC = infos[2] - - peerA.multiaddrs.add('/ip4/127.0.0.1/tcp/9001') - peerB.multiaddrs.add('/ip4/127.0.0.1/tcp/9002') - peerC.multiaddrs.add('/ip4/127.0.0.1/tcp/9003') - - switchA = new Switch(peerA, new PeerBook()) - switchB = new Switch(peerB, new PeerBook()) - switchC = new Switch(peerC, new PeerBook()) - - switchA.transport.add('tcp', new TCP()) - switchB.transport.add('tcp', new TCP()) - switchC.transport.add('tcp', new TCP()) - - switchA.connection.crypto(secio.tag, secio.encrypt) - switchB.connection.crypto(secio.tag, secio.encrypt) - switchC.connection.crypto(secio.tag, secio.encrypt) - - switchA.connection.addStreamMuxer(multiplex) - switchB.connection.addStreamMuxer(multiplex) - switchC.connection.addStreamMuxer(multiplex) - - switchA.connection.reuse() - switchB.connection.reuse() - switchC.connection.reuse() - - parallel([ - (cb) => switchA.transport.listen('tcp', {}, null, cb), - (cb) => switchB.transport.listen('tcp', {}, null, cb), - (cb) => switchC.transport.listen('tcp', {}, null, cb) - ], done) - })) - - after((done) => { - parallel([ - (cb) => switchA.stop(cb), - (cb) => switchB.stop(cb), - (cb) => switchC.stop(cb) - ], done) - }) - - afterEach(function (done) { - sinon.restore() - // Hangup everything - parallel([ - (cb) => switchA.hangUp(switchB._peerInfo, cb), - (cb) => switchA.hangUp(switchC._peerInfo, cb), - (cb) => switchB.hangUp(switchA._peerInfo, cb), - (cb) => switchB.hangUp(switchC._peerInfo, cb), - (cb) => switchC.hangUp(switchA._peerInfo, cb), - (cb) => switchC.hangUp(switchB._peerInfo, cb) - ], done) - }) - - it('should identify a good peer', (done) => { - switchA.handle('/id-test/1.0.0', (protocol, conn) => pull(conn, conn)) - switchB.dial(switchA._peerInfo, '/id-test/1.0.0', (err, conn) => { - expect(err).to.not.exist() - - const data = Buffer.from('data that can be had') - pull( - pull.values([data]), - conn, - pull.collect((err, values) => { - expect(err).to.not.exist() - expect(values).to.deep.equal([data]) - done() - }) - ) - }) - }) - - it('should get protocols for one another', (done) => { - // We need to reset the PeerInfo objects we use, - // since we share memory we can receive a false positive if not - const peerA = new PeerInfo(switchA._peerInfo.id) - switchA._peerInfo.multiaddrs.toArray().forEach((m) => { - peerA.multiaddrs.add(m) - }) - switchB._peerBook.remove(switchA._peerInfo.id.toB58String()) - switchA._peerBook.remove(switchB._peerInfo.id.toB58String()) - - switchA.handle('/id-test/1.0.0', (protocol, conn) => pull(conn, conn)) - switchB.dial(peerA, '/id-test/1.0.0', (err) => { - expect(err).to.not.exist() - - // Give identify a moment to run - setTimeout(() => { - const peerB = switchA._peerBook.get(switchB._peerInfo.id.toB58String()) - const peerA = switchB._peerBook.get(switchA._peerInfo.id.toB58String()) - expect(Array.from(peerB.protocols)).to.eql([ - multiplex.multicodec, - identify.multicodec - ]) - expect(Array.from(peerA.protocols)).to.eql([ - multiplex.multicodec, - identify.multicodec, - '/id-test/1.0.0' - ]) - - done() - }, 500) - }) - }) - - it('should close connection when identify fails', (done) => { - const stub = sinon.stub(identify, 'listener').callsFake((conn) => { - conn.getObservedAddrs((err, observedAddrs) => { - if (err) { return } - observedAddrs = observedAddrs[0] - - // pretend to be another peer - const publicKey = switchC._peerInfo.id.pubKey.bytes - - const msgSend = identify.message.encode({ - protocolVersion: 'ipfs/0.1.0', - agentVersion: 'na', - publicKey: publicKey, - listenAddrs: switchC._peerInfo.multiaddrs.toArray().map((ma) => ma.buffer), - observedAddr: observedAddrs ? observedAddrs.buffer : Buffer.from('') - }) - - pull( - pull.values([msgSend]), - lp.encode(), - conn - ) - }) - }) - - expect(2).checks(done) - - switchA.handle('/id-test/1.0.0', (protocol, conn) => pull(conn, conn)) - switchB.dialFSM(switchA._peerInfo, '/id-test/1.0.0', (err, connFSM) => { - expect(err).to.not.exist().mark() - connFSM.once('close', () => { - expect(stub.called).to.eql(true).mark() - }) - }) - }) -}) diff --git a/test/switch/limit-dialer.node.js b/test/switch/limit-dialer.node.js deleted file mode 100644 index e6395400..00000000 --- a/test/switch/limit-dialer.node.js +++ /dev/null @@ -1,93 +0,0 @@ -/* eslint-env mocha */ -'use strict' - -const chai = require('chai') -chai.use(require('dirty-chai')) -chai.use(require('chai-checkmark')) -const expect = chai.expect -const multiaddr = require('multiaddr') -const pull = require('pull-stream') -const nextTick = require('async/nextTick') - -const LimitDialer = require('../../src/switch/limit-dialer') -const utils = require('./utils') - -describe('LimitDialer', () => { - let peers - - before((done) => { - utils.createInfos(5, (err, infos) => { - if (err) { - return done(err) - } - peers = infos - - peers.forEach((peer, i) => { - peer.multiaddrs.add(multiaddr(`/ip4/191.0.0.1/tcp/123${i}`)) - peer.multiaddrs.add(multiaddr(`/ip4/192.168.0.1/tcp/923${i}`)) - peer.multiaddrs.add(multiaddr(`/ip4/193.168.0.99/tcp/923${i}`)) - }) - done() - }) - }) - - it('all failing', (done) => { - const dialer = new LimitDialer(2, 10) - const error = new Error('fail') - // mock transport - const t1 = { - dial (addr, cb) { - nextTick(cb, error) - return {} - } - } - - dialer.dialMany(peers[0].id, t1, peers[0].multiaddrs.toArray(), (err, conn) => { - expect(err).to.exist() - expect(err).to.include.members([error, error, error]) - expect(conn).to.not.exist() - done() - }) - }) - - it('two success', (done) => { - const dialer = new LimitDialer(2, 10) - - // mock transport - const t1 = { - dial (addr, cb) { - const as = addr.toString() - if (as.match(/191/)) { - nextTick(cb, new Error('fail')) - return null - } else if (as.match(/192/)) { - nextTick(cb) - return { - source: pull.values([1]), - sink: pull.drain() - } - } else if (as.match(/193/)) { - nextTick(cb) - return { - source: pull.values([2]), - sink: pull.drain() - } - } - } - } - - dialer.dialMany(peers[0].id, t1, peers[0].multiaddrs.toArray(), (err, success) => { - const conn = success.conn - expect(success.multiaddr.toString()).to.equal('/ip4/192.168.0.1/tcp/9230') - expect(err).to.not.exist() - pull( - conn, - pull.collect((err, res) => { - expect(err).to.not.exist() - expect(res).to.be.eql([1]) - done() - }) - ) - }) - }) -}) diff --git a/test/switch/node.js b/test/switch/node.js deleted file mode 100644 index 7a2bfbd1..00000000 --- a/test/switch/node.js +++ /dev/null @@ -1,13 +0,0 @@ -'use strict' - -require('./connection.node') -require('./dial-fsm.node') -require('./pnet.node') -require('./transports.node') -require('./stream-muxers.node') -require('./secio.node') -require('./swarm-no-muxing.node') -require('./swarm-muxing.node') -require('./identify.node') -require('./limit-dialer.node') -require('./stats.node') diff --git a/test/switch/pnet.node.js b/test/switch/pnet.node.js deleted file mode 100644 index 2bc1c645..00000000 --- a/test/switch/pnet.node.js +++ /dev/null @@ -1,152 +0,0 @@ -/* eslint-env mocha */ -'use strict' - -const chai = require('chai') -const dirtyChai = require('dirty-chai') -const expect = chai.expect -chai.use(dirtyChai) -const parallel = require('async/parallel') -const TCP = require('libp2p-tcp') -const multiplex = require('pull-mplex') -const pull = require('pull-stream') -const PeerBook = require('peer-book') -const secio = require('libp2p-secio') -const Protector = require('../../src/pnet') - -const utils = require('./utils') -const createInfos = utils.createInfos -const tryEcho = utils.tryEcho -const Switch = require('../../src/switch') - -const generatePSK = Protector.generate - -const psk = Buffer.alloc(95) -const psk2 = Buffer.alloc(95) -generatePSK(psk) -generatePSK(psk2) - -describe('Private Network', function () { - let switchA - let switchB - let switchC - let switchD - - before((done) => createInfos(4, (err, infos) => { - expect(err).to.not.exist() - - const peerA = infos[0] - const peerB = infos[1] - const peerC = infos[2] - const peerD = infos[3] - - peerA.multiaddrs.add('/ip4/127.0.0.1/tcp/9001') - peerB.multiaddrs.add('/ip4/127.0.0.1/tcp/9002') - peerC.multiaddrs.add('/ip4/127.0.0.1/tcp/9003') - peerD.multiaddrs.add('/ip4/127.0.0.1/tcp/9004') - - switchA = new Switch(peerA, new PeerBook(), { - protector: new Protector(psk) - }) - switchB = new Switch(peerB, new PeerBook(), { - protector: new Protector(psk) - }) - // alternative way to add the protector - switchC = new Switch(peerC, new PeerBook()) - switchC.protector = new Protector(psk) - // Create a switch on a different private network - switchD = new Switch(peerD, new PeerBook(), { - protector: new Protector(psk2) - }) - - switchA.transport.add('tcp', new TCP()) - switchB.transport.add('tcp', new TCP()) - switchC.transport.add('tcp', new TCP()) - switchD.transport.add('tcp', new TCP()) - - switchA.connection.crypto(secio.tag, secio.encrypt) - switchB.connection.crypto(secio.tag, secio.encrypt) - switchC.connection.crypto(secio.tag, secio.encrypt) - switchD.connection.crypto(secio.tag, secio.encrypt) - - switchA.connection.addStreamMuxer(multiplex) - switchB.connection.addStreamMuxer(multiplex) - switchC.connection.addStreamMuxer(multiplex) - switchD.connection.addStreamMuxer(multiplex) - - parallel([ - (cb) => switchA.transport.listen('tcp', {}, null, cb), - (cb) => switchB.transport.listen('tcp', {}, null, cb), - (cb) => switchC.transport.listen('tcp', {}, null, cb), - (cb) => switchD.transport.listen('tcp', {}, null, cb) - ], done) - })) - - after(function (done) { - parallel([ - (cb) => switchA.stop(cb), - (cb) => switchB.stop(cb), - (cb) => switchC.stop(cb), - (cb) => switchD.stop(cb) - ], done) - }) - - it('should handle + dial on protocol', (done) => { - switchB.handle('/abacaxi/1.0.0', (protocol, conn) => pull(conn, conn)) - - switchA.dial(switchB._peerInfo, '/abacaxi/1.0.0', (err, conn) => { - expect(err).to.not.exist() - expect(switchA.connection.getAll()).to.have.length(1) - tryEcho(conn, done) - }) - }) - - it('should dial to warm conn', (done) => { - switchB.dial(switchA._peerInfo, (err) => { - expect(err).to.not.exist() - expect(Object.keys(switchB.conns).length).to.equal(0) - expect(switchB.connection.getAll()).to.have.length(1) - done() - }) - }) - - it('should dial on protocol, reuseing warmed conn', (done) => { - switchA.handle('/papaia/1.0.0', (protocol, conn) => pull(conn, conn)) - - switchB.dial(switchA._peerInfo, '/papaia/1.0.0', (err, conn) => { - expect(err).to.not.exist() - expect(Object.keys(switchB.conns).length).to.equal(0) - expect(switchB.connection.getAll()).to.have.length(1) - tryEcho(conn, done) - }) - }) - - it('should enable identify to reuse incomming muxed conn', (done) => { - switchA.connection.reuse() - switchC.connection.reuse() - - switchC.dial(switchA._peerInfo, (err) => { - expect(err).to.not.exist() - setTimeout(() => { - expect(switchC.connection.getAll()).to.have.length(1) - expect(switchA.connection.getAll()).to.have.length(2) - done() - }, 500) - }) - }) - - /** - * This test is being skipped until a related issue with pull-reader overreading can be resolved - * Currently this test will time out instead of returning an error properly. This is the same issue - * in ipfs/interop, https://github.com/ipfs/interop/pull/24/commits/179978996ecaef39e78384091aa9669dcdb94cc0 - */ - it('should fail to talk to a switch on a different private network', function (done) { - switchD.dial(switchA._peerInfo, (err) => { - expect(err).to.exist() - }) - - // A successful connection will return in well under 2 seconds - setTimeout(() => { - done() - }, 2000) - }) -}) diff --git a/test/switch/secio.node.js b/test/switch/secio.node.js deleted file mode 100644 index 3e9080d6..00000000 --- a/test/switch/secio.node.js +++ /dev/null @@ -1,116 +0,0 @@ -/* eslint-env mocha */ -'use strict' - -const chai = require('chai') -const dirtyChai = require('dirty-chai') -const expect = chai.expect -chai.use(dirtyChai) -const parallel = require('async/parallel') -const TCP = require('libp2p-tcp') -const multiplex = require('pull-mplex') -const pull = require('pull-stream') -const secio = require('libp2p-secio') -const PeerBook = require('peer-book') - -const utils = require('./utils') -const createInfos = utils.createInfos -const tryEcho = utils.tryEcho -const Switch = require('../../src/switch') - -describe('SECIO', () => { - let switchA - let switchB - let switchC - - before((done) => createInfos(3, (err, infos) => { - expect(err).to.not.exist() - - const peerA = infos[0] - const peerB = infos[1] - const peerC = infos[2] - - peerA.multiaddrs.add('/ip4/127.0.0.1/tcp/9001') - peerB.multiaddrs.add('/ip4/127.0.0.1/tcp/9002') - peerC.multiaddrs.add('/ip4/127.0.0.1/tcp/9003') - - switchA = new Switch(peerA, new PeerBook()) - switchB = new Switch(peerB, new PeerBook()) - switchC = new Switch(peerC, new PeerBook()) - - switchA.transport.add('tcp', new TCP()) - switchB.transport.add('tcp', new TCP()) - switchC.transport.add('tcp', new TCP()) - - switchA.connection.crypto(secio.tag, secio.encrypt) - switchB.connection.crypto(secio.tag, secio.encrypt) - switchC.connection.crypto(secio.tag, secio.encrypt) - - switchA.connection.addStreamMuxer(multiplex) - switchB.connection.addStreamMuxer(multiplex) - switchC.connection.addStreamMuxer(multiplex) - - parallel([ - (cb) => switchA.transport.listen('tcp', {}, null, cb), - (cb) => switchB.transport.listen('tcp', {}, null, cb), - (cb) => switchC.transport.listen('tcp', {}, null, cb) - ], done) - })) - - after(function (done) { - this.timeout(3 * 1000) - parallel([ - (cb) => switchA.stop(cb), - (cb) => switchB.stop(cb), - (cb) => switchC.stop(cb) - ], done) - }) - - it('handle + dial on protocol', (done) => { - switchB.handle('/abacaxi/1.0.0', (protocol, conn) => pull(conn, conn)) - - switchA.dial(switchB._peerInfo, '/abacaxi/1.0.0', (err, conn) => { - expect(err).to.not.exist() - expect(switchA.connection.getAll()).to.have.length(1) - tryEcho(conn, done) - }) - }) - - it('dial to warm conn', (done) => { - switchB.dial(switchA._peerInfo, (err) => { - expect(err).to.not.exist() - expect(Object.keys(switchB.conns).length).to.equal(0) - expect(switchB.connection.getAll()).to.have.length(1) - done() - }) - }) - - it('dial on protocol, reuse warmed conn', (done) => { - switchA.handle('/papaia/1.0.0', (protocol, conn) => pull(conn, conn)) - - switchB.dial(switchA._peerInfo, '/papaia/1.0.0', (err, conn) => { - expect(err).to.not.exist() - expect(Object.keys(switchB.conns).length).to.equal(0) - expect(switchB.connection.getAll()).to.have.length(1) - tryEcho(conn, done) - }) - }) - - it('enable identify to reuse incomming muxed conn', (done) => { - switchA.connection.reuse() - switchC.connection.reuse() - - switchC.dial(switchA._peerInfo, (err) => { - expect(err).to.not.exist() - setTimeout(() => { - expect(switchC.connection.getAll()).to.have.length(1) - expect(switchA.connection.getAll()).to.have.length(2) - done() - }, 500) - }) - }) - - it('switch back to plaintext if no arguments passed in', () => { - switchA.connection.crypto() - expect(switchA.crypto.tag).to.eql('/plaintext/1.0.0') - }) -}) diff --git a/test/switch/stats.node.js b/test/switch/stats.node.js deleted file mode 100644 index e9852ebe..00000000 --- a/test/switch/stats.node.js +++ /dev/null @@ -1,280 +0,0 @@ -/* eslint-env mocha */ -'use strict' - -const chai = require('chai') -const dirtyChai = require('dirty-chai') -const expect = chai.expect -chai.use(dirtyChai) -const parallel = require('async/parallel') -const each = require('async/each') -const map = require('async/map') -const series = require('async/series') -const TCP = require('libp2p-tcp') -const multiplex = require('libp2p-mplex') -const pull = require('pull-stream') -const secio = require('libp2p-secio') -const PeerBook = require('peer-book') - -const utils = require('./utils') -const createInfos = utils.createInfos -const tryEcho = utils.tryEcho -const Switch = require('../../src/switch') - -describe('Stats', () => { - const setup = (cb) => { - createInfos(2, (err, infos) => { - expect(err).to.not.exist() - - const options = { - stats: { - computeThrottleTimeout: 100 - } - } - - const peerA = infos[0] - const peerB = infos[1] - - peerA.multiaddrs.add('/ip4/127.0.0.1/tcp/0') - peerB.multiaddrs.add('/ip4/127.0.0.1/tcp/0') - - const switchA = new Switch(peerA, new PeerBook(), options) - const switchB = new Switch(peerB, new PeerBook(), options) - - switchA.transport.add('tcp', new TCP()) - switchB.transport.add('tcp', new TCP()) - - switchA.connection.crypto(secio.tag, secio.encrypt) - switchB.connection.crypto(secio.tag, secio.encrypt) - - switchA.connection.addStreamMuxer(multiplex) - switchB.connection.addStreamMuxer(multiplex) - - parallel([ - (cb) => switchA.start(cb), - (cb) => switchB.start(cb) - ], (err) => { - if (err) { - cb(err) - return - } - const echo = (protocol, conn) => pull(conn, conn) - switchB.handle('/echo/1.0.0', echo) - switchA.handle('/echo/1.0.0', echo) - - parallel([ - (cb) => { - switchA.dial(switchB._peerInfo, '/echo/1.0.0', (err, conn) => { - expect(err).to.not.exist() - tryEcho(conn, cb) - }) - }, - (cb) => { - switchB.dial(switchA._peerInfo, '/echo/1.0.0', (err, conn) => { - expect(err).to.not.exist() - tryEcho(conn, cb) - }) - } - ], (err) => { - if (err) { - cb(err) - return - } - - // wait until stats are processed - let pending = 12 - switchA.stats.on('update', waitForUpdate) - switchB.stats.on('update', waitForUpdate) - - function waitForUpdate () { - if (--pending === 0) { - switchA.stats.removeListener('update', waitForUpdate) - switchB.stats.removeListener('update', waitForUpdate) - cb(null, [switchA, switchB]) - } - } - }) - }) - }) - } - - const teardown = (switches, cb) => { - map(switches, (swtch, cb) => swtch.stop(cb), cb) - } - - it('both nodes have some global stats', (done) => { - setup((err, switches) => { - expect(err).to.not.exist() - - switches.forEach((swtch) => { - const snapshot = swtch.stats.global.snapshot - expect(snapshot.dataReceived.toFixed()).to.equal('2210') - expect(snapshot.dataSent.toFixed()).to.equal('2210') - }) - - teardown(switches, done) - }) - }) - - it('both nodes know the transports', (done) => { - setup((err, switches) => { - expect(err).to.not.exist() - const expectedTransports = [ - 'tcp' - ] - - switches.forEach( - (swtch) => expect(swtch.stats.transports().sort()).to.deep.equal(expectedTransports)) - teardown(switches, done) - }) - }) - - it('both nodes know the protocols', (done) => { - setup((err, switches) => { - expect(err).to.not.exist() - const expectedProtocols = [ - '/echo/1.0.0', - '/mplex/6.7.0', - '/secio/1.0.0' - ] - - switches.forEach((swtch) => { - expect(swtch.stats.protocols().sort()).to.deep.equal(expectedProtocols) - }) - - teardown(switches, done) - }) - }) - - it('both nodes know about each other', (done) => { - setup((err, switches) => { - expect(err).to.not.exist() - switches.forEach( - (swtch, index) => { - const otherSwitch = selectOther(switches, index) - expect(swtch.stats.peers().sort()).to.deep.equal([otherSwitch._peerInfo.id.toB58String()]) - }) - teardown(switches, done) - }) - }) - - it('both have transport-specific stats', (done) => { - setup((err, switches) => { - expect(err).to.not.exist() - switches.forEach((swtch) => { - const snapshot = swtch.stats.forTransport('tcp').snapshot - expect(snapshot.dataReceived.toFixed()).to.equal('2210') - expect(snapshot.dataSent.toFixed()).to.equal('2210') - }) - teardown(switches, done) - }) - }) - - it('both have protocol-specific stats', (done) => { - setup((err, switches) => { - expect(err).to.not.exist() - switches.forEach((swtch) => { - const snapshot = swtch.stats.forProtocol('/echo/1.0.0').snapshot - expect(snapshot.dataReceived.toFixed()).to.equal('8') - expect(snapshot.dataSent.toFixed()).to.equal('8') - }) - teardown(switches, done) - }) - }) - - it('both have peer-specific stats', (done) => { - setup((err, switches) => { - expect(err).to.not.exist() - switches.forEach((swtch, index) => { - const other = selectOther(switches, index) - const snapshot = swtch.stats.forPeer(other._peerInfo.id.toB58String()).snapshot - expect(snapshot.dataReceived.toFixed()).to.equal('2210') - expect(snapshot.dataSent.toFixed()).to.equal('2210') - }) - teardown(switches, done) - }) - }) - - it('both have moving average stats for peer', (done) => { - setup((err, switches) => { - expect(err).to.not.exist() - switches.forEach((swtch, index) => { - const other = selectOther(switches, index) - const ma = swtch.stats.forPeer(other._peerInfo.id.toB58String()).movingAverages - const intervals = [60000, 300000, 900000] - intervals.forEach((interval) => { - const average = ma.dataReceived[interval].movingAverage() - expect(average).to.be.above(0).below(100) - }) - }) - teardown(switches, done) - }) - }) - - it('retains peer after disconnect', (done) => { - setup((err, switches) => { - expect(err).to.not.exist() - let index = -1 - each(switches, (swtch, cb) => { - swtch.once('peer-mux-closed', () => cb()) - index++ - swtch.hangUp(selectOther(switches, index)._peerInfo, (err) => { - expect(err).to.not.exist() - }) - }, - (err) => { - expect(err).to.not.exist() - switches.forEach((swtch, index) => { - const other = selectOther(switches, index) - const snapshot = swtch.stats.forPeer(other._peerInfo.id.toB58String()).snapshot - expect(snapshot.dataReceived.toFixed()).to.equal('2210') - expect(snapshot.dataSent.toFixed()).to.equal('2210') - }) - teardown(switches, done) - }) - }) - }) - - it('retains peer after reconnect', (done) => { - setup((err, switches) => { - expect(err).to.not.exist() - series([ - (cb) => { - let index = -1 - each(switches, (swtch, cb) => { - swtch.once('peer-mux-closed', () => cb()) - index++ - swtch.hangUp(selectOther(switches, index)._peerInfo, (err) => { - expect(err).to.not.exist() - }) - }, cb) - }, - (cb) => { - let index = -1 - each(switches, (swtch, cb) => { - index++ - const other = selectOther(switches, index) - swtch.dial(other._peerInfo, '/echo/1.0.0', (err, conn) => { - expect(err).to.not.exist() - tryEcho(conn, cb) - }) - }, cb) - }, - (cb) => setTimeout(cb, 1000), - (cb) => { - switches.forEach((swtch, index) => { - const other = selectOther(switches, index) - const snapshot = swtch.stats.forPeer(other._peerInfo.id.toB58String()).snapshot - expect(snapshot.dataReceived.toFixed()).to.equal('4420') - expect(snapshot.dataSent.toFixed()).to.equal('4420') - }) - teardown(switches, cb) - } - ], done) - }) - }) -}) - -function selectOther (array, index) { - const useIndex = (index + 1) % array.length - return array[useIndex] -} diff --git a/test/switch/stream-muxers.node.js b/test/switch/stream-muxers.node.js deleted file mode 100644 index faf7409f..00000000 --- a/test/switch/stream-muxers.node.js +++ /dev/null @@ -1,155 +0,0 @@ -/* eslint-env mocha */ -/* eslint max-nested-callbacks: ["error", 8] */ -'use strict' - -const chai = require('chai') -const dirtyChai = require('dirty-chai') -const expect = chai.expect -chai.use(dirtyChai) - -const parallel = require('async/parallel') -const TCP = require('libp2p-tcp') -const multiplex = require('libp2p-mplex') -const pullMplex = require('pull-mplex') -const spdy = require('libp2p-spdy') -const pull = require('pull-stream') -const PeerBook = require('peer-book') -const utils = require('./utils') -const createInfos = utils.createInfos -const tryEcho = utils.tryEcho - -const Switch = require('../../src/switch') - -describe('Stream Multiplexing', () => { - [ - multiplex, - pullMplex, - spdy - ].forEach((sm) => describe(sm.multicodec, () => { - let switchA - let switchB - let switchC - - before((done) => createInfos(3, (err, peerInfos) => { - expect(err).to.not.exist() - function maGen (port) { return `/ip4/127.0.0.1/tcp/${port}` } - - const peerA = peerInfos[0] - const peerB = peerInfos[1] - const peerC = peerInfos[2] - - peerA.multiaddrs.add(maGen(9001)) - peerB.multiaddrs.add(maGen(9002)) - peerC.multiaddrs.add(maGen(9003)) - - switchA = new Switch(peerA, new PeerBook()) - switchB = new Switch(peerB, new PeerBook()) - switchC = new Switch(peerC, new PeerBook()) - - switchA.transport.add('tcp', new TCP()) - switchB.transport.add('tcp', new TCP()) - switchC.transport.add('tcp', new TCP()) - - parallel([ - (cb) => switchA.transport.listen('tcp', {}, null, cb), - (cb) => switchB.transport.listen('tcp', {}, null, cb), - (cb) => switchC.transport.listen('tcp', {}, null, cb) - ], done) - })) - - after((done) => parallel([ - (cb) => switchA.stop(cb), - (cb) => switchB.stop(cb) - ], done)) - - it('switch.connection.addStreamMuxer', (done) => { - switchA.connection.addStreamMuxer(sm) - switchB.connection.addStreamMuxer(sm) - switchC.connection.addStreamMuxer(sm) - done() - }) - - it('handle + dial on protocol', (done) => { - switchB.handle('/abacaxi/1.0.0', (protocol, conn) => pull(conn, conn)) - - switchA.dial(switchB._peerInfo, '/abacaxi/1.0.0', (err, conn) => { - expect(err).to.not.exist() - expect(switchA.connection.getAll()).to.have.length(1) - - tryEcho(conn, done) - }) - }) - - it('dial to warm conn', (done) => { - switchB.dial(switchA._peerInfo, (err) => { - expect(err).to.not.exist() - - expect(Object.keys(switchB.conns).length).to.equal(0) - expect(switchB.connection.getAll()).to.have.length(1) - done() - }) - }) - - it('dial on protocol, reuse warmed conn', (done) => { - switchA.handle('/papaia/1.0.0', (protocol, conn) => pull(conn, conn)) - - switchB.dial(switchA._peerInfo, '/papaia/1.0.0', (err, conn) => { - expect(err).to.not.exist() - expect(Object.keys(switchB.conns).length).to.equal(0) - expect(switchB.connection.getAll()).to.have.length(1) - - tryEcho(conn, done) - }) - }) - - it('enable identify to reuse incomming muxed conn', (done) => { - switchA.connection.reuse() - switchC.connection.reuse() - - switchC.dial(switchA._peerInfo, (err) => { - expect(err).to.not.exist() - setTimeout(() => { - expect(switchC.connection.getAll()).to.have.length(1) - expect(switchA.connection.getAll()).to.have.length(2) - done() - }, 500) - }) - }) - - it('with Identify enabled, do getPeerInfo', (done) => { - switchA.handle('/banana/1.0.0', (protocol, conn) => { - conn.getPeerInfo((err, pi) => { - expect(err).to.not.exist() - expect(switchC._peerInfo.id.toB58String()).to.equal(pi.id.toB58String()) - }) - - pull(conn, conn) - }) - - switchC.dial(switchA._peerInfo, '/banana/1.0.0', (err, conn) => { - expect(err).to.not.exist() - setTimeout(() => { - expect(switchC.connection.getAll()).to.have.length(1) - expect(switchA.connection.getAll()).to.have.length(2) - - conn.getPeerInfo((err, pi) => { - expect(err).to.not.exist() - expect(switchA._peerInfo.id.toB58String()).to.equal(pi.id.toB58String()) - tryEcho(conn, done) - }) - }, 500) - }) - }) - - it('closing one side cleans out in the other', (done) => { - switchC.stop((err) => { - expect(err).to.not.exist() - - setTimeout(() => { - expect(switchA.connection.getAll()).to.have.length(1) - done() - }, 500) - }) - }) - })) -}) diff --git a/test/switch/swarm-muxing+webrtc-star.browser.js b/test/switch/swarm-muxing+webrtc-star.browser.js deleted file mode 100644 index e5fe9e93..00000000 --- a/test/switch/swarm-muxing+webrtc-star.browser.js +++ /dev/null @@ -1,153 +0,0 @@ -/* eslint-env mocha */ -'use strict' - -const chai = require('chai') -const dirtyChai = require('dirty-chai') -const expect = chai.expect -chai.use(dirtyChai) - -const peerId = require('peer-id') -const PeerInfo = require('peer-info') -const WebRTCStar = require('libp2p-webrtc-star') -const spdy = require('libp2p-spdy') -const parallel = require('async/parallel') -const series = require('async/series') -const pull = require('pull-stream') -const PeerBook = require('peer-book') -const tryEcho = require('./utils').tryEcho -const sinon = require('sinon') - -const Switch = require('../../src/switch') - -describe('Switch (webrtc-star)', () => { - let switch1 - let peer1 - let wstar1 - - let switch2 - let peer2 - let wstar2 - - before((done) => series([ - (cb) => peerId.create((err, id1) => { - expect(err).to.not.exist() - peer1 = new PeerInfo(id1) - const ma1 = '/ip4/127.0.0.1/tcp/15555/ws/p2p-webrtc-star/ipfs/' + - id1.toB58String() - peer1.multiaddrs.add(ma1) - cb() - }), - (cb) => peerId.create((err, id2) => { - expect(err).to.not.exist() - peer2 = new PeerInfo(id2) - const ma2 = '/ip4/127.0.0.1/tcp/15555/ws/p2p-webrtc-star/ipfs/' + - id2.toB58String() - peer2.multiaddrs.add(ma2) - cb() - }) - ], (err) => { - expect(err).to.not.exist() - - switch1 = new Switch(peer1, new PeerBook()) - switch2 = new Switch(peer2, new PeerBook()) - done() - })) - - afterEach(() => { - sinon.restore() - }) - - it('add WebRTCStar transport to switch 1', () => { - wstar1 = new WebRTCStar() - switch1.transport.add('wstar', wstar1) - expect(Object.keys(switch1.transports).length).to.equal(1) - }) - - it('add WebRTCStar transport to switch 2', () => { - wstar2 = new WebRTCStar() - switch2.transport.add('wstar', wstar2) - expect(Object.keys(switch2.transports).length).to.equal(1) - }) - - it('listen on switch 1', (done) => { - switch1.start(done) - }) - - it('listen on switch 2', (done) => { - switch2.start(done) - }) - - it('add spdy', () => { - switch1.connection.addStreamMuxer(spdy) - switch1.connection.reuse() - switch2.connection.addStreamMuxer(spdy) - switch2.connection.reuse() - }) - - it('handle proto', () => { - switch2.handle('/echo/1.0.0', (protocol, conn) => pull(conn, conn)) - }) - - it('dial on proto', (done) => { - switch1.dial(peer2, '/echo/1.0.0', (err, conn) => { - expect(err).to.not.exist() - expect(switch1.connection.getAll()).to.have.length(1) - - tryEcho(conn, () => { - expect(switch2.connection.getAll()).to.have.length(1) - done() - }) - }) - }) - - it('create a third node and check that discovery works', function (done) { - this.timeout(20 * 1000) - - let counter = 0 - - let switch3 - - function check () { - if (++counter === 4) { - const s1n = switch1.connection.getAll() - const s2n = switch2.connection.getAll() - const s3n = switch3.connection.getAll() - expect(s1n).to.have.length(2) - expect(s2n).to.have.length(2) - expect(s3n).to.have.length(2) - switch3.stop(done) - } - if (counter === 3) { - setTimeout(check, 2000) - } - } - - wstar1.discovery.on('peer', (peerInfo) => switch1.dial(peerInfo, check)) - wstar2.discovery.on('peer', (peerInfo) => switch2.dial(peerInfo, check)) - sinon.stub(wstar1.discovery, '_isStarted').value(true) - sinon.stub(wstar2.discovery, '_isStarted').value(true) - - peerId.create((err, id3) => { - expect(err).to.not.exist() - - const peer3 = new PeerInfo(id3) - const mh3 = '/ip4/127.0.0.1/tcp/15555/ws/p2p-webrtc-star/ipfs/' + id3.toB58String() - peer3.multiaddrs.add(mh3) - - switch3 = new Switch(peer3, new PeerBook()) - const wstar3 = new WebRTCStar() - sinon.stub(wstar3.discovery, '_isStarted').value(true) - switch3.transport.add('wstar', wstar3) - switch3.connection.addStreamMuxer(spdy) - switch3.connection.reuse() - switch3.start(check) - }) - }) - - it('stop', (done) => { - parallel([ - (cb) => switch1.stop(cb), - (cb) => switch2.stop(cb) - ], done) - }) -}) diff --git a/test/switch/swarm-muxing+websockets.browser.js b/test/switch/swarm-muxing+websockets.browser.js deleted file mode 100644 index fcf46f87..00000000 --- a/test/switch/swarm-muxing+websockets.browser.js +++ /dev/null @@ -1,74 +0,0 @@ -/* eslint-env mocha */ -/* eslint max-nested-callbacks: ["error", 5] */ -'use strict' - -const chai = require('chai') -const dirtyChai = require('dirty-chai') -const expect = chai.expect -chai.use(dirtyChai) - -const PeerId = require('peer-id') -const PeerInfo = require('peer-info') -const WebSockets = require('libp2p-websockets') -const mplex = require('pull-mplex') -const spdy = require('libp2p-spdy') -const PeerBook = require('peer-book') -const tryEcho = require('./utils').tryEcho - -const Switch = require('../../src/switch') - -describe('Switch (WebSockets)', () => { - [ - mplex, - spdy - ].forEach((muxer) => { - describe(muxer.multicodec, () => { - let sw - let peerDst - - before((done) => { - PeerInfo.create((err, peerSrc) => { - expect(err).to.not.exist() - sw = new Switch(peerSrc, new PeerBook()) - done() - }) - }) - - after(done => { - sw.stop(done) - }) - - it(`add muxer (${muxer.multicodec})`, () => { - sw.connection.addStreamMuxer(muxer) - sw.connection.reuse() - }) - - it('add ws', () => { - sw.transport.add('ws', new WebSockets()) - expect(Object.keys(sw.transports).length).to.equal(1) - }) - - it('create Dst peer info', (done) => { - PeerId.createFromJSON(require('./test-data/id-2.json'), (err, id) => { - expect(err).to.not.exist() - - peerDst = new PeerInfo(id) - const ma = '/ip4/127.0.0.1/tcp/15347/ws' - peerDst.multiaddrs.add(ma) - done() - }) - }) - - it('dial to warm a conn', (done) => { - sw.dial(peerDst, done) - }) - - it('dial on protocol, use warmed conn', (done) => { - sw.dial(peerDst, '/echo/1.0.0', (err, conn) => { - expect(err).to.not.exist() - tryEcho(conn, done) - }) - }) - }) - }) -}) diff --git a/test/switch/swarm-muxing.node.js b/test/switch/swarm-muxing.node.js deleted file mode 100644 index 8ca774e5..00000000 --- a/test/switch/swarm-muxing.node.js +++ /dev/null @@ -1,248 +0,0 @@ -/* eslint-env mocha */ -/* eslint max-nested-callbacks: ["error", 5] */ -'use strict' - -const chai = require('chai') -const dirtyChai = require('dirty-chai') -const expect = chai.expect -chai.use(dirtyChai) - -const parallel = require('async/parallel') -const TCP = require('libp2p-tcp') -const WebSockets = require('libp2p-websockets') -const mplex = require('libp2p-mplex') -const pMplex = require('pull-mplex') -const spdy = require('libp2p-spdy') -const pull = require('pull-stream') -const PeerBook = require('peer-book') - -const utils = require('./utils') -const createInfos = utils.createInfos -const tryEcho = utils.tryEcho -const Switch = require('../../src/switch') - -describe('Switch (everything all together)', () => { - [pMplex, spdy, mplex].forEach(muxer => { - describe(muxer.multicodec, () => { - let switchA // tcp - let switchB // tcp+ws - let switchC // tcp+ws - let switchD // ws - let switchE // ws - - before((done) => createInfos(5, (err, infos) => { - expect(err).to.not.exist() - - const peerA = infos[0] - const peerB = infos[1] - const peerC = infos[2] - const peerD = infos[3] - const peerE = infos[4] - - switchA = new Switch(peerA, new PeerBook()) - switchB = new Switch(peerB, new PeerBook()) - switchC = new Switch(peerC, new PeerBook()) - switchD = new Switch(peerD, new PeerBook()) - switchE = new Switch(peerE, new PeerBook()) - - done() - })) - - after(function (done) { - parallel([ - (cb) => switchA.stop(cb), - (cb) => switchB.stop(cb), - (cb) => switchD.stop(cb), - (cb) => switchE.stop(cb) - ], done) - }) - - it('add tcp', (done) => { - switchA._peerInfo.multiaddrs.add('/ip4/127.0.0.1/tcp/10100') - switchB._peerInfo.multiaddrs.add('/ip4/127.0.0.1/tcp/10200') - switchC._peerInfo.multiaddrs.add('/ip4/127.0.0.1/tcp/10300') - - switchA.transport.add('tcp', new TCP()) - switchB.transport.add('tcp', new TCP()) - switchC.transport.add('tcp', new TCP()) - - parallel([ - (cb) => switchA.transport.listen('tcp', {}, null, cb), - (cb) => switchB.transport.listen('tcp', {}, null, cb) - ], done) - }) - - it('add websockets', (done) => { - switchB._peerInfo.multiaddrs.add('/ip4/127.0.0.1/tcp/9012/ws') - switchC._peerInfo.multiaddrs.add('/ip4/127.0.0.1/tcp/9022/ws') - switchD._peerInfo.multiaddrs.add('/ip4/127.0.0.1/tcp/9032/ws') - switchE._peerInfo.multiaddrs.add('/ip4/127.0.0.1/tcp/9042/ws') - - switchB.transport.add('ws', new WebSockets()) - switchC.transport.add('ws', new WebSockets()) - switchD.transport.add('ws', new WebSockets()) - switchE.transport.add('ws', new WebSockets()) - - parallel([ - (cb) => switchB.transport.listen('ws', {}, null, cb), - (cb) => switchD.transport.listen('ws', {}, null, cb), - (cb) => switchE.transport.listen('ws', {}, null, cb) - ], done) - }) - - it('listen automatically', (done) => { - switchC.start(done) - }) - - it('add spdy and enable identify', () => { - switchA.connection.addStreamMuxer(muxer) - switchB.connection.addStreamMuxer(muxer) - switchC.connection.addStreamMuxer(muxer) - switchD.connection.addStreamMuxer(muxer) - switchE.connection.addStreamMuxer(muxer) - - switchA.connection.reuse() - switchB.connection.reuse() - switchC.connection.reuse() - switchD.connection.reuse() - switchE.connection.reuse() - }) - - it('warm up from A to B on tcp to tcp+ws', function (done) { - this.timeout(10 * 1000) - parallel([ - (cb) => switchB.once('peer-mux-established', (pi) => { - expect(pi.id.toB58String()).to.equal(switchA._peerInfo.id.toB58String()) - cb() - }), - (cb) => switchA.once('peer-mux-established', (pi) => { - expect(pi.id.toB58String()).to.equal(switchB._peerInfo.id.toB58String()) - cb() - }), - (cb) => switchA.dial(switchB._peerInfo, (err) => { - expect(err).to.not.exist() - expect(switchA.connection.getAll()).to.have.length(1) - cb() - }) - ], done) - }) - - it('warm up a warmed up, from B to A', (done) => { - switchB.dial(switchA._peerInfo, (err) => { - expect(err).to.not.exist() - expect(switchA.connection.getAll()).to.have.length(1) - done() - }) - }) - - it('dial from tcp to tcp+ws, on protocol', (done) => { - switchB.handle('/anona/1.0.0', (protocol, conn) => pull(conn, conn)) - - switchA.dial(switchB._peerInfo, '/anona/1.0.0', (err, conn) => { - expect(err).to.not.exist() - expect(switchA.connection.getAll()).to.have.length(1) - tryEcho(conn, done) - }) - }) - - it('dial from ws to ws no proto', (done) => { - switchD.dial(switchE._peerInfo, (err) => { - expect(err).to.not.exist() - expect(switchD.connection.getAll()).to.have.length(1) - done() - }) - }) - - it('dial from ws to ws', (done) => { - switchE.handle('/abacaxi/1.0.0', (protocol, conn) => pull(conn, conn)) - - switchD.dial(switchE._peerInfo, '/abacaxi/1.0.0', (err, conn) => { - expect(err).to.not.exist() - expect(switchD.connection.getAll()).to.have.length(1) - - tryEcho(conn, () => setTimeout(() => { - expect(switchE.connection.getAll()).to.have.length(1) - done() - }, 1000)) - }) - }) - - it('dial from tcp to tcp+ws', (done) => { - switchB.handle('/grapes/1.0.0', (protocol, conn) => pull(conn, conn)) - - switchA.dial(switchB._peerInfo, '/grapes/1.0.0', (err, conn) => { - expect(err).to.not.exist() - expect(switchA.connection.getAll()).to.have.length(1) - - tryEcho(conn, done) - }) - }) - - it('dial from tcp+ws to tcp+ws', (done) => { - let i = 0 - - function check (err) { - expect(err).to.not.exist() - if (++i === 3) { done() } - } - - switchC.handle('/mamao/1.0.0', (protocol, conn) => { - conn.getPeerInfo((err, peerInfo) => { - expect(err).to.not.exist() - expect(peerInfo).to.exist() - check() - }) - - pull(conn, conn) - }) - - switchA.dial(switchC._peerInfo, '/mamao/1.0.0', (err, conn) => { - expect(err).to.not.exist() - - conn.getPeerInfo((err, peerInfo) => { - expect(err).to.not.exist() - expect(peerInfo).to.exist() - check() - }) - - expect(switchA.connection.getAll()).to.have.length(2) - expect(switchC._peerInfo.isConnected).to.exist() - expect(switchA._peerInfo.isConnected).to.exist() - - tryEcho(conn, check) - }) - }) - - it('hangUp', (done) => { - let count = 0 - const ready = () => ++count === 3 ? done() : null - - switchB.once('peer-mux-closed', (peerInfo) => { - expect(switchB.connection.getAll()).to.have.length(0) - expect(switchB._peerInfo.isConnected()).to.not.exist() - ready() - }) - - switchA.once('peer-mux-closed', (peerInfo) => { - expect(switchA.connection.getAll()).to.have.length(1) - expect(switchA._peerInfo.isConnected()).to.not.exist() - ready() - }) - - switchA.hangUp(switchB._peerInfo, (err) => { - expect(err).to.not.exist() - ready() - }) - }) - - it('close a muxer emits event', function (done) { - this.timeout(3 * 1000) - - parallel([ - (cb) => switchA.once('peer-mux-closed', (peerInfo) => cb()), - (cb) => switchC.stop(cb) - ], done) - }) - }) - }) -}) diff --git a/test/switch/swarm-no-muxing.node.js b/test/switch/swarm-no-muxing.node.js deleted file mode 100644 index ef9a2dc9..00000000 --- a/test/switch/swarm-no-muxing.node.js +++ /dev/null @@ -1,90 +0,0 @@ -/* eslint-env mocha */ -'use strict' - -const chai = require('chai') -const dirtyChai = require('dirty-chai') -const expect = chai.expect -chai.use(dirtyChai) - -const parallel = require('async/parallel') -const TCP = require('libp2p-tcp') -const pull = require('pull-stream') -const PeerBook = require('peer-book') - -const utils = require('./utils') -const createInfos = utils.createInfos -const tryEcho = utils.tryEcho -const Switch = require('../../src/switch') - -describe('Switch (no Stream Multiplexing)', () => { - let switchA - let switchB - - before((done) => createInfos(2, (err, infos) => { - expect(err).to.not.exist() - - const peerA = infos[0] - const peerB = infos[1] - - peerA.multiaddrs.add('/ip4/127.0.0.1/tcp/9001') - peerB.multiaddrs.add('/ip4/127.0.0.1/tcp/9002/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC') - - switchA = new Switch(peerA, new PeerBook()) - switchB = new Switch(peerB, new PeerBook()) - - switchA.transport.add('tcp', new TCP()) - switchB.transport.add('tcp', new TCP()) - - parallel([ - (cb) => switchA.transport.listen('tcp', {}, null, cb), - (cb) => switchB.transport.listen('tcp', {}, null, cb) - ], done) - })) - - after((done) => parallel([ - (cb) => switchA.stop(cb), - (cb) => switchB.stop(cb) - ], done)) - - it('handle a protocol', (done) => { - switchB.handle('/bananas/1.0.0', (protocol, conn) => pull(conn, conn)) - expect(switchB.protocols).to.have.all.keys('/bananas/1.0.0') - done() - }) - - it('dial on protocol', (done) => { - switchB.handle('/pineapple/1.0.0', (protocol, conn) => pull(conn, conn)) - - switchA.dial(switchB._peerInfo, '/pineapple/1.0.0', (err, conn) => { - expect(err).to.not.exist() - tryEcho(conn, done) - }) - }) - - it('dial on protocol (returned conn)', (done) => { - switchB.handle('/apples/1.0.0', (protocol, conn) => pull(conn, conn)) - - const conn = switchA.dial(switchB._peerInfo, '/apples/1.0.0', (err) => { - expect(err).to.not.exist() - }) - - tryEcho(conn, done) - }) - - it('dial to warm a conn', (done) => { - switchA.dial(switchB._peerInfo, done) - }) - - it('dial on protocol, reuse warmed conn', (done) => { - switchA.dial(switchB._peerInfo, '/bananas/1.0.0', (err, conn) => { - expect(err).to.not.exist() - tryEcho(conn, done) - }) - }) - - it('unhandle', () => { - const proto = '/bananas/1.0.0' - switchA.unhandle(proto) - expect(switchA.protocols[proto]).to.not.exist() - }) -}) diff --git a/test/switch/switch.spec.js b/test/switch/switch.spec.js deleted file mode 100644 index 142d40fc..00000000 --- a/test/switch/switch.spec.js +++ /dev/null @@ -1,37 +0,0 @@ -/* eslint-env mocha */ -'use strict' - -const chai = require('chai') -const dirtyChai = require('dirty-chai') -const expect = chai.expect -chai.use(dirtyChai) - -const Switch = require('../../src/switch') - -describe('Switch', () => { - describe('.availableTransports', () => { - it('should always sort circuit last', () => { - const switchA = new Switch({}, {}) - const transport = { - filter: (addrs) => addrs - } - const mockPeerInfo = { - multiaddrs: { - toArray: () => ['a', 'b', 'c'] - } - } - - switchA.transports = { - Circuit: transport, - TCP: transport, - WebSocketStar: transport - } - - expect(switchA.availableTransports(mockPeerInfo)).to.eql([ - 'TCP', - 'WebSocketStar', - 'Circuit' - ]) - }) - }) -}) diff --git a/test/switch/t-webrtc-star.browser.js b/test/switch/t-webrtc-star.browser.js deleted file mode 100644 index 0bfa8431..00000000 --- a/test/switch/t-webrtc-star.browser.js +++ /dev/null @@ -1,83 +0,0 @@ -/* eslint-env mocha */ -'use strict' - -const chai = require('chai') -const dirtyChai = require('dirty-chai') -const expect = chai.expect -chai.use(dirtyChai) -const PeerId = require('peer-id') -const PeerInfo = require('peer-info') -const WebRTCStar = require('libp2p-webrtc-star') -const parallel = require('async/parallel') -const pull = require('pull-stream') -const PeerBook = require('peer-book') -const tryEcho = require('./utils').tryEcho - -const Switch = require('../../src/switch') - -describe('transport - webrtc-star', () => { - let switch1 - let switch2 - - before(() => { - const id1 = PeerId - .createFromB58String('QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSooooA') - const peer1 = new PeerInfo(id1) - - const ma1 = '/ip4/127.0.0.1/tcp/15555/ws/p2p-webrtc-star/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSooooA' - peer1.multiaddrs.add(ma1) - - const id2 = PeerId - .createFromB58String('QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSooooB') - const peer2 = new PeerInfo(id2) - const ma2 = '/ip4/127.0.0.1/tcp/15555/ws/p2p-webrtc-star/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSooooB' - peer2.multiaddrs.add(ma2) - - switch1 = new Switch(peer1, new PeerBook()) - switch2 = new Switch(peer2, new PeerBook()) - }) - - it('add WebRTCStar transport to switch 1', () => { - switch1.transport.add('wstar', new WebRTCStar()) - expect(Object.keys(switch1.transports).length).to.equal(1) - }) - - it('add WebRTCStar transport to switch 2', () => { - switch2.transport.add('wstar', new WebRTCStar()) - expect(Object.keys(switch2.transports).length).to.equal(1) - }) - - it('listen on switch 1', (done) => { - switch1.transport.listen('wstar', {}, (conn) => pull(conn, conn), done) - }) - - it('listen on switch 2', (done) => { - switch2.transport.listen('wstar', {}, (conn) => pull(conn, conn), done) - }) - - it('dial', (done) => { - switch1.transport.dial('wstar', switch2._peerInfo, (err, conn) => { - expect(err).to.not.exist() - - tryEcho(conn, done) - }) - }) - it('dial offline / non-existent node', (done) => { - const peer2 = switch2._peerInfo - peer2.multiaddrs.clear() - peer2.multiaddrs.add('/ip4/127.0.0.1/tcp/15555/ws/p2p-webrtc-star/ipfs/ABCD') - - switch1.transport.dial('wstar', peer2, (err, conn) => { - expect(err).to.exist() - expect(conn).to.not.exist() - done() - }) - }) - - it('close', (done) => { - parallel([ - (cb) => switch1.transport.close('wstar', cb), - (cb) => switch2.transport.close('wstar', cb) - ], done) - }) -}) diff --git a/test/switch/test-data/id-1.json b/test/switch/test-data/id-1.json deleted file mode 100644 index 504e71ac..00000000 --- a/test/switch/test-data/id-1.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "id": "QmYmfUS4A3E64BzU8DsCmCWpPhcXWU2KTKNRGtdtN4oCgU", - "privKey": "CAASqAkwggSkAgEAAoIBAQCYtGLh+ow9WEJMn50voPGa6MsqSgJx8pNXGtk5kMSktWxfYHrejLZJjN0+br2CwpFMtf9JW6dAIpxb3qViBCFXjzEK8JuYaXM2sHC6sapyCxeZUbZJtGAXNWQW3qV7m8s8cJTOu2s1euT/G6uf/mIVFIzCkQDx+Ejh5Aie+BTAEf1WbLmcoDDxVESe22gpTxtMG8WTocMV34BxKn8d8vhcZZsi8LLkjg172QwQr3Q68jKgdja3K1YYm6fnso6H3+H06IHgPFAvVhycBbmlyR3bL/hFBl6+ElwBxeIrlM/oAY93KCs622SLYWFHb+J2q7WofSbUSscp3gWj7c8KJqHvAgMBAAECggEBAJZi4BcpBj/L0c9gSg8D86zZomvNY0cQ3GYmPNPibKbBPS9Y9uiBr2wT3DeGHADQ2QOxIO7/4mDZNR+Mz1cONj/i9yuM9c9N2nd7oClcmz2hCualgF5p01BH9oBHWLW5IpgtT3+hN939X9SVTZpNjg6wpEdhQosKN8yvJIZaTyUvh/ZMRIJvbnbLg13gIF7Lpyn1rtFovQg0dET0C8zhTCDPacJIOLp8BIBMknPfOl0SrvOMZjufzVZLvbt0YraXhLK8EWe87ffTMoBlIktWpEKdPBOCuFf4E4WRXJ78tcbvNtx3f5zGi+ZVbKcLA1axu+OqbjHCG6yrlywcVBoTuxECgYEA56yDBaM0VFD1CqsqwYIWmAyYBjV7dkM+ogMb+mfQn+ja6QSt+U/APXB3dP+EDvysh5AZR0wpUrmz14xC1yB1/XAKIfMLQZB8DdUkuj5UcsKjkzLJkIFYGOXIutU7IHTma7s/0fLxwp8SvkEL+6nHuZskf77yjDAvWLZeSD/CYWsCgYEAqL0mKeyyhBBFvNJyE3CyyhDfzgf+NrvrNJcx73nAzLDE44BPc/3lHYn2AJJhasNnjJfRiFzW90PNgCjZLLXqeHkX4xixoibvRtb31WHR2UyxXe/KQZwBy11mPzStnI4Y83C2A8OXsx4xAPq69nX9foSFD6cuLkWUGeb8f7Jxbo0CgYB25mfcJdW+jEom7pAj/kLgSF5hmWNC3+IuPhBG5K8C0vw+6ULsmEyee7EjX9wD4RQfAwqmN+VhaqNtNbQ8OpGzv6PDprwZKzEv3DtcRo8K0vAmpMMkIe334T6y/Kq6zqRPmCt58gi4DPIOqM2gnJM/o+sIkRRkdHpoOjiLNgXp/wKBgQCNrGpLjwl/am4zEHppKhljIPHX+cwORo8/06ZAi/g9pDlbThLnr4fb2kaqyjxyuGfLmnh5xoFSkCINdb6KFJ8t0XYl3UjffVMvJjRle0EG8qaE2Vz24zZ6egvsC52ssX3vf3XDCUjoQfQg/2NUpVJWFIvnzZUvkom7ib38tWUZzQKBgDe0+OqdJEIdajkwCMEYbmZDYqkbw4pgmwSqCwK7HeCi8dvACW5OCCutnN0L57eEltyWy0XP2XmRlfsD0atkKBq3KgNfSawx6/t/K3OtZa8VAtg2M0PbCZljW/8Bz6xlxiyPXFTRgr9zr4yM1homMmPA39hURmXNNedXUh3IMkH7", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYtGLh+ow9WEJMn50voPGa6MsqSgJx8pNXGtk5kMSktWxfYHrejLZJjN0+br2CwpFMtf9JW6dAIpxb3qViBCFXjzEK8JuYaXM2sHC6sapyCxeZUbZJtGAXNWQW3qV7m8s8cJTOu2s1euT/G6uf/mIVFIzCkQDx+Ejh5Aie+BTAEf1WbLmcoDDxVESe22gpTxtMG8WTocMV34BxKn8d8vhcZZsi8LLkjg172QwQr3Q68jKgdja3K1YYm6fnso6H3+H06IHgPFAvVhycBbmlyR3bL/hFBl6+ElwBxeIrlM/oAY93KCs622SLYWFHb+J2q7WofSbUSscp3gWj7c8KJqHvAgMBAAE=" -} diff --git a/test/switch/test-data/id-2.json b/test/switch/test-data/id-2.json deleted file mode 100644 index 167daf01..00000000 --- a/test/switch/test-data/id-2.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "id": "QmQAbW9j3wQ8JDFmg8JRid82EpZabuCngVDmhqzCmJwqt6", - "privKey": "CAASpgkwggSiAgEAAoIBAQCAQjiCzMF+PQaDUuNa7avUsj2xnNTQcUrs4yHz/L+JI/AY2ij0iXsBSE0chK1KtBu24gZzWs3/BDyNl28E0Sd41QpK6oTVMHjUfLovO+h7G78bqpI83vk5CEOKt29VihQs282fivbQb5ALYwzBIW2lsIoWwrQq1btsNA5NXJ43OAcPZ9SybBUg49f5gWf/kmh/J6e1rvwyVjQc7cmmpzcQUc+XNL7db6T3ArokXZMyBK6oQCOaJc1bqwgHwYSI3parjds9k8Z6fXA2ub3Va//1EgjQ50lRZH03PGYS42HR1QSSz1eLjMmdrbJrZZj7IbXgqAO6gT6wlGLr5xMQudabAgMBAAECggEAQ9NBESJ4fGqHJDFUG8St5pevelqGTAhtZ+IhFWamXz6K/Il5uP9u9dmnNZqQDX47XbYfVSdC4kX6Q6I+SlzUs9htTfrA7gBpFW00BEB5C4k7wcSs+tWrE9bj6NpiXOjdDG/cSC9zn/wvP2ZM22DzG/jEvY6POku2hlzs50pAPNB7bBaKysA/e52J0Tu/Wf/+sZyp2MiYQJmIkfbYeDF2rqm5y04S6Z31O3SMQIETNcBK8T+L2jwx+Q0msB8toam7hRf1KjxD0yZe+Vff9tPfwjgEoWF+O27g3+rjDq/QqUfzOPMgvAFgELBMpv6CCM8/3l9gUu+7itBxDq65sDCoCQKBgQC6FTLTQA3ux3WV0/7MKXJIHgYZ4b8lIbiiWuO/6t2ZnwvLfTbiU5br/8bcRPL5ygFuIdzkx8VHcbkOmld/VE7qaRZoJb94JVvC6N+5MQxr+pzbWQSNcE+cKJgy1RADea8nad698ifls/39kZGCc6Srt2TqxTBuoZ3c9jEMs3N2pwKBgQCwcxNSw7Wkq302lKc/7QdtfegrwlLjRClLYaW9ESQeErayRY8pxLgl/XKap1HPyc0aQ+78W6w+DAxvcToGBsLak0ujJjzP7b8G6fo+cexuIr8NiGL4LVzpZfQjkfQU4DDwsOdedeKzGelIdstMMtAZDFG9eNPe99XeJBnYfIDS7QKBgH8xFjiHQ/6+n4T2DueGPPNGcm0mfPzoe8ed0KbR5v6mU+2XfPheon5VqpvNFTff9/JLey11z0byWMe+f6gs/HQFuKcfhiydfIdRnfp7qD32Y1kbE52J8yCOLtowAG4fsrWCDBpRdyvvR+EWqxs76IbnKDfA6UX1em4aaZSA5J9pAoGAE8aB5ue6Rt9VZDWa3QZCq9nNmIHp6kCsZB9ohN0T8C7mvOog1myOuutB2eVgvOoAC66LbUsU7ctJ5X+KIjzFv9t8Qae6bw9VNoAopLD974YDZY/gj7H91Maxav8jnOdXdNJOy/5oTuxbgdyWgk67leMUkiiljjq2hHQFVYb2pS0CgYBam0ZJ5Trds1LijE2eoYPyiJdhWEsHYFDzoV17cyjhbSrmlWJBNKQfw6q6UtnxSNFMvsPOZv53d3B8iIDnZ/UHFvw1et+yQk/QrxTfXurqn8lJcMCfKzm3ORKibgJPMmtcPbLoxuEKXMXx18iwoCsMnapijJ0Qj5HofluiupSfxg==", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCAQjiCzMF+PQaDUuNa7avUsj2xnNTQcUrs4yHz/L+JI/AY2ij0iXsBSE0chK1KtBu24gZzWs3/BDyNl28E0Sd41QpK6oTVMHjUfLovO+h7G78bqpI83vk5CEOKt29VihQs282fivbQb5ALYwzBIW2lsIoWwrQq1btsNA5NXJ43OAcPZ9SybBUg49f5gWf/kmh/J6e1rvwyVjQc7cmmpzcQUc+XNL7db6T3ArokXZMyBK6oQCOaJc1bqwgHwYSI3parjds9k8Z6fXA2ub3Va//1EgjQ50lRZH03PGYS42HR1QSSz1eLjMmdrbJrZZj7IbXgqAO6gT6wlGLr5xMQudabAgMBAAE=" -} diff --git a/test/switch/test-data/ids.json b/test/switch/test-data/ids.json deleted file mode 100644 index db62a096..00000000 --- a/test/switch/test-data/ids.json +++ /dev/null @@ -1,904 +0,0 @@ -{ - "infos": [ - { - "id": { - "id": "QmdWYwTywvXBeLKWthrVNjkq9SafEDn1PbAZdz4xZW7Jd9", - "privKey": "CAASpgkwggSiAgEAAoIBAQDiRcrWi2mFd9G3bRd3YvmL+NfuZ4/62WKbDkXndWfrUXGxLkQOAwCp4zJklWWXwGeqEx0GJbvhFK6BUgotcSMDjuWL45fCnNL+kttJI1oM5SP5nhub6SdJKsvYa9GpTjfVu4iI3JkCVxlG8QoPhC0d0k72HsCVf6goTgRuI0ZHTv20wOjbYevRIGgGr4MZEXLiasGl4yD8it7X8MXcvVWQBYkdJOyB4z4cRGxRM0TtjPdxBmQg2/tFc8veIBuoTQhLyu6ClNm72gDQTNRrGtZUCx3pKmfaJPvf5A36Wv/JZyO/KkexxRMlKOYpHUNI5yJWdXcSLjtuNpx8m0vpfu7XAgMBAAECggEALU0t1BBrWvZnPWMQ/K0LKzPx/2Aqml1leYe9BR8jZCCVM5UAuRFu05SSJUMn6N7zokBbYjyxxdl/KpMDSJ/LE85LNNunKaZ+M8uxLY5vW/+QWUyHWIqwe9yenUDQ5CWt1hPKvSP1WluXyvU9P2gGJF9TwcDca9H4F8Gu72IOkv3kE8yoA2z33hBeRlzUVIhlcnrQ0pLASoWTDG/XeZWIWR1zbJXKliPzn5p19MdfJLCjEQR24f/X+vxOFlrLK0l74j5/rhOZWTR8wqE0R04MCjC8BYqTHnfyEaqP8ZL67GFOjIfGdPOlfI0hrI5t47j6FKEmoCoJjTdVQqyXUuXjUQKBgQD+2SS9XtL+IBcRA+6rgQYX3Ly0kz+rncM6lx/k1zD4ciRXQbvf4MhX3ab1IZMmujkCvaNPMBKLxZ8E9N7MY4dNdNVPy5fjvQY/2Qt5dB+o7dbzRIlBRj9Y4oPtswfKWiuyOVFa5tIf0TOpApcuIyyD+O9zbNyqIOZUfKQniUzr7wKBgQDjS5ZR/8Snvrp4kw1tfNUOwdsEcQUmt51ey3sNmGe+U3DDdvwKJx0W1b5PiR1b1Wheap8KR1d5UQn2NjUp7G9GSbvFr5uOH82vxk26Fsv5ZghXZTBEs4/HcX2sQ+mCb60sOVbs5UlV2mnok+EXY12sZrVhmmsw0Q1ZqCuMQr/jmQKBgFdWm5y6rpyg6sbODjGAmlH7OEC6ZguumYWu3SNUDFhY5dNxl612H7LdJ6bCxudy0q75xsoQs4prQ8AzG1f4lBobfC9ImtlVopqnC6OoBGGkgRIF3vQb2wHfP09rF7RliqwdsJ/ykviMfaPiW2VYcJ0Z5xYrrMQxWj6CKM/T4iTJAoGAdvA4ytPiHj0p6qpYnnByNPSwHRTfMzFmAhLMY4La1rdnDIGYxd9N04MpwQjo+gMkSDPW4VQPrAYCBnq7OyLj34352ipYZfiyc0Z7qeL//ZOszb6/kVO86wqyTpCDAqRZpAilOfWJeImAXhnz8X8np21fgKGDcdoS+FWN5CmRrBECgYAwLH/G3e+QqZ9d8ihdKiPVBhrTVcYD3ElL2tI/S6ELTP7GBMz7HH64gLoZNqT7drcNXgrbPOjSC6b7fVyyIjEmT5vXUNNtZtlkSb9mNKwP4qFUSkS33liHn+dGa3gERX84AvL8lKTG8P+VXbB1XJHnPW6LPyI/eGelW6lEVwFkAQ==", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDiRcrWi2mFd9G3bRd3YvmL+NfuZ4/62WKbDkXndWfrUXGxLkQOAwCp4zJklWWXwGeqEx0GJbvhFK6BUgotcSMDjuWL45fCnNL+kttJI1oM5SP5nhub6SdJKsvYa9GpTjfVu4iI3JkCVxlG8QoPhC0d0k72HsCVf6goTgRuI0ZHTv20wOjbYevRIGgGr4MZEXLiasGl4yD8it7X8MXcvVWQBYkdJOyB4z4cRGxRM0TtjPdxBmQg2/tFc8veIBuoTQhLyu6ClNm72gDQTNRrGtZUCx3pKmfaJPvf5A36Wv/JZyO/KkexxRMlKOYpHUNI5yJWdXcSLjtuNpx8m0vpfu7XAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "Qmf6uXDHoY6b2Ahbiw5yf18XvRP7WDBS867v2F2V4K4QES", - "privKey": "CAASpgkwggSiAgEAAoIBAQC89Fbt4LtEk6LkBedZCoa+nIIAiSQpFW1XnMU9xTLviUtgBLCaYNgCuwpO4kVsZJvQMjINQqJEI1BjQvgpEkkduUVDq04NFdtcUmM1XVu5E8sm6nIWa+0skEkKEmm5vtnmB5qt/zN+pmwJzxOQXglRQ9f1WsiFSvKSMxNeXIh2Ht0QMqSNRX03uN8xBbrW+ZtoyUGbaPFQ4ZBABZcZdjHZmhkWBqWIRpn4JaUIdAt6nU+4LKISLV7aM+sBDiZMZ/CH3yNNwH2xDzvuAO1K3rygvMICNOIMNg23aieQ/C9MCgJTspGiMHzn0TkOesv/Ay45ONcPk1Dgy3KQg1/KXPB9AgMBAAECggEAPxS3XKzU9/ztuYA7Dt/TwhjP0cv29XxAx6n/szJ9YbiNIF4Qc0l3c9nrhBBIKvqfhe7sBL9FGshLUwgNfvCq1jB+7itnYDj2xah/lFY5g90WykQkmFWplWIJ8EHbZ/ZOGlxZiFMVZue6U7/9AQpTw/yJQVDwdodh2esRQURVDlGGQa8uqH9DImVOtnifClJcrftnF4BO73Wp7tK7rjNTt+UN6P234cZ/S2YXZRP/fGw3ObSopEYOSxMn212u7n+ieI0Cl02A/lUAcLQfIAN+XoH8cSQowc9xUOmBomplucOXCV4Mu3karZjlS5GeL17XAXXHg+J1kAbkWmXNBcDBYQKBgQDq9wx9P0ThBsiqOfPVI9Mwn/0rcaX6j29xK+kDMUVuxotaQjQB+gdO/o26DV1SOj6T9EmaAS/vCfQXMvWqgmubH2lCgC079RjgpQliJmo7FDufO0HoFMgwZzf8oX5Xld9fi/5QusWoGpyk8txvl8vvDlcg/8iC+Q1Lx1ZnPVq2SQKBgQDN3tDZ7n2IAu0I5B9e2YYSCsbPBh3TmQNGoguVGeseyzTGaLIEzmXYjgwwuhAzIgWZ/HMQMvvutMtP+opT+yU9XGQyWnZjPDzDMNtexY4XTmqQ1SWST2qNPbj9Gs6oZ1jJK6+MdgoNh7hFeT+uftzXtMzxVGrOcBMAK4qzT6oYlQKBgBeLT9YRC+7chikAi51U7KmXrn+28KHN06Xsd3nZaxKxlG8j6SA1lJvmx/7Xrf06VuDufp2O9uWmAq58bb97OBsgJ6UBQQccBTUldG5AWS64VU0cW/tMcc7f2O1YpVdTbkGdvosKXBn/KKkiqNIJzOaUckidONNe72UjgVXxAPD5AoGAAQYot8zN5w1MrIyl80zVs+VF0+XN5C2QrJtFv3ofh0mve4UtzYRRUWBzgxKJ3hc/O+Lbl6sJQci4ci9m3MAVEVcSUIXOrPOxwa7OiIwnBsqnEQ1eYHnwp7802l11xbSt5mJHP0WfCy4vpnjR7kZHRvNpSZIH7fr0vT16NSYiTHkCgYBEhZ0CHQcekk1/cWjIfNNjbA7as+doecQ+hj4nwlZG9Ng6CreCS/pMN0oupYO2pw8fm0xPZF2BABIQ0sI5igppyvD+sYF/SkKNUDCBq3KSD/5cuOdv2bEefnIhkhLTR75f8gJ4MJeCl4lkP695lMgYkalYHrCkJLuKaGcGmZg4FQ==", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC89Fbt4LtEk6LkBedZCoa+nIIAiSQpFW1XnMU9xTLviUtgBLCaYNgCuwpO4kVsZJvQMjINQqJEI1BjQvgpEkkduUVDq04NFdtcUmM1XVu5E8sm6nIWa+0skEkKEmm5vtnmB5qt/zN+pmwJzxOQXglRQ9f1WsiFSvKSMxNeXIh2Ht0QMqSNRX03uN8xBbrW+ZtoyUGbaPFQ4ZBABZcZdjHZmhkWBqWIRpn4JaUIdAt6nU+4LKISLV7aM+sBDiZMZ/CH3yNNwH2xDzvuAO1K3rygvMICNOIMNg23aieQ/C9MCgJTspGiMHzn0TkOesv/Ay45ONcPk1Dgy3KQg1/KXPB9AgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmdoLademTcdA2VM29NLb99GpqajKZWnVeQrKp99XpJn61", - "privKey": "CAASqQkwggSlAgEAAoIBAQDEzlaMbNq9kIAET1QHVsAXFENOtU9iWTiv+VyEoEl2P6ABkf1q6c3WjsP2CxgUDdHvstb5kyt38RhCIxa6CfQ0pg1c5/lJlDLmqx4eG1z6KTgYh6YePHjTvSFbKjrhCzFnXjuwitzkzAyYcaZMNeN0jkBU8uQ0FmgIzDZ31YgymkXqj8aFdxNx1Ry290oYJbTH+wsSEeIjSCGwvyZSF+IxKnwACfYG4rnZchEvd0mBnfdVIJW1xKfeVrihWnwbqn7Abq52gvFvojbCQlDzPvwku+BoH8yBEBuShXPHfHA6pFyULPa4f5TwI5/dyGm4ptLCCTCEYCuagrVqaCH6BwpdAgMBAAECggEACccWlbNyyqg7M/uc+SBeOsdO8MIhR4mXP2bsKcqs26sdj/Zo2L708wv0wGycraJiI76G369oIXVg9yg3INcNwu/dChicUgOC4+LshCJn5CXYG5/hqO7oMdzbo2PduQCNW81audKsVtGsboZ29KJYwpmuqInIvK3ATW+X5Sw+sATTxMG85Csr6s+kO3h6JVkbe+xyaUvyTNu/9ajkdpAIGlFqPtuhD3yxKaVxuEP1BIqr4RHzUA8IV1qB2M7PLstIFqAsUUOYymleeHZb6xDSjZxg9bO9nfSFTRCu/d6mSFj8ISE0YaE7w6nHf+P25VPFhIGzsQ1lc9Jo2n/DlxzZAQKBgQDy+7EXHewzOuDh+Fpsx5oBJVOixUnOryFgpFXe+o96x4Fjy/rHUUwSt01ikOOdmXqbtNpyJjWARPZWud1C5euBcePrzaVyL/fwUjCd0DFfka7ljB1v+mcb5dKkeVp0KFheQlqA7iOyPy4HC6xB3ZGY4uXjnEkB+nHLiQ0jxg0oBQKBgQDPWV2cswRDwQ124hhVn4sB3SfaUzNZ1m1zklHWy4sGXPYaEaG+gO4IIP6tiqG5E5Aj+wUCEPKBCwANW80bNOxdZIlFYImt82MhLuc2e/9U/hA8vXKwrX1Xm9/HPfbi/ZkB7WSVBGntoMx2uU6SDdJURHeR4RYFrUknw76GJ1ygeQKBgQDTQAPdB0Td3Wi6zYNAY+D+8gbe0wuySAyKyxVlQQ4RPva9XxBuzb2H4BnFghaCZHd2fCwXZiTJmitZh0pY6TBxYCU6U5ZtykqTg8GE0wa6Ahy+say+OEQAuzUBjggYSSNa//FTerdKNye7NGjU8t+svkgENVI8CBN7U3I7EetKSQKBgQCumNCjz3Yq21fMIGxfRR3XLvOM+vxFjLLTW4VAOlrRu9ubbfdlo8lL3QS2+wJdBuUb9xZre/vHv4yGsyON4k2aArs4SScF6+kwGv+kuFrzpY/kpZ36ucvOxrlzW3EWCHcb0Vsdw/6ykvE4k6degvb18EVC+GcD1rvAGSrIakKr+QKBgQDqTd13e9sjAtwabxykZif3j/eGSzHUrxrAAEqUJTA/yrEEHIRuFRG5q6Y3rqD+EZMoIHd9KeSKnv+WyeHLhT07r4V8RSdHKWJP5+avy25nJ8ykOUpawiwoKXeID1N/xLNMrF4suUgjjxEWjP6nW52eFzDhMqcOjgSWfb5zsLfBOQ==", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDEzlaMbNq9kIAET1QHVsAXFENOtU9iWTiv+VyEoEl2P6ABkf1q6c3WjsP2CxgUDdHvstb5kyt38RhCIxa6CfQ0pg1c5/lJlDLmqx4eG1z6KTgYh6YePHjTvSFbKjrhCzFnXjuwitzkzAyYcaZMNeN0jkBU8uQ0FmgIzDZ31YgymkXqj8aFdxNx1Ry290oYJbTH+wsSEeIjSCGwvyZSF+IxKnwACfYG4rnZchEvd0mBnfdVIJW1xKfeVrihWnwbqn7Abq52gvFvojbCQlDzPvwku+BoH8yBEBuShXPHfHA6pFyULPa4f5TwI5/dyGm4ptLCCTCEYCuagrVqaCH6BwpdAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmZR5a9AAXGqQF2ADqoDdGS8zvqv8n3Pag6TDDnTNMcFW6", - "privKey": "CAASpwkwggSjAgEAAoIBAQC+5OEQhdvRsGh2pBY/a6dGBccThaH0L8coYFYHPoQx9M7puC7smS95zIyvP92j2+RK9VIswgdAjuqWRyBpev+i2TfFE37kcuE67HMGVWdk+2CAF79e6Ndabcb2/TuAT/1S101mV60niVn7URYqfFStbfqaUcsBViBbEKCIQtpA4AfmcN4rI+C0q4BncRLQHgr0tqJ7sHPYWbhPHBP6zJfFi7Wl3o/6lp8Mn2TuMdGhFmYaGZjn97F7NEUct7VzXnRTxXdwtNHu2RyeluASctHDr6HwKUsilJiUiZcpOrBdV2zrLYqO1yV2uh6R6r+xfejcIbhmS9Izd5wvlRv/v0UbAgMBAAECggEADY/VLYdVBqCxyzv9GKRdTew7KHfl+aMrUwMFGZ6nZaUuzgv3yXdYmB6gIBM5e9qzbV/gZq2iNkPxBpwnAVdrsfYcsDOiYDiJJ9aElX6byeDSCkeloOiJ5DLIX+O9xm/oX2pMZWj1NEndyq0IFhyfJ3MYyr3k3kNwKQgVX5jgSJuCTCMB4PP8IJDo0dRJQucjyBCeE91S2FpF7+eyxPsfz7ssl4EZ1RzqSmoAB2p1B9e+ajzSkRl0JX7nFTL3P/lcwn9QyvoBtSmulfZDXvMm7g07wE5B7EqFTzI7nbU2ZjgD/1Nk8zCzUSfibdmI6EXBc3k77h/dKc1WE77nn99DgQKBgQD6fSIja07V1hpMczSrmLabuRQy8aBAiOUT6gNGqU07eLJYokpUlLEv4Gs9vy/tcfGbPrJ131v0bd9nh/QdktIyhVwA3g0CU5kBUNezYHWRRA3zXHbNYuiHmRgdaouzN+SmMMylOxL/2tOj8Bb5Nm9L8LuCp8y/E83dYRp3cCKtHQKBgQDDGBTG2QWWftcQnU1jTWAHeBf7K05KwDgJAPAwB7dmQpFtlNxLxWpOOEixOPzO8b2FP/QRjapnxsktaRrhnMZ/nVz3U5nq7HBJEairFsyLNYF5DIyKXValHxjol+4abaxGw6YotDmsGtg00Y3nz5WOViNiNJxdXm0NCuf7uJR9lwKBgQC4lZmgjCTuAvYiPAsmIEUAf+RYniG/LKHSiPGdEoltN8YE9qLbrS7c3v1n5QlGal7mTc9oeQ3kE0s7mb3URStMO2XO5dKkUkI/6/jnoD9Cqum02gBZ3XcI5VIV6zvC938w0GkdoWigzfqDphrnzqs5RM6Iu2pvrAJaDoJYXXPQKQKBgFfvrs3CXIZtPbs7a/pqkfJL62NHLc77vUYxqhG8KKprLunZw0JUBYqkS/+11B3jUK2TGgwfcsO8EknpqjgvVjmHULQadrIxSJtm3kPfzuqgf290fJSRZdCfp7aPZL981749ydNnCOfOYc3M9s2Z/6tcoC5P0Hs1aKoMVGxd0nCZAoGAVi+UF2A+mYuDcu1dU5vlBt6FQmcwvy8wodY/0RR4dpDwMsgdSwMzfVy/nR8U1HXOWnQP2rlej0PrOc3fWKC2vMpTYtQ2lUVGCZRj+DnjIsodF2dyQXD7zzpDzh9PLO0n9+ZWf8aZkmKhHQc7HxB4aJLSJ+r6ti9zfjHqzOnXC2Y=", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC+5OEQhdvRsGh2pBY/a6dGBccThaH0L8coYFYHPoQx9M7puC7smS95zIyvP92j2+RK9VIswgdAjuqWRyBpev+i2TfFE37kcuE67HMGVWdk+2CAF79e6Ndabcb2/TuAT/1S101mV60niVn7URYqfFStbfqaUcsBViBbEKCIQtpA4AfmcN4rI+C0q4BncRLQHgr0tqJ7sHPYWbhPHBP6zJfFi7Wl3o/6lp8Mn2TuMdGhFmYaGZjn97F7NEUct7VzXnRTxXdwtNHu2RyeluASctHDr6HwKUsilJiUiZcpOrBdV2zrLYqO1yV2uh6R6r+xfejcIbhmS9Izd5wvlRv/v0UbAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "Qmeb34XKhbo7VtHMXmyep4MKdYWHKSVvnBVm1eijoJmy8U", - "privKey": "CAASpgkwggSiAgEAAoIBAQC/uQH9VPMIQ0Q5FIHRJj1rvYhXX1q/83Gv1EVBc4fOEPJMoRys47V78y9NXtPu1NivOi3qN+hFwXdNIX4azEQNiODHRJ4To8HC2uyoDtL0cjZQucEGDSJXXkMbtEwS1IFvKG+iKaaoFjw4LErYvaSYVSsV5xFpzi+ScL9u58bWEpwG5pAI5WJjoV4BX/mO5NFDfViTOsXAFkeiZQeru1GoJ+bWuulsLjnmggO++9yQG+PnXw7d+7S6XynCEnJv1F1KZrpCyfStpoayHHBg2zyDwE2EpwHvXQzTxXN414+8V0AZ7PPkQptFcxEKtbJWXXBnlIc3V70rtEKRAmlcKqZlAgMBAAECggEAPzcYUdh1ve64Cv4ZA8ZREDpRP0XgnVP+01PxdfBLAgYSbnPdCaCXUYRQv3kZ9jDWNYjAZO8ENiPhW1xEwT9C3ReZzfpxCNbA56fZylwA8LrL7/gfjgg8n4QkKnlbcAYDm4xAqr6DBf824eqwzyBQqi3C5BjpY/KpOubUKBRiOmkcUeqqVxXe0Dud8qz3rOaSOsQ7LR8VWliasCqp41uGbgBlSNnhFDFG+ONep71OqNMEV+/S341E6MbDj5243JVnLZGdyqUK/V46D9ALMdr3QKy+51fHXPz86GJvhzBq23xdmHARt1nmDbEjALw8lva1Yjss1vXPKJr167lkHYAWDQKBgQDejPUK/qqmYLFi3PhfF+Rw6YSSPPi2n+tWF5F7gm6xxzi8vLAHfr+U9vV5GtWXztDJ5DbdKF31+WlfBOOFWMYw0G2Z6ad2nxvzAMcuC2jP7cIf6x2sMq83AdCSdxr0eFr/eDA26CNPrIQyMkAnScSwsnJlce3j08dU8zOsvUdZRwKBgQDcieO6EdgxZr4adkSUaaKeS4NZSzOo0owZhDk0ipHHCc7j28LmyKrA6ouokLKB9GSYHIf3hlhmUORljMwR3wElG6aDd94awEcsuw7gYnoHUp6XIYr2H5Kr5r5rcUOmIV20SKD0k9OZvpeZDo+paRT+xPdN8lpdGZXrD60qH3vY8wKBgEL0e4CcT7EQpC2PN3Y8lPDXgJgSme0vvbjADHfxLOZ1fn9h8T/ABVmG1yFhTmOGyFAFRfBRhbtMF0SMDvt+Uto6ys6kekp44grA8CvNKPJtoJrDvMCi2w4ckKiQBt8IGrCDc1YBjyYYTAliDuUDD5btiPc2SJDjlTPcm25b38xfAoGAZat3/b7eQSARgdeGFDmCy6EaY58EqM6v4c+QI8XCINVHuMoGVyipd5hpXAOhF8IYYfu9PwKDXF/se1hmd9KsD3Ro1nD7Rq/f4CI4YH9lrFyNWjUPgBncHz2YCaZEvqDhNwzIjxhbU6SG9Pu+hSY5lJ4vOJMCz6rM73nhpeqvyLsCgYBk6nr0QbXLRDnYcK/91NN9mJQWoj2p3K4KLtkJh7zWHKDihf1a1cxRUj+kfNU875PH+TaZ0Md48VnnYlTwe07Hfc38rKZMX6vODBHbe654XGd5yhibhtTq6dhejLatGpwVj7cFM2TfA8lhn2yS2aSZLeOsUqEsRbpFc7c7dOcljg==", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC/uQH9VPMIQ0Q5FIHRJj1rvYhXX1q/83Gv1EVBc4fOEPJMoRys47V78y9NXtPu1NivOi3qN+hFwXdNIX4azEQNiODHRJ4To8HC2uyoDtL0cjZQucEGDSJXXkMbtEwS1IFvKG+iKaaoFjw4LErYvaSYVSsV5xFpzi+ScL9u58bWEpwG5pAI5WJjoV4BX/mO5NFDfViTOsXAFkeiZQeru1GoJ+bWuulsLjnmggO++9yQG+PnXw7d+7S6XynCEnJv1F1KZrpCyfStpoayHHBg2zyDwE2EpwHvXQzTxXN414+8V0AZ7PPkQptFcxEKtbJWXXBnlIc3V70rtEKRAmlcKqZlAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmXcgkTakwu7rsVf8ZT7A3uD5XwNB6Pbz2AbbjaENrFe3N", - "privKey": "CAASqAkwggSkAgEAAoIBAQDW/ZsbRHAZf3Vh2PyUSlXbBRHl0K/PCX26vMe38j/eKSYn80Up3iRk1Fk4MUKgC0X3t1Ldf/Y6In5RhohbX7mpni4kHErjbhKtjLqQWCTiSNWMDVuFoZ7jiy//Qu5V+w4tdHogyNVgLhQhn1SouGJ5wgYP+XA67NQ3XoSzycAPU1N3LWuqcWwHWI/L8mY1qsmZ46ShporoSul+Pp4h0sDXqSPSJAZJsSV4/baywJMBXD91wRo1IhKvd+3OcSgVAnNnmBMvVodSzN0xNmmMGvHijslam+mYWW4B/603QAeFmYK3orEKcQ2lZVV/1lsvbg4GqrlMLkK4hM8NbTASa4LLAgMBAAECggEBAI0xl0lMJBcK12uAlzlIrKQf60Y0TRI62IDodH4BMiLUgYOhSB4cD2jM8R9vcqMrZDMxCdIAtRQvDSi7oxfngUa9ZO5ASoqdAtVJ5EjiKq8WSHEnYKEdqP0lr0sEiQSc0g3WPlMDsubsvDnsqyv3lG0EmPiqyCNa4HDQuXReHq2wxh8XMkX68CaqeNOianRZ/r/pgXjVigcvxckUA+wyTm+N1tvnbF9jFAZkJOYbL71qcf1VHTgYIZF+TZCRixSaysWSotmeGX3PeoJlYHF4/5G+9YJknNDRRB31+IAZJX68S2Nz69DxuttWCIRiSp70gyctG+DN0xmNAQhTEo/VRYkCgYEA+RcXbAgMMovnp4rPSklez8+C7tFxNeHMzYfu9UfHIfzrQXZncs3W8K1I+p3e7eeKZ08mruzAL/U8vrlr/ydtF1kNNaBDMUfibovUh8a4HlHzqvYlBNQq0NM87YM0P4ishQ5b6MhTIcLv/Xhjjr8tyLFmevt8qwJWh8uEl0pREQ0CgYEA3PRbb+qMECEepuYxjkh5LztzQNLcab/KxtmK8om5hmXahT8t8iBobxZlzjDxVhAL3OYCYLA9tomULMBXJxJLS44uH+S04M/LbKFl4sDZ5scgXbwOpR2+a7OQJeRwhxqc0ETdFP3qfGLtCeJaYHdeq2M39fp2ywb1HEPB+nID/TcCgYB7hVLtFJSP4EbxE2m16eplXP8N1LiyQpXf+h+qbHy4QwaagM/N43tKAHRnKzBog2Bj2KFTLz4iyhbkcWi3r+JuKI/fXujTIFWOAjNTXVziVDtkNQmoeln9EjNtiJm5Q9phZPx41BY9cMC3ziJ4oB9hHW+3Xsy0tMUaM/c9WvIWZQKBgQCYNs52/wGWavqOx64D8vFpFG+FjL3DLBkpe9w40aA5chlkCe5BCwpm3OstbJIVU+CYQOwKZ99bzNODMM3ZYMT2O/CSkB/7b6sYHuftmiWC0lL9v/vmy+LOl1kKgaDzseWtpIMZXwMWxZ++W20fX5ycPTHkBrOnkhdxbUxImBsfaQKBgHTr3FGm/7BhVdlG1GW5qlTIi1gRLr2w+0Txmikoq6i1ljfV5sQ+qHWNYX5SEzShcXvG3qghyjtkvHzP2h5GQ9ji4Q6R5XC885vSpe/4/B1F82otAMmEqPMppk1qZnYbsjfDi06n8KHk4EGZjTVfPe6mkXs8R+hK0VmFoHYMQzdy", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDW/ZsbRHAZf3Vh2PyUSlXbBRHl0K/PCX26vMe38j/eKSYn80Up3iRk1Fk4MUKgC0X3t1Ldf/Y6In5RhohbX7mpni4kHErjbhKtjLqQWCTiSNWMDVuFoZ7jiy//Qu5V+w4tdHogyNVgLhQhn1SouGJ5wgYP+XA67NQ3XoSzycAPU1N3LWuqcWwHWI/L8mY1qsmZ46ShporoSul+Pp4h0sDXqSPSJAZJsSV4/baywJMBXD91wRo1IhKvd+3OcSgVAnNnmBMvVodSzN0xNmmMGvHijslam+mYWW4B/603QAeFmYK3orEKcQ2lZVV/1lsvbg4GqrlMLkK4hM8NbTASa4LLAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmVc9FLuDob3aE5ToYqx3L9MandZL8TD23mzmamUqBtz62", - "privKey": "CAASpwkwggSjAgEAAoIBAQDR23+nHVwMhKiv06tm4hsMbvPUpMMBCnv4SctsJA/6ByOfCy6tw66RURfMGbk2xnLFv74/vayf38jckD9lMmz2RSePsRk05IsZxXXgBzxNEO0eL7MoK+60CvJaYflFB9TzHKOnZheZktEa7wZttpF1IxpXS0XqejvuSPH5FqO8iBBWgKqpiXi0mV/5M9UJ0exLynKFPiB9zaijJEY4SXmbPqHPQeARcivqGorjmpRBe6QXWjGOKsRaWdbB5WbI5iz4Qh0jTThy1LOkM9TBY3DvbgmtZ/+0G/BgLjrZov38TKSBb4nNbNysgV1iphUucQl53sUxbWdaLq20RHrOkyx7AgMBAAECggEBAKGeEel5yvI5GFCRC2foqjwhFtelLCkZEfBdpLRb8ZH0/ZH24rQgB8kSUul0xhdRLgLtcG9WfCOEDQUQckJVW2UuTRF0qpz5hccLM4SdDeusJXEh+y/s5aDy7UJ+QaLQLUgtvjulfHdhgnjjrGfCOrOjnR2tcuLp0E3rD69tqBwAp+744DBvk+GEnwduzr+Akk9F1UlxQ1C3FmnKEwOYrHcdGQvsFQf9mKsfbmDhqhqaH7cr1f9hlRXjFKN39xmVWVgqVDWZ1Fx133am8fNn/Gzd+5lSDkyAWsWcFRJZoJ+n03YOsf4yC2OzDhzVyZ7DKjouIwYeqI587gseox7BBoECgYEA+K79ITiGNYJx6rur3MojRTPajcX38bcvHBCDegttmAKbUgepYhQppueuC0xFjdqg6NBbtjbeURwI4G/qyD7kd2jI3ZXRiaBRbV21BctWt+fdCtp0Ri7FLWSKHMq2XpgHex7nxj9POqmOdPf0hHbJcm+WFK8S1VFKxt7jZLQ0siMCgYEA2AgVNl+wBGr1lgvqnxwZhfFjZJvbmn9lA/IabENcRDMGexxVvPwX3U0gH/2BMrsSG3qMoyF0hH3HHAYJQSnl28vqJtd3AbBqtJ8p/+KQHJ8RinPuxrc2/lt9WlKxxV191PR5hWgyR0r7GGEZAPiDTya5/pkmRgILpOVNX/de5ckCgYA11kxeoMoNU4wt8SsnxWsVVECAaNdgsPO1861DAq5bNlVB0P7OiObrh0SalYyJRUeIn3L7Y62FibgyPohpiZQUdc7micSvMtHuB1dlRbwkXEHyU5DQkNeHGDj+OrR4jhkwgmRS+unAHW0FzZhWBRFfgODQ4YYGQG8b1q0L5Cd0WQKBgA49eih7Zj7kTgv1/SE/2O7bWpHnNDKa8y2vZ857IjncozC6TWyHsYsE6nkxXLLbYfYtvdeC/Qs+v0E5pKKHAH/ckTK+QTn7Rw1g8IPNi3JXifB2c+blbNqXbUvm55D6+LBw7RG+LJJGfwa8X8mQmBc/lkMSFVPIDrxv4QnSZI8BAoGAQIoWEL49iWhre4EedAS+awGFxVs1twC+VteWZoKwEmISjavUL7ecRzFANPCi1X30b6VJhdI4lZp+c6OeZAQ1f2o8I/OERDYWeMuEFZVKQCzxsfXVUtv+bjKflKKdevhMK2wtG1KgSEKvhLWaNZCbUNgEyijyikx0Gn6f3mx7e/M=", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDR23+nHVwMhKiv06tm4hsMbvPUpMMBCnv4SctsJA/6ByOfCy6tw66RURfMGbk2xnLFv74/vayf38jckD9lMmz2RSePsRk05IsZxXXgBzxNEO0eL7MoK+60CvJaYflFB9TzHKOnZheZktEa7wZttpF1IxpXS0XqejvuSPH5FqO8iBBWgKqpiXi0mV/5M9UJ0exLynKFPiB9zaijJEY4SXmbPqHPQeARcivqGorjmpRBe6QXWjGOKsRaWdbB5WbI5iz4Qh0jTThy1LOkM9TBY3DvbgmtZ/+0G/BgLjrZov38TKSBb4nNbNysgV1iphUucQl53sUxbWdaLq20RHrOkyx7AgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmYazJE7CkpYuDRQihzJdAqTNxHnPspEyoqTQ7TJ5pGQW5", - "privKey": "CAASqQkwggSlAgEAAoIBAQDR/98aOI26A/x1Uxu1/gm/belKQw3FnUi90eniBFANW8Fp3EVjYhuZELJelILmYex1ELxROoojxJ9g+eBNxM5/ZopzfbqBT2vUhH/94AdXXRksNR2WKHMVdy9M+tnIUpMELuey2ibkQSOMlYoqdQI1zNK3fg9QsACRUkFe1T2Z2gkIJZ/Lasb5pU4SqaTrhqtBs023ghdPaC0wV+LRwAhuso9mwWysonYfuxRi1++1RLizBHfFjfJ9sXRTaPjb6DEY/W9/984ypIZOoeif5+zq9UV+tt9LBrmuHKeE/K19Q0WIpoK+KlSGN00TwglSy1vI8q34FFguxr3d41FqOlIxAgMBAAECggEAeC7q/TOukO3lFzRYIKDh7Ue3AwQ7JoSsc85l/y8erXZ8y9v/bjBgwQoYOx7dh4I1dI3+aLKLCotl93cqUve2gp0p0Yz8JzNP8BFguufy66HhXTaM1zoRGxDZ5kGOUCJJ91Ps0KQfK/THppaSu1e5yxaM5ezkUPZZbNHZja+WkKx5gFU2s0VD3zpX8rrGbsJ7w6n0sW5dqHYyBcE5u4dhopLkRcLwtAti1PL2N6qsKXc7eEjlKsgFCfe0gBZZO6o9A2ABXob5skB9k8TatRCKc54aGJtGjHjff7Lwl/D6gGb+GrBeUCwohIC/OON+lg4dv1eT55wNm/RfAOD7NnlgXQKBgQDv6FMoy4zLpSjDtWU2QDzfX/t437hCXSPR8pG9XTF6lO9aQk5pOvMrDt1oeOp6L27L02d8RGxcCO5krj47zAxkGSSJS2Msw4Vg7/IpcV91D86wGEORxzzhITHccKkXK8eAIwffKDg8QgGA/dDpGWKURx+v+zErOORDuKtk+gKUwwKBgQDgFffcZqoilhj2sIu6r8lp/a1DZeLHLIQTMVtflP9GO2qprnbOIYwU9W3iVc28XpLDjI1x2+MduohqBYkjalpvOlx5XTI2t65fBa4xtmCOfOwiBNhBkLS/sf8cSCsVEDA7Ixwd9FnufZNlTDumKzipYi+vpQt94d2t3dMv3sA9+wKBgQDgKNDC1mYoxZowOyZlqWH3SSSbzVXKVGKqwZ6hNBmOMujuCfRf6J/bBJmmCwzzu6wnsNEJ0Jj66bFty00E7GRLhx6XViRFaC8Q40H+rRsHMwzphtJjvKjKpgyDr5SevN48gP7S6S6aRwZGs2Hm2zw71bTq5qcLfq3yBPPIdr3ApwKBgQDHekbe6HVjvIIUeCyqz3lY5P2sFbK+4x3fh/xzJcvo1VOqISiZbruonKJo7UDsArRbZ28ygC+5cyekWbEu2aoPgcB4OUJN+006QXBDyLpDnWkHD5EDLLH6Q5V5s7TGV1bYDfUlpTO5XggsEKS405jpEAKrNRz5vmr8L4+j+YLgqQKBgQCBBGCglLh4eUZEx3cDXzAonLEJGKHgQtvDXBHqYXImmeBHHxs1zcL3oL1n3dYTuXljz39YmoN3RRWVBzHsfnj6m8vBROZgR3SEipLPr7mVtJizdFiaia+YBEPPZdoOTHIOxmLYumvu/WArbpSwpk9RBJndbD0wLpbUIAUUoR4CIA==", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDR/98aOI26A/x1Uxu1/gm/belKQw3FnUi90eniBFANW8Fp3EVjYhuZELJelILmYex1ELxROoojxJ9g+eBNxM5/ZopzfbqBT2vUhH/94AdXXRksNR2WKHMVdy9M+tnIUpMELuey2ibkQSOMlYoqdQI1zNK3fg9QsACRUkFe1T2Z2gkIJZ/Lasb5pU4SqaTrhqtBs023ghdPaC0wV+LRwAhuso9mwWysonYfuxRi1++1RLizBHfFjfJ9sXRTaPjb6DEY/W9/984ypIZOoeif5+zq9UV+tt9LBrmuHKeE/K19Q0WIpoK+KlSGN00TwglSy1vI8q34FFguxr3d41FqOlIxAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmTw5gC2FkobMiSJ9uWAUPXuLoZMRXkXrAVUQYTA972fo2", - "privKey": "CAASpwkwggSjAgEAAoIBAQCvs+za7Dg7qKqOg3e+Zc4y0fKucQ72ECtbYTlbXmPiVeLQ+FZAl4QDWno07aya0ej2PgNlWX9USO4gU7gjBWYJpfm35vFpSedV/dglS4G5SdysaE6mvh39GBqpP2mIsD+R7STrpCamh+b/K3GUb5bwfbrdmkMYpsfANH5Y3TCGVBNjFFZPlnOIlafzgpMHJRcTOhCGmO39KQt76ZFfgtXqFXnvZhiedeP5bdWTRMHc4zP12IRLLLAoyseysfpYBdDoSETytPe9YGEQfCHBcfzz+xPTrEQXFu5uTz8PUEyH3pR6lX/aFLrQLyJ/CZZHh3ouCEQlj2/EM8ppYNkgiZu/AgMBAAECggEAROVGgOmTe0E977f5Yj1FR4QvpttKRI4+kgxjk0JF5GBNGifmmllPOIln1g1EW0joEnZqmnknhoM6bI6na4QYaLweWVBDZUfHYF6zPJyI94DQ+QHFpXhzBeVHvwnQdfq2UqAslAG/7hjoKTJ9zPictRx4A6ETojzzophy2qGQ/3qdRxyc0ybcUAAcSvArnqTPp37LZgSyBXU4yo+oRJtmgCFBCdUimgn5YVx8YQ5Zub6mGutWixVVuGChaXYx181ifT/DqWI26WACvs1mTr+9GVD4SkZ3FUEH5X1KvggydQesvFQrjKp7842b/bsP+z+jOXU78+V2I5DJCsnfJr/+cQKBgQDoFrx3J06md18BfrL0PDz3eRFKR5vQ/8l7WUo0tN5UUFMoPq4xHlbBcaKEdhM0FjBb0KPqugugLIgscX5oz3AfEA4l4ljHoDABN7D7A4XNLyMgx7erH3aXj/1hz7++QH3VhW2rZ/uUw8voTkFdvYjXoeToUIlESYywMA/tOnYHewKBgQDBzgckMga8SGr8p+QOdP3HDt41/XqDRtHX0q8dBw1akyB8p6vYmxKb0BQuNlR+A/rzJXM5usVdjSRdRWOr2lHPyJ/NkH2oKJiYH4+q/n61DFRHA0f2/XuWqLaOdOk7PpH/7ctx2tEodXMmocyG+cVFEwRnMoMXXaeN/Ck75/9njQKBgEGe/BaslH5YzhH8ItkPlyVZo9vet122lN89dc/FO/+W3oxIfLQCogD8Ajl1sSRPCclMCqy5gcP+E1qNlHJKBKejwHxRrUx0LF6LwoyWiGRlaYdBMNs/gCaGXdwkA1Dlpy6SFVobgnSjj6nVRoIcru5ZJgHRk54tNYwzaq1mlCy1AoGAYCfcmzTG6rvzeQ/DsviQwSa7UYZGNsP4cWByybAqC/pbb/2w4XNvNCd1G8iQ+0T2SZUXKllkexoAJNa8sRNM7A7aWp+J+NjLfQ6LtYc3TpSja+hQ2FbD7ugeS2fuIBrXTWeqPP8YLz62t0AnvgBGxBK/aIRDTmCFNYka3EIrEjECgYEAtL8cYqCkxJQF3fvN+B/iSUabXV7kUKeB2eELaodzAEBJjcfKwwTEyAdvb+7YXND5TAF83+qoXsq+f+Dbimh1MKsYV3kBTIxDOHSeUwgb7IS4mMlyr/A3SftDQkYZ/nJ9MyFfLhfC549WSOZNMwXs6AVxV3YzfoeF+5Fv4dsNZa4=", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvs+za7Dg7qKqOg3e+Zc4y0fKucQ72ECtbYTlbXmPiVeLQ+FZAl4QDWno07aya0ej2PgNlWX9USO4gU7gjBWYJpfm35vFpSedV/dglS4G5SdysaE6mvh39GBqpP2mIsD+R7STrpCamh+b/K3GUb5bwfbrdmkMYpsfANH5Y3TCGVBNjFFZPlnOIlafzgpMHJRcTOhCGmO39KQt76ZFfgtXqFXnvZhiedeP5bdWTRMHc4zP12IRLLLAoyseysfpYBdDoSETytPe9YGEQfCHBcfzz+xPTrEQXFu5uTz8PUEyH3pR6lX/aFLrQLyJ/CZZHh3ouCEQlj2/EM8ppYNkgiZu/AgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmRkAAboTpBsx8xB3BZpf9JcGfojpbUapi5M2tTgUuhaR7", - "privKey": "CAASqQkwggSlAgEAAoIBAQDNK8zVQEpDabjQnPpSAgU8O0yqtRHUiHPIFDB/MJ7aKlqXnOSIrM3ZmOBPl+WRRwOZnUJFV+Zkp1rYMF56oKRn2CJ0jrAYDdJ9At63ozjS+3ejg+F46zo3r6AEvcCKNIRFAf4IkI7iFLYL+FbiO0RVB3fmQePanSCTAz3WuNlmO93MgvEQB/HwAytMyuhX/aV0dYKUS2cHfJXUvw+6qnBnZ9nmW4acVUWf3+A+bVXrR2KX9YBG2p8XwljIkJexdilfiK+6PMae2OPvzgfd1J2fHCTLQBEyujNDu60egxnhVM5EjGwkwUqrFGWs6sXD7JO5lfRbjigZ8rxKInVMISL/AgMBAAECggEBAKAYtH4W65wM7CUEySOi5fjpANsX7bDdRRN0BZ/KDbqJYCV8TKwFw58u9qHFEmK5eiqtFqBLhcE3AeE+ZQrlPUS217QB/5DVgFECI05CdD3V8bZLW25ihwwa5A+vDYYKksfSVSrTulrZ9HAEua9QtfJvoHSxJ55YC6oL1n4twZ5OZAvCA5qQgx6SisBK3WoMwUtGPvpiktr06cXJTveHzn/8gQs+UgUi9hGQwT2ytwFr+15o7FMHxiEPXoln5Ltsw5PbIWD6kAHLRPaH3NEMdhR3CpeOxLg/Hk4qWLcDr8kvOccAf1gsr5mYqkdTNqX6zTgGi9krfR9L+u/9UBG19uECgYEA9uyvmVPtgVroJkTG2KxfDXX4zRcpa8+WQTZCv0tjdRC4A6nXk+zppzYibNdt8e6k+ofmHQ8sEZ0UlLy+5auNWGxiQ4DUyX/SlhB2BY+bpfQ2/yXyW5b1l8y/XSzojQ5coYG3CSvCm+iSCiJVXD5oFpjzi4GjwRfgSsrsbbVz5q8CgYEA1LZBd5RveQCGdL7H0N75j3VyOF3Xu033mPO2NFO1517xEcYJlfzNe//Zikwe7fxPH5HEp6Tud88pdIcNxXMbaA6GL4gJAXMWoT4lyZVbG4QAz0oyvasByEv54UZtgebX0k1aKb8AJ9FPfhuJ9CQHdXWOiBHhcLNh/fB8GQtUnLECgYEArnAClVT/IjTwb6iCuSr8c2v1+hz0vB8ITMViXfWKK3dGKABiNTRW1DOgGjgOia1Hi11aKQlA3qiTk4fLbEDHN8JJoNpweHD+edjjJ4aONKzT9Wf/UMjScwzH27EQECYnNkmG3sm1T6L7GIGsv9+udNhUpSdOYejWIMA+Sjq3yC0CgYBn3xQ7E6YXvZTq/5rNuYS+dEixk8ncMmedLi2kgdhLQsaPulhGAOxLCBYv/ZoA9vugW+tfPiAhK21/9M9Zwyr39le6cECNj6jWVmXXeXLDDgPjNcVvb0lwiQFd66lgDN0JWjKUPiwSRZj+6O3F5a4qwpw2gBzJjx9kBQJkrG7GEQKBgQCtuQoVnjFUVBzKTJP9N+SDbS+hg8uyzoawfNPhs6AKdo2I9eveJBut2Fdi+ZUq52OApnwn7+YvLSUl+9eS7Aoq5vxvfsE7ooavtlOXkrIMkWPUZiUp5xLVy276R0AjVhWWoZ3+7mPOmUx8F6w3+lk9+g82bjxZoI065f9hml/DTQ==", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDNK8zVQEpDabjQnPpSAgU8O0yqtRHUiHPIFDB/MJ7aKlqXnOSIrM3ZmOBPl+WRRwOZnUJFV+Zkp1rYMF56oKRn2CJ0jrAYDdJ9At63ozjS+3ejg+F46zo3r6AEvcCKNIRFAf4IkI7iFLYL+FbiO0RVB3fmQePanSCTAz3WuNlmO93MgvEQB/HwAytMyuhX/aV0dYKUS2cHfJXUvw+6qnBnZ9nmW4acVUWf3+A+bVXrR2KX9YBG2p8XwljIkJexdilfiK+6PMae2OPvzgfd1J2fHCTLQBEyujNDu60egxnhVM5EjGwkwUqrFGWs6sXD7JO5lfRbjigZ8rxKInVMISL/AgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmcEhzB1bUTptZ8tMHGDscgAgRUok5gPZUiqHWrXy64dG4", - "privKey": "CAASpwkwggSjAgEAAoIBAQCtS3RmkSwMLNS7Kvn+8ztrn9olH3EzScK3r/5+PyJV4klYYLZ5RkrPWdbCB7gRmVkwc+GqwiMaHVDualDoQNdbguAX0r4nhxo3/46qDgUf7JpNAT0sjyyr69LzJj9/Qs+o0YGTX8W8rAX0UBikoFI7DjbFf9dD4a8cEKLiaqh5CGKS7zgYVCGv/ohp3MbZYXZGbV2FAVOvLQwY1edJP6s7kBHlQhinmkzW0bCdAgIUD5DZKiwsmPjkX5oBQw2sCUComKuAQGiacXcgJ0/ykqLkj4779KO10Qs2DwaqvEORx1W+sgtQ8KllbDqQHzCq2fXJY6359noSTAlgn+LI1b6XAgMBAAECggEBAJcBrUjDL/LcDfObG4WCRkEeZmT65RWgLMEL52Pzd+QG74rHm7pJ+l59Fpq1RzxuuD10fSzjRts2uJNIqX/5ILBpdwTLa0/edoZddt/Qn76V2k9HyRrPGEonkQa4SZSHj5S4G4Vka1ZhQD8InLC303AKjsfDAr3wJzr5dDaAYpYzvSe2KVZpQ06JPYxwiaS2iwxJX1f5vNyvVj3HZOShaw3evKsFilzRo5sZhmeMGa16F8koEckbbjtPmK/zQkrJ6Tayy/8FClffgMOEinrU9V/DKq57wJT+MlwrlIa9i+/N9Yx+X9qgSNfnMJHPLRxRnNELzl8TXGm0/Ey2UJfyDaECgYEA1zttzJUHIQX5bGNwvoFMs6arLs11OIZOJXAodfTSlZjJJqwFfpuxfqaUXzA7WCSKAMbUJsgD2BPU5yZMF8RxhWCCadweXZdELK5kXXip9ddhcw7Hi/eBC5DWk2in2QS1fwC1sC9+WHGpemUQJ8kQWVrSH6Vga9ez2VMjAI1gtrUCgYEAzh58/H/4zyPJWGQaD5m5tUC5AOW7h5hYsVhgHM2g/vlt4V8iAQNo/eY7xO5jNp1c+rpr2QbHxKMmgCYPSGVOO46JnKsafOxupW4rYsxkywLHGlK+dop6yORBygP5wkNdcpGklR1kvrBN7bC8Kal44YIFVrIbLVoqIvSbHAN3A5sCgYBNkjmsdjmviTuv+Nb1khxW00b3A02wJZecnqO2f5o2GG7G5VDFpM9/2gG3nOaGigTC6uYjZAseoWcmOANMvZw8eeAGzzKSgKYthFzf41E+LXYNxdHdfEKiLH1pe1qjOLNBJrxU14ktzylJ14rPDAQ8cCMzDKOHuqIzPWdsF4g30QKBgDTo4J6UXxMVFZ9J+uKcTG55kcPoNO5GriXAENPz+OrarlkW6YynCnF6g0c3BmLDnFWEOyD3u5n/Y2er3WpxDtb87Ng5l9APhQuULzDqVMlECkX4jYmyXHhrF3Q69wbl8fvx5PSeGflVGnv0TSjIpw4EKUiq6Y0Hwx87+QEE5q0XAoGAcDHscbuA/w4SCywZEbzrlnY/Za1VW9hwgiDbQFxQD85eSYLMt0D9u141l663JHAHEHAWcFv4LhdeCrdhvIv1+/6J1vndPjX2cJ8XCV76x3mnSSUvrwalMoMTOgERO5MLQ4B/qHn3mJIWEUMPSIZ4aJSqpS5nne2113TbW7ijQF0=", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtS3RmkSwMLNS7Kvn+8ztrn9olH3EzScK3r/5+PyJV4klYYLZ5RkrPWdbCB7gRmVkwc+GqwiMaHVDualDoQNdbguAX0r4nhxo3/46qDgUf7JpNAT0sjyyr69LzJj9/Qs+o0YGTX8W8rAX0UBikoFI7DjbFf9dD4a8cEKLiaqh5CGKS7zgYVCGv/ohp3MbZYXZGbV2FAVOvLQwY1edJP6s7kBHlQhinmkzW0bCdAgIUD5DZKiwsmPjkX5oBQw2sCUComKuAQGiacXcgJ0/ykqLkj4779KO10Qs2DwaqvEORx1W+sgtQ8KllbDqQHzCq2fXJY6359noSTAlgn+LI1b6XAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmXDuRHJvjK8WDmqwjnd9PSRi1aUTC1p9BryZ922wdhdBY", - "privKey": "CAASpgkwggSiAgEAAoIBAQDXo5iqDYnxNP3h40X+0fAw8b3bCEfYdtzrYCV6huKegNzxPkySFTh+9y6fcfi8Y9HYH4RkovmHqavlWlE999IreLQOzvsRVJ2pzOaSLURid2KjLO5hSi9CZTRPOMF70OGh/k2Egh7Y1buYVbDKGrgB+aHm76xxI03MQpvzhINtxrVlgzU4ErYEOZDFNXaccSH1UOfATb3FnAtD/3rhqldMZn9sAyULThoDYi1x7WxgX8j+DLx29cwOyoBBFtwS7/AmI0p3rYN3Y6otBNv0ycGnfg6dcnMdMDS+OcxBzqZph3o3ICKXtfI2+cP7zWl1QRBkblQdbHOYo8qevdw3wr4XAgMBAAECggEAB5FaPj2TZb+yWUccocDEaTNSsmkr/FDPmAMbzZ0GPwHOvzisf0P3Y51RKY9aZ2IpbyhMASwnDbfKrJXq2/3ihlwKFar17LnHfroOLXshN0NxVsCw7QEpf28F0vHu+GVwRbsjBU97vahimQoI1k7xvkAAipZGuwG+LTj5OCaiZivOkFmFwiZb8FSwGkDkGnWEObh2fWXbh/NNZU3JJ9CqU9RcHaguSonk0z5gOrQrcrbQrY3oNpgPssTVtwdPFxtGzf85D6tnhhmQsjS9E/dDyGYoNK+Vue7E0KoFGkJCXj9z/I8hPt4eDLh6HMA6Zqjxj1EcM9AhIqe8B2wYtAhCIQKBgQD9JFWEbvJ5PuNznOPmVG8ScBpQNbFulVnxOorg9S7dBSeS/TU3hFJZi0ojjtCKzIfDGumc+0Vf60M8FvXxbkM51R6DBaHtHwbIaX1luH6s05e5tpat9B/mc4xwQPaoFbDO/EvrdflBZRpUjXfM2Rgxa5rnzxgOKyN6Mck5aFZAowKBgQDaEt3DE3bSgnvovVsYlxOSDdGBEs718ZzGtrxz5u/i2WxXgXhzriRWYXoLjEBCUF017frbTyMIilBzX3DR4y/WrPP3NP+n/IbdyERQUEo4CY4cfOuHjS64UQo2kvIxBpz8tAbE7w2eQAFm1c9AIOhKPYmSC/SRo7iO2ZMekPx//QKBgGoxpOJyvKuac0ab6YtFnnboqlE9xRpz8xBck8g9cxRrRifGq12H2BgSc96o2dlwZf+2OYyOaJMNmd4Kb9CBhhgrzKoAYeacnnbSsjVLCXEtLrhM3bdJ81v021R4HEF1IAAlHSBBFHiXlk0kL76y0BBjaM+YNCo1dKOdYSIBIDXrAoGATqUtKtQTLxn1u9rGRpj9atfm7WiuEM6A3r06O4ZWjvYgd3Ju0TFFU4216QI8jm3TH8biiEMC/Gp9Vw5dbqRDNWWMWmPXq2qL7OHzmQ9LpOf1Q1rdyjXlWn2HdGUMSRf8d7opEs6vl5m3p7GGG7eCbnvA6FW9buSfg4z93LEnDrUCgYBNxymg6Mgnrw4XMDhPS0RR8NE1j1AWm3bhaMNrcgRnOCtmNcT5GGdUX626DJOk5xx7whfg6Ee4qb7Zj4hcVOSunOyjIOv5RRsWFC1Gw34n/WiyMdJF0afXZJKSRV31BZ4OP0R4EfVVLcfzKuScZKuxtuKjn6/2UVRfwjWAbz5WWA==", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDXo5iqDYnxNP3h40X+0fAw8b3bCEfYdtzrYCV6huKegNzxPkySFTh+9y6fcfi8Y9HYH4RkovmHqavlWlE999IreLQOzvsRVJ2pzOaSLURid2KjLO5hSi9CZTRPOMF70OGh/k2Egh7Y1buYVbDKGrgB+aHm76xxI03MQpvzhINtxrVlgzU4ErYEOZDFNXaccSH1UOfATb3FnAtD/3rhqldMZn9sAyULThoDYi1x7WxgX8j+DLx29cwOyoBBFtwS7/AmI0p3rYN3Y6otBNv0ycGnfg6dcnMdMDS+OcxBzqZph3o3ICKXtfI2+cP7zWl1QRBkblQdbHOYo8qevdw3wr4XAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmeHPhtUcxVo6eVciRKbDpS4x2KAGgsiyxA9Cim3k9gFzn", - "privKey": "CAASqQkwggSlAgEAAoIBAQCnch4Vp835CUp+5RNhMUbtgg5N37Ejekcd15fbmZkjSeCUjoE0dY6Lu4WN1z/z+F6V8TYd3ql4Nvx1qp9eB1urcJ0WXKQ/o/4dZ+iZqbonPAxyBl2OEzqQ2csbAiSkrio0ciSylMNAIF2Wv18gRwHw9YZWa+sOM55HNPiLK/qav2HEvaSUfypjWrwdRhrYOho0+pRrcHoU/neI8mFUkQQDMvArnWpeaN/dgPDsyO6RRaXyyi3A0ULK6FFjMyiwZEqQBQlRdrc61lC2o7b4ySpzfgzk0j1zwROSjZaUfH5IJn1FZVgAl9Z+S8nLgTvV/xrBjABee1gYsD0DVFleEgUrAgMBAAECggEBAIsRTkcyDPFOdB6b5tKL+Jp9r5+hrx8GCVaRnj/2e6dBTlJTYJ/PGsqWvb8mDKl1mCj0IrwAF8QN9vNK9/1CIzJp3y2ZV5i7fOuzRw2IV2EKkFOLUdwTwEpZeERALWrQc6EHQ89FmjwCJXh0DG9kSgp0AFR6YMh0unntVpdPuV0XSfxI1cEKoBRjcXweewxXNmQc3a4BTI8n860sr83jF2f3aoMihVnOCPDTk7NPDF3OUI5lXV6xUCpbAtdN7u/aG1st3exS34cLl8lMk/a9+s7GMBwBseJiBzENkF4i6cEkz2c/oR2VMvFd7nnIDGcgqWHM93ghQMYa7NBaljr809ECgYEA0/5ju1fU7Rwn6+5Sov90I4zgIhS1b/CfYow055U2s12RX5iAzhW4etYIX8lNFj+lHQMj6zHSdWJXtGxxSrqBQXLkOmWrfc9ZvG1aonYhMAasydNRcsajr5WYkfWbS1FgnUo6vBeOBdGzFi7yUYtgLooNigdg0Ob7m4HBEUQRPnUCgYEAyjRlGGbyHoEixo2Zpg6zMaYCVquB65P1kIJXDVpQAjz6pI035/VUsiF/HIfPkKfQ/IokuR5qCIfFN++OwCSFDub1lttikUJFakgeCgGlLVuu8VCc3IeQbPT3pSxqF/BHngWcvykTq7aNzz1yJiY//TjAucLthdVOYu/eyRYpAR8CgYEApWac76Wmtt059KV8ijpfpgEbOtwHd/A4mw4jlPBhvm5ppzl4fdKKniRyYjHQWGSN8eXqV24G85koLthRSGndwW/fzARZWg62yAJWLd2XJT5///RFXxTGz48be/4yDQDQLcilrO1/3OBxJwS4AZGKGKWTzLbW/gbKFtmVBmCiR6UCgYANSqxqkjnQL4TtsFktRUIaPWNh9xwvNCasPSUjx5AC1adUMcQ/By1uGC2W3oaSZ7WhJCON16X4sZQRPToQ/1WPyTbTl9A+5DBT8DGpTrpg5On3CumExZSE1QWCYg0HTdAnXw8SscyNOQ7RVKSwRUtnhdeFXn7mkUL51fK7HS3M2QKBgQCD8OOaClhJNMSvkHInrcKImhl96p+VgCw8GMoLxPHV8MnUGwuNYNWWatmoLQps8OavZTMWgqnbjusvBJMFlKSZ93ULaUnMaLFekwePiaPlLnE7DCpmBjlKbryLzBgrdOLW0R7PwFW88fDJ2q1IqZZmD01U6LnyfF+tRhj5ysdY5w==", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCnch4Vp835CUp+5RNhMUbtgg5N37Ejekcd15fbmZkjSeCUjoE0dY6Lu4WN1z/z+F6V8TYd3ql4Nvx1qp9eB1urcJ0WXKQ/o/4dZ+iZqbonPAxyBl2OEzqQ2csbAiSkrio0ciSylMNAIF2Wv18gRwHw9YZWa+sOM55HNPiLK/qav2HEvaSUfypjWrwdRhrYOho0+pRrcHoU/neI8mFUkQQDMvArnWpeaN/dgPDsyO6RRaXyyi3A0ULK6FFjMyiwZEqQBQlRdrc61lC2o7b4ySpzfgzk0j1zwROSjZaUfH5IJn1FZVgAl9Z+S8nLgTvV/xrBjABee1gYsD0DVFleEgUrAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmTd3oibLxr8XKqj7DDKcm28a6bqnMLJu4tc4xf8yAm5xJ", - "privKey": "CAASqAkwggSkAgEAAoIBAQDYsSTm+HDwoD4Th72iDePNdWl/RMPTFPLX4Uj1GmY/4l5B776+pSYGcY16Ak8rlBURz4NIntiqQcW31eTMdRKusDCgBqNzY3as4qFtsTig7D8baqFYwoswSj8awe4y1Cq6N6R1/w54r9pEap1RzA1+MPof5+WjGA972h4BQaXoE5xdidm2PJxyk1ZTK9hKqZI33JII1JQ2Zdr3VQzMeH2Mh50U+OsJU0rtGNUqSOkvfCy67a0pNJHssGeKUWI77go1t7gWy5F9s85zGEJKd5J6lh1bpam3cDX0hX+jfxKtwHut/kCtEEGxBSxO6n7EHHhdLJylP26skd+YhAk1VfHdAgMBAAECggEBALsZgV55B7OM+OyOGPvy+E4v4e6E5ny8qs4h9IfFyqHAiFhwdIdSO5n2tAy0L73V97dQMPAkT7n6XojUA+FR+NaixOl3sevw5shySqZXDilMs1St5jCokdwZT5F//3cd4OK3JqbHmqw0UsceM0YsZT4fdejUp2ACZ2QuOhgloeXWaPKb+CcukkQywFa/mtCkbpD8yjUMmaTXg6Roq6tBcr2qk90ICgHxp9qG3udkWRFd0FWoWxXNKn8AuxeJix14S7LBSgfzDck311M41wNOb/wMzy2Z4tWerxlhd9YZ564fC7+xudAxN6KcNlN7D0Sy7jckfjRSI2015a0uPZ62lqECgYEA/zozaRWhlk6Q51Dr0hHcm9EJpktAqhGcbd8BvztoMKIJJhcPa0bmWaKDvOV00F8n/Ps9c7AsoD8iSyfl9Hhznjn1QXEiuwXJ4wyYmby8a2wWCs0JggMrSgdoZiOtqGhFem1Dkmy9mLvy5pZK6w7CzeiRu22Xecc9aVH7pEQPYAUCgYEA2VkUJpdfkUS8cLZK0hBANxWB/poTAkp1H6T1ECPYNRHGRskpoZcPWKVfFdMgIBSkU32nIL1D95zY0LzAjK47SwVzOfhUsmt+FTTZ+YFseWeqNUNuA0Uid9ARJW0weFPPK4yJLmO9v6C9c7fZFllob+ajcS+MKYpE7VVeDO2Z6fkCgYBslWxN5uAKPH61it3pT6QVvodmclmegUOWEuyBWVroZeeShvkOYOmbdOKrOMvL4s/2d0UbtPYnbvS+GMliiuRVir7nCqUGAF519GPv9DYNVbzC95x17bc7FY+69K7rGQGGJno7D3xSQJQEuihBfNQwGiP2I5fwPW3JIxH2PuZzqQKBgAQ9383NAHl2TPMqK5Wj6Yzpp4rPePV/fH+smXfCK1MF0MfK3zwfFZaWS5/CagsWPArBFgTmjLAFaJnSRTO5psCVD6We+hAtVt2VFXfwFazc4A6ADWKU89JAxkTjt6FxiUaBTKASJD7cJTZf7SWpgwdECgaIdgTNhQDYvKgl7u4JAoGBAOKu34WNvGDTX9BB+NeMQjrmkjVCnaoqRsg7yurUhzUlTbMNA3S2o3ZoUrbXUh2quYZlTDAqQjKpeCOLMpZn63fIA7Z0Pp/wfTry+QFL8cPs+4q0d+SJXFI7s/DcgUgVIbndtGDKSeougb1VNx7v4FILW+/tqUqcN3oEblmnFjuI", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDYsSTm+HDwoD4Th72iDePNdWl/RMPTFPLX4Uj1GmY/4l5B776+pSYGcY16Ak8rlBURz4NIntiqQcW31eTMdRKusDCgBqNzY3as4qFtsTig7D8baqFYwoswSj8awe4y1Cq6N6R1/w54r9pEap1RzA1+MPof5+WjGA972h4BQaXoE5xdidm2PJxyk1ZTK9hKqZI33JII1JQ2Zdr3VQzMeH2Mh50U+OsJU0rtGNUqSOkvfCy67a0pNJHssGeKUWI77go1t7gWy5F9s85zGEJKd5J6lh1bpam3cDX0hX+jfxKtwHut/kCtEEGxBSxO6n7EHHhdLJylP26skd+YhAk1VfHdAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmNNQLdERMKBZNs2kPd7ejWVVYLpBwb8UMqAYUNSQVE6p7", - "privKey": "CAASpgkwggSiAgEAAoIBAQDF0/EXsnhPXtfFZVyibJ0t0g11Z/0D8DXgQYriyYOhYWlBho562/Ga0ETEcwoyuY8mtsJ/8//3gAURAcsyfYgx5R0AFC+HV+ar0Ft+fNhxO2QStYF6lB2fYc9bFnxJ+fT/4ZS9BKao5ioGIRNA/zVCk9alc/dhQIA2Lj5rwYBw151ucKwuMNpLg/mh4qcsLLCEaJ5BITxTaKlpHguWn1b93nhmp4Th52mZUv6yHKD/G4gf9W4Kb/GJEYYb1a6ctw7h4Z0HTE/unesgWCROd8crzDx6X1HRKnR7wbudulfBNJXUtBKJUlabe30i+LNXEPwAGJQ2e8cAkKbDWslZZtdFAgMBAAECggEAEcOnaaZYEWCF5a7lc5xnPN8Y4EsXOExQujOIgjbwQAScTAsGLlgjyPAczLs71jQ9e497xbumZ5YyXkWX9o+5NCnLwd8OKYwmJZWPMbuKQBjCMr/jwZsdUduZoCdTv9zXOEcMcTDCunX4nhZIQVTpdnIKG09fjncZTEQ4zLpSi09YqjfVBfxACUrMhmcpERSAGam1UB7bBEze0Ddv+l9HZyRXW0+1elX5YFpRXW4VaaXjtDI4jZx3iGUsimvcIlWRiOxOzPCE4Plp/thQQ0EAo+8DcU3RKCoHoX9UnbEwr7mRxE24en2JQGX/R6z9nialaQHAmIXtI4Nkqb/SZL/FgQKBgQD6IZ7qOqrHGdwwDYHngxU8hPotcFD5nbmS84t7VIuIJ/lmbvFreFtEjV0DZMgEMlDlctivurRVKltwLrOsV9Fpt1+VVwpvleMhjwHNLldP64PBsKfhB6wgTQLxK5dAgNbMM4NOUUan/IzIro9xio6vXewNRVVczd5tpH8uQe9/5QKBgQDKeCrsSP17kkbrgEtemcE6ONw8n2PDuaW67jtWMX4W2pqeiFjuNuSuO5ttERAbLi0EoqzqKszUql8LSUr2TE4Ya7jPII/AARtnM7X+OrzPmm5aJhmk/QiJ4ff9KpZZuUlSLtmlBRsi0DUlZWRJA4aJuSKVMrnaPGrJqo9jCczD4QKBgCS6QRJVkPPxOSKZKSTsW3bqc62uW0V7wl7wgd+XF3HjpLxEuBA2uPgE5c50wuXS2YwHZAfRm18R/CEpyloY/vfN5CwSfsbJtHMeA360Oj/S7iLHpK7nKIAJrs/ovanMAT40pigeyQgrjiR9dTSPysm3OcztDE63L9zblY0eQ2N9AoGAOfwaRttMhSRKXU27yBb+qL76C/6V4sr7NMLfiXrZIpBusbJYzbg429FEXQMC+tXJnMc+AD5LtSgp2iCecFVAFGxdXCx2HsXyZCcCGxIVWtteeUDqHT8+P8bQb9fPgVi4L+os+L6ym9DHN7OG+gYhdLXpupLxeRfOeXz4XaPD2eECgYBvLlpviAeCe0ZpcNEu/7LHogxfE7q0ijHY6gJUXFcFVlHmRUFdqpM63XzVEzzfTBJ3CWIHVwybwPQJF5CIaloOXP6Ac0MTY2x7mJ+rIqnqF2qgY8vf8I2beM3a+A7zJGiERtiIYXQV1jzcgnT/FI8rcq8/v2jSB3U3kzd7P6JLSA==", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDF0/EXsnhPXtfFZVyibJ0t0g11Z/0D8DXgQYriyYOhYWlBho562/Ga0ETEcwoyuY8mtsJ/8//3gAURAcsyfYgx5R0AFC+HV+ar0Ft+fNhxO2QStYF6lB2fYc9bFnxJ+fT/4ZS9BKao5ioGIRNA/zVCk9alc/dhQIA2Lj5rwYBw151ucKwuMNpLg/mh4qcsLLCEaJ5BITxTaKlpHguWn1b93nhmp4Th52mZUv6yHKD/G4gf9W4Kb/GJEYYb1a6ctw7h4Z0HTE/unesgWCROd8crzDx6X1HRKnR7wbudulfBNJXUtBKJUlabe30i+LNXEPwAGJQ2e8cAkKbDWslZZtdFAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmbhNX2z766ycmLDP8awr5BLB2FbqX8FyvUt6fBLRRWsjH", - "privKey": "CAASpwkwggSjAgEAAoIBAQCgRIZTcbtfOQI4aEpjVgyVx6+hR5z0TurjppSOv3p6e/Q5uBcXHBx0LjP7bLW93oAcgwMAPZwXBHOHIXq//XtBdzIAYDzUUztfCk76AXTWr5MR3jafXqf5lwyu5XLKSa9PiZHDwSEQDY05N4WeCS3AMyprcu1Z4ey5W3nGMy/ClPicyLPhCgyWONhk+ig4bVNfByP8AUcTHAzN2hgfhY9t6f5aqvXodybvG440GMt9kMH5sSYdZrqPL3Ixpmz7mOxkfbc6gaXh0M/pTD+q/f5JTrYZ7NbF0jdrLWcx/wAgJA9iLyrHG93ObDvn1L41k5H5l+QBqTNQzfz9mnv1Sx8xAgMBAAECggEAORiwkkHOcxooRFhDSCh7y1CcrWSJ8i+7Vucdvc1RoRlP5NBEyaLmMC3VrxkHlmESWxYBl7BbT4fycI3o4UU5CBWi5qdihHIykKVnhYHHUkSyrIbyBsz+ItlBV32+63pczoVAPPEtCj8JtPymyaqTdgnEbws+q+rlHxQLyiSqOzOu/eJJKFBaW9/C6Trh7EOxEwZYfIoHQuihb/zdrZtlsU36yotKxckaZSv44IXTv5cGT3vO3U3s/t0rPH8MJdz1wONr7iowqEdUp5GZivFg1VwUJoHvutBjIj/eiJGebQ/jlKBOg4HltjbHf3YAniK7Dty435eTRaQvIfS0zgZ7kQKBgQDSyytoU8ssoIP/CyIwbRGpQfnqHPeUcBXXntS3P/gbXV77iDdvQz3YCI0wmbDPzMVxynWbQuquPZVxhKInj58SF6DW9qHV7AuDScVY2fK37YbrEg9UdIX0WJAYbtiLeA3ejZ8BqOUeh3zNPrvx3z+QLiDIEFXU3iIigsIxFIUZYwKBgQDCo2r03ArJMeaCgD5i0U4ZAD5Dy6AAeYke2mS1L3NHGjIFlFNGmcc7LvChGGzCv0fktFzZTbD1reY0f3eF+JKz3DjhprUQRdsWKdSxnFagJmUjujal7BJvhBYg1oiqRAVXmGQXtVgylDU8VKdK3dAoBYkufcEoiG5P3VogTkBTWwKBgGm1CvqRcsTZZfgjPCzutTmc5Vfa2OkuYDW159RRlvkaFMSspaf9H2lTuIITwJAkjysmLV4D664fIe9AZRTTuCCZisXh/nxJl+hpuTZ6bXaA/fSqJNfkazyCoRgvlhYyyTm+6WsqqGNr7FD80cFUhAqopzXMw04xawrFad60/J4jAoGAIpQbvVKWS/YkiIy2CKI8qK5lYW/8hfkRhjywZYv/g+NAfcNDJCjPv1DwiP4o3FRVNmlgkW5/ALabTjpTBqcJkRCPvm76feCbMo3N7pviu+L2VumPKd0NzWf+8miKsQ0SkeRN6/RYreuspYI4klFj2KhbHbpTpZrPVjrx9wlP3j8CgYEAnz/M0t8/PcYvjwRS5Naned4gEMLFIPkwJdkQnqAVw6W0s2ajeUTEcCRYKT6fVUqyAj3unH9R1k7YT0kzjFLNypYuQjVbQJtVOSHnssbulKKlWcs9dwW3NUTxvf6XdOP+6ywZodwGLSZpQJqMpwEvS/qN7uHlkAQY0gPGLtOC0MI=", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCgRIZTcbtfOQI4aEpjVgyVx6+hR5z0TurjppSOv3p6e/Q5uBcXHBx0LjP7bLW93oAcgwMAPZwXBHOHIXq//XtBdzIAYDzUUztfCk76AXTWr5MR3jafXqf5lwyu5XLKSa9PiZHDwSEQDY05N4WeCS3AMyprcu1Z4ey5W3nGMy/ClPicyLPhCgyWONhk+ig4bVNfByP8AUcTHAzN2hgfhY9t6f5aqvXodybvG440GMt9kMH5sSYdZrqPL3Ixpmz7mOxkfbc6gaXh0M/pTD+q/f5JTrYZ7NbF0jdrLWcx/wAgJA9iLyrHG93ObDvn1L41k5H5l+QBqTNQzfz9mnv1Sx8xAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmUQUxKeaJrVqEp89fpG38FKJKh8sFpyehCKosevfnE9bJ", - "privKey": "CAASqAkwggSkAgEAAoIBAQCdIzV6DUcwSmKGhCT1a2vs2zlhDs/0I0JR0OGTaNgDYeFHGM6Hw1a3QBoxvP+odOncCOkCbGLIEMfj4Yca9RkDvuF64wZWqx/tVQYvXJ5MYx25gfB2XQq5FMXxqaASP66FZN15kc8i8Gkzxt9Ff+My4vk7ve0QzXokSOcn46LAjyGU1D+abAS5RnAyBwsTEoXiWl2yTR7iunKEwbtJBIuCKtCYThRnbvTmr3wX4Ywn8twbJEjg+esR+MTrGerv7dqPtbB6/q8QPh0CCT26yRTpzOjielqqYs8ZOTBwe/+a2Wjnjr+PG6zPWZM1xl4DtxmTImsLUqRe3+48NFhLv3h/AgMBAAECggEAJ3V0804sRzMWpKLASSSNeG/ga7/1dl/4QmVKj+KvA8JreJgBHNRvjRq6uSy1ok6hfxB5upMPByA3ocC7VYignHEtW9dwewkDvmwwXmpKkfH9v9yiToa0r59IyZOHz61QHM0kVGfJ9QMb19WjsWcY3Wljnp3lzudaOYxZB4pBD0s9LzrSfiJ2HI1skgxIeMLtU1vvVhz3KUYar/xCjfuqqeVP2RKcDNbnlECokndpzVhqyQCHvcdNitpkkGmG7cwTHReVlZnrFcZeoH4pPKRdebZAxBZXg7jtHNBwZK+8X4hVxZgVRE9b5q1RQvFl75q43aB+CgiXg1cvmaG1tZCxAQKBgQDJk3YH3+G5PnAxXO0tejc+hY3mobFyNv5LYtx+fnY1Osu5rmy4iz2XVaA0xlegQwhxjPHDTd/ePVDAOaAANLa+iFT4Sv+dwlBNICfIXtM6eNhDjECY4kfZ6SDS6CsLEGsTV9d33ybq1vSlrYYWeGbShURIzpGKbTSKDynjgbPrgQKBgQDHkD84Mi4DwJiIuuxPesf9BHQw+F4dycDZ7ZkZed19aH1pWk9J+sqlDuLOvpGeCq52av1rK/t+Zn3vBvgJgs9upHq7zx+NgX0uvjr4V1i1Nxu23N++DBuhFRD9HcnyS+rOXjRVmoK3NId2YxwBZ0629NVBssP4fBqxdZ5uo/Vj/wKBgQCR9DjpWL0bIU+hHnUJkc3AcnmdvgQ6/ADC2xFmcfDrd+gdSWOleASfuDspG1hFTWQmu/QuAwwO4fy/QrpMi96qNRK5Oay+MP1t6tODbM2rL+b/eeUoDegSq4+9xqer+jZdqiP0wtpt/jjkYbGOQZ3J3v7jbNbLEWmScYpWFgsNgQKBgQCMGL/I+7FCARsUIeVzhoaPIWlQV4v67X/tfddVAzByscAZDcVL8jwA1Ap1iWNAx87iYwm1CxNrERinjQTj6GknC2D+J9HGzXjML8/GN8uWrDFQlo6cJHPhCaD7kMYMyy7z4T5sOiQ56S6P9dPbSGMCHa74iD77WmSC4Edw9Ll4kQKBgDGjPA5lIztQGNDZwdadsf34AQtN1k+JYziBj++alVdk57CT26Il7w0X/qPLtEzqigZgjWW6EfFV7f/qKpSjl/jcpzJFeUC1/mwOfTYa9HoX10gV7knHZP/vKL6+Q8woHE8rjUPswirX9jxU1N5iYlaIBkGbfDbrIBO2GbGQW91H", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCdIzV6DUcwSmKGhCT1a2vs2zlhDs/0I0JR0OGTaNgDYeFHGM6Hw1a3QBoxvP+odOncCOkCbGLIEMfj4Yca9RkDvuF64wZWqx/tVQYvXJ5MYx25gfB2XQq5FMXxqaASP66FZN15kc8i8Gkzxt9Ff+My4vk7ve0QzXokSOcn46LAjyGU1D+abAS5RnAyBwsTEoXiWl2yTR7iunKEwbtJBIuCKtCYThRnbvTmr3wX4Ywn8twbJEjg+esR+MTrGerv7dqPtbB6/q8QPh0CCT26yRTpzOjielqqYs8ZOTBwe/+a2Wjnjr+PG6zPWZM1xl4DtxmTImsLUqRe3+48NFhLv3h/AgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmT3LdBHJFQUcNfCD4u4ecknh28k31EkrCBAnaSRg7ngXc", - "privKey": "CAASpwkwggSjAgEAAoIBAQC9YSjJQC8rMGegKO9Wn2sZnJt/BVDAv/druvl2uB6mgKc7GBLQNyTOw2DFsktbovXDMSMhYx9FvRa2rAShGzzeWmO9HnyXqu5mzL7r6oVhtb5FTHG9j6PetN1HJEiUuI7xSEjh54DI9GWfsnwhML3PXT1+TsDaz2BTBoNWW5NLKFZCqitIxMHWk8R1ooBz0gicqvRv50isfrIDAL9teiBNcfbUNSiXJ3F8ueq3NPuUKCVaCPoFrCsNlK0h4qGEV0jb50f0CkK4sKzeDcg/UD/S+jGrkNDAaA3f7cuks7JeVuL/gJpCaRQlBBjG4lt/FK0bmrPSm2AOL5wOUdGGUgbZAgMBAAECggEBAKJ9wykq0U4VclSRywpgLt0C6sjKHsfD7t+YxoN+542lxdeGiF3vcr2WFmqK2O3/nS+l8aasDiEgZWTHpBE39bozhHC4v97C41uBQi/aQifccS20scMchFaKiXKJR12UHdIZW6+5m17RlIC5/Jfd4n8SWbkOiZs1ZEjYxchLOs64i02bumZuj8NT8OMFEyfR7fl4TG08HSToCUvRNyGAz8T7p/fy64/Z2lw+xP24XCW/YHe1gD5Bu/QmOys1mrkT14w8SUhIDW1z/Lr1+mnfe78prPRxe0Z7BR2aidHVAu7h65LS0XuFvi9uOl3qrzTrrLTwXyTmUhJuqN5YnJVej6ECgYEA5mmujbZ+n9qKuOq6GVkSf00LqmUd8oVbEcI/d3lT/u/omapmKPrfPzz+VFrh0S7I8rCYlLUmoDnkBNGzF68llt0g3cYTIO8MnW3iySbiCv+mTCYCfzlCxPX4N+zW6Az1ofCds1SDMAZL3qZEowwy2iresS8KmEqj3gUYKgiFNcsCgYEA0mj1K0YdPZ59klb7PZAScIeScT3rH7xnRGH+2tNMQcDW/W8H6GXhqZJBrqH5Fs9y6Ndyi3dWQiQiEvuBIT0uaiMa9UkFDhhyz05uIajBqBpJY4JJAu67whUL3ng9uFhHF875wSsygxvJMNuTf9pgThyibKvq2l8O6gJfFMx0QWsCgYBKIxD+Gg0uJCRkkWolw8o22bR6NCTpps0Brs27BHfpXIor/271mpsAfwCaZc+o/fO8WuQNXSg7f8UFY+/LHBjtLONpWFVJUIFvmi7RaEhtH4sDj2tYQjVgqIAghn0zlw/l9kTXscawSiZZUohdKgymtAqJWkh/bezCAEOhKrKp9wKBgEYgy04QAWDvOSUULoq3QR4WYX2yyHH8ZmLJUpr2f90Oe9leL0GK62qMH64nuBCdNcxbOoc3UB2dU2oGP2SnspeXeb21B6VKCsIDfvti9qCjmkA7RUBf915Zi2oro06UxaUuy9lRH3XJRgYtuPyM+TovmwcjSZRcyGjAP5Z8CmdfAoGAUBDcDoxjm4qn21HCt0Kb83fxhyd0KAeCZx1DkA8ENu+sGaq0Var+iXdJwIzH84HfPzNVRylSfis6p3tkKtuhiVLxE5aNauPPD5oKwf5/nr06CgiMG8ENz7vdih0LilThK/cSGm9M57oITBkrw6fTEMSqt/18dRuvH7hD2Arr0l4=", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9YSjJQC8rMGegKO9Wn2sZnJt/BVDAv/druvl2uB6mgKc7GBLQNyTOw2DFsktbovXDMSMhYx9FvRa2rAShGzzeWmO9HnyXqu5mzL7r6oVhtb5FTHG9j6PetN1HJEiUuI7xSEjh54DI9GWfsnwhML3PXT1+TsDaz2BTBoNWW5NLKFZCqitIxMHWk8R1ooBz0gicqvRv50isfrIDAL9teiBNcfbUNSiXJ3F8ueq3NPuUKCVaCPoFrCsNlK0h4qGEV0jb50f0CkK4sKzeDcg/UD/S+jGrkNDAaA3f7cuks7JeVuL/gJpCaRQlBBjG4lt/FK0bmrPSm2AOL5wOUdGGUgbZAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmSeTfyubWsRZRcVczDxzBxhDJBgj7MhgHYbwv7zU3ib4c", - "privKey": "CAASqAkwggSkAgEAAoIBAQCwZ/HZRcMVjQ88dKCc5YnKARHUGBLt4sj1vCVofk6fotl5qojz/pNN+lCKtxi8nmVt4GQDVQC/xl/uB3iHfFkc6qho1XzaaRxI8XaBSJV9z3BOEGmrXBkISluDnJAXhfaPquZJZUhWoXjO9H+54G0Sd5sXeLDlzXSUGt9tMGE8tWggthN3TWR0G3jKw8oacoQZKOzJneuksyf+RZGeIY85THzaT8tBBCF3s/TPK0SkLbHyy/Yet8Y+8FSyz2sQPLLpeCRKnIZ+opxvtE7M7/mwD6NXVJ155eNLrFLBQOWOYU2bI451YQXCErJP98JwYeKqaYCETIw+jWY0ekc47edLAgMBAAECggEAYHnYmN1AXg7xYDzggi4+900ydO5dm+BFy68EPmulkES9735GvDpkUWcumU6dprpx+m+YAwKAEGHroQBQ+LgW/GuRgxQO3lxR7cqw5u/NYisK3oa3Y9JQlmokNoxveY34VIZAv682qrpQmc658+w7ergTB/knteZxdXZk7xBgfZRINv65p9ArT0rvsEOk3Ff6uq8g++zXN711b9xFMBmxJT1haHGT7fX7JuyUExCSqTElXjSMFykk40zOxjMzDQpzjxcSihffhTRyhScIjpru372BrCmL1WCeDrvN8pEV5DIJoYhgvjGLI1RuPGaGwqIGiR+OwXjX2kcyRgkXnVPPkQKBgQDbjljw1em8741Mkq8N+W/voAJZ59O7bUALSaxjrU8Hpljgd6uu3MI9YVTGp4gjI63XIHo5mKlONXDyer047E/58KNNjl8v/eyLLDUi/9lERzD6TPBsRspwgs172Yljm3UpmiWDfveiu+7/qVbrSK6b+4V4aAnRH7kSyUU29qSgrQKBgQDNsAM4qw+48wEscGWbqM+PlUOwVHhpzyo42gM8y1xtYhRC9oaYeUVpUPwXzS419apreart6KC/AFt/2mBh75pEBk/Vp9Z5l9a13yJV4xdTnDxRAUh+Zywqb6mCTqWFBnqeoPDFKZBcYBDqkgYkCuHLMjYZcebP9EC66yIcJBeO1wKBgA9kzKGeLfQ8S4jp4/Iz4gBIFMIe+f5zK4FfGgInHZpotGSQn230Nn49O8dt6aKlFsQ1l7xAEubT4mZt6qR6FSVuFNUUPWJNCG+9msAodiBOaYWzLUw6LmlzElszpmlgdfeDwkuU9GHpkVlFkz2N7Agtu270xHNwKPbDO+Idqu9FAoGBAIgZ0Hfd0QB7YyppkQJH2FfU175ElozE9NY7g+rlUVpbjLamc3dOv1wppzWEofA4hzSohC76P+tCrEjUUfRb3ALo/kiMz0ET9JHRfOHB6zx64/ph0/s3/6Rw0IQV0DZOjDKMoeSEVS6arnbYetG8lZ2jsuJxWN3/bBmC3sYqJ6BvAoGBAJT5hMfOTN+y2+DbT0o3E+bCbGGVJaQA7BBD53xoyvO414/weVqJ6JexW6hYAiTJ/VeA4Pbs7nvxZB9Ych9jj98Gmm07LRs59YcuICxsy7IidFwvUyTV9i8dFxmc02kXTqJZXihwNbPz/bKCO8DpMRPjk7Q5cT1uocsfZyEwyguY", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCwZ/HZRcMVjQ88dKCc5YnKARHUGBLt4sj1vCVofk6fotl5qojz/pNN+lCKtxi8nmVt4GQDVQC/xl/uB3iHfFkc6qho1XzaaRxI8XaBSJV9z3BOEGmrXBkISluDnJAXhfaPquZJZUhWoXjO9H+54G0Sd5sXeLDlzXSUGt9tMGE8tWggthN3TWR0G3jKw8oacoQZKOzJneuksyf+RZGeIY85THzaT8tBBCF3s/TPK0SkLbHyy/Yet8Y+8FSyz2sQPLLpeCRKnIZ+opxvtE7M7/mwD6NXVJ155eNLrFLBQOWOYU2bI451YQXCErJP98JwYeKqaYCETIw+jWY0ekc47edLAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmV3Gf2G79yWWGLnKQHuDuRTQZbvbnhb6NSKg6yGTKdibp", - "privKey": "CAASqAkwggSkAgEAAoIBAQC2qI/XfXskB0F8LzOEQS9Yx25lkj5thj+ddMMsOsKV5IDfFNKyd/t8T6LhC90Jhxsfm6BpN3cSSuR7i9pHBVHdoPB2n1LSTyHYi95ZzAm3SRy6SA6G3n+kXtQHaTgqZy38qEQozAeLUOoDFU4SGW0ai1cv14TojXnsPNphim3DwHRceouOmWkaIA7zRCdgNkB6Wfx9D5AVw5RravHrSsdn+jaBAXLjCqa0zoLJWPuvKlRD948JHyTG4ozvC260arxvqOtSlBDixnbaKAnycna7j6Xme2gJ7/t/cDSbWtBJGOYczst5u2W0zxfpqLeT+//BPoz5qhjyTrz1EHQTPm2RAgMBAAECggEBAIhjxUR7BgAZCuTXuff/VINOJzjgwoy1ubqw/SuBlNqoDTKGMe3heX+RV2YDncEHiVFIu7bVG6wlEAbQnuR5LG/5RJTO0uEHBZbUmesjV/3sMe9G7tH2QglSZbBC+RVwhf4rBvoPn3J/sL0so2cQZU90zF2E6FFdkrS7m7VJ0Dxhq4wAl8Qveda8hwvi2FAiTLL+8l2sf457pt2XF+jXH7qb+3uC7YuFCGKohPXK6jmsNPlvMZC/LIvyUNV4unla13t9VrUl5BPwUaPd6UI0MwiSDVwwPRK0m/pQVw46CKQNiX2lGoWFKyJjqa/OFtkKQ8g7CD/jPpbTiUl5JzCwYCECgYEA8dRJEmvrHZZiUGxwCxWc64BXVvDqOmakFxNUjxITGb5X4X5tWEYmGODEEoxmujfVbM9+xE43I2AaeowKBZssm8ebi0+rIP/SZJcMHXLUfDOj31BF2TJ7r6mAQBQ3nBPY0NpW0aOja6oQiGznxe2tPcPW4jKkKXvE2WSUfHXTIAMCgYEAwVyloXRV1UvLpnXegmQn7vWDZJI4WW9eJ2BicRP7E2RviRMo4JMAqDzUdANxu4UxQScxoVcWuZHPVC1NlDGdH76VoQi+tBKObc8vcPR6K0MM2Tgu+xf6pLthp/LlpcztJeZHeGD0nAxB3cWIc+SowM4UAVnbzMIRW9EWuisrWdsCgYBcwTDZ2PzQV2sUL9N13O9YQNy/Ix6kEdRkaWyoh6U93Y01l1l3X0ijiCqMdr+8M0gwORIFV368mdLuKCJ77f3ZLmGRuJgJyzW2kVz7Op0XmnMDZ3WzDjL0uI3Rhi+iNNaXnPdp51r6I7u9qA/qEfS92Qzlq8jdhHSHcZWme0bkYwKBgHFNWniK9Kixazm1I5cAHS42irFpxL8TNPaZ0dU0whCQ75JAudkuClqKmmsIgaJB36Sv1LMXludR+0z15tmJYOpzALaFq0lU/kR1/PSRLO0gsuytsUnMuT/B1O1WtR48QFHO594v4eV2gTn0P4q5V/DyUGKiRttqdEV69XhNR2+1AoGBAMgpdNgV3hogNUHJqPvT/MuGo5O74NK8xp6j0FrBlZUlE+8JSB8pxI6IREBifOkDZ+cy7Lebi1REPwZ5QVVtzRpKppgKJO0ybd3XFI2iEBMebDQMMZP/7Lq8KO/FHrNgiQGQfhnf4vBZWwj4ZHcI8+UeXcctTSwRYFqsFl3Vbyif", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC2qI/XfXskB0F8LzOEQS9Yx25lkj5thj+ddMMsOsKV5IDfFNKyd/t8T6LhC90Jhxsfm6BpN3cSSuR7i9pHBVHdoPB2n1LSTyHYi95ZzAm3SRy6SA6G3n+kXtQHaTgqZy38qEQozAeLUOoDFU4SGW0ai1cv14TojXnsPNphim3DwHRceouOmWkaIA7zRCdgNkB6Wfx9D5AVw5RravHrSsdn+jaBAXLjCqa0zoLJWPuvKlRD948JHyTG4ozvC260arxvqOtSlBDixnbaKAnycna7j6Xme2gJ7/t/cDSbWtBJGOYczst5u2W0zxfpqLeT+//BPoz5qhjyTrz1EHQTPm2RAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmeecBL9V5NybZhaKKMcSxQCaH553JCup6uf1oqhQziL1F", - "privKey": "CAASqgkwggSmAgEAAoIBAQDVHmqxlszl0nTg0jJTgt+7ubcx8TSwn1UGdvgkcb6Wi05UkcW1qqUBOaeQR1P3xe0rCLRHEdWBmG/Ap2GgOXh2xqZYFfb/LUU5+B3BKVIFpUk4WyK1T7BYWr10zDeWxbi6VGAOk4n5bZ+DGSjA8utJpIAVeQyePSJkKfrs33pes2cR6keshYbDuKk1cXROVaUacEoHTL9iSly4LBJ7bWaG14g4tPGBOATiRtkB6ABkdgO12aq5CwuzgUlfdhfQqzaIdwn8/tw0WAg4A1/MyFq8XAl6S4LwWcM0FL3qJZeZggpQ7CMJYjXyt1mAjGG99d4jDNl2npayWPorr1eGeIhDAgMBAAECggEBAKtOWsbLB4JIq+g3LXrRPRQBkQ7U6tx6Bnc+0/E/eMo7ycfSsNB5DU8xz836d7U3ZI9t3LMv06XrKRD7uk53Q6x9uyIc7cBp3DZfiVNF6oddN8DUCM8i8gXjUlx69sf7wKQNxHSTBZn4EvrnE0odOSGl18rq1UiwrV9EG02hyRQquktPlmABrp+WRP/eAcvq9ZKTyVlpoZnmL17MhdGarA1O3abGIsbbXpGC3n0jAzleG3fkaXO4nAWwQxBScfIV/wf3JAbyG+4sSYBhSDvCIjGjecBdFvBUjjvWoedO8enL69V09DI/PPfeC7RZMmGhySqIyG1hRexALYsE262n5ykCgYEA+313sml/E7mrCEjvSb1okAGb8F7rEVSh0HjsMuw9C1hmDmYba20a6Yew3j65GRuAYgyfGaG9ftPHwXqPuf0dzCnQtf6Pk1DwuoPLaciAqFl9f+jOCaMmzpOgMWC/QWlZ5cnpx2x/VkF9ssCEgPNP3jEPkR3k+BHKEvE7xobWxoUCgYEA2PDLqVREUGxByfex/c8yz4xf/eb6exy6p0p7CUJWIbYtgSmVRhUORfG0EMHONbZrvZMgt325WQA5eKbKm2eozIUKNnnjS1EViYDUQ0yQrckGZiH7ZlUZL+cL+d0pMlrBzkMsvsNBdJ/lThRdeM+ksEykGVkwLahqbYwcTbvqQicCgYEAmf9rg3msUiTYgXs/4/SzCbOijJ9i7DrZ13GkmU4l10OrQtftpGusFiJ8AKuB5sj7ZY77AdQT2IzQfj6Rsj83tuRIJJmby4a90kiQD9eySOR7wA6L1ETup4KojnQCyYg8f0ST/gUHOIdj9EiFGv1jA9khAii/I9So286SXvAEpo0CgYEA0PJMFpF1IsjCLNcHdmBkngakRhZ8Vqt7E7nm+yoLb3jaJzd38QJCtxdvyVwBUzaaWwMkVdcf+BsBP7XWGwwiRqo1Bfcr9tToG4Ib754FE301TpWYYB3CnqK4pDZhgYBsfk+w/yNtHfkLkMKIrN3Bz5Rh0ZBXmQJHT6/Nawl9Pa0CgYEAsyT3mt0dbXyOGK/qdHj/BQBt4FpPN2g8EVtT2sWWjZhWNnsNN3ITHeMG7GC+2ewzW365WXQnfmBOaWLL3yUnA4weRnaQWiwHVhcy1TH2ezR/lQ7ihwMbxwmlS/6O6Bmlmvh4DsjntJ+vZjxbGTC9pd3e4t7b0fhGJVR5lL0wLZE=", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDVHmqxlszl0nTg0jJTgt+7ubcx8TSwn1UGdvgkcb6Wi05UkcW1qqUBOaeQR1P3xe0rCLRHEdWBmG/Ap2GgOXh2xqZYFfb/LUU5+B3BKVIFpUk4WyK1T7BYWr10zDeWxbi6VGAOk4n5bZ+DGSjA8utJpIAVeQyePSJkKfrs33pes2cR6keshYbDuKk1cXROVaUacEoHTL9iSly4LBJ7bWaG14g4tPGBOATiRtkB6ABkdgO12aq5CwuzgUlfdhfQqzaIdwn8/tw0WAg4A1/MyFq8XAl6S4LwWcM0FL3qJZeZggpQ7CMJYjXyt1mAjGG99d4jDNl2npayWPorr1eGeIhDAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmbcRvn1ZcdqsPsrGqM6sgJRcskAKratagpVLXF96hP9Dt", - "privKey": "CAASpwkwggSjAgEAAoIBAQCtymwBl1Fp6mEyF5tcv6qvHoCLgDDREbFB1IrkYBDk88FMfpYIJ5WA02pN+hZ2S6BoA+uNBAgae3m+zfsSav1QRzyJJUoAYcZ9XMiGkFKAJyGV0FFsKP98I6ic31yaoSd78iROxei+lhkXy7dvfR4z1maVW2HjhW6mMF6Qz9i/zdc3txq8fOkJvb66l+H2RJz846Mz6h7OrNmdRP1ZkH31+Rllc8/itwqM0nsoG0iKq3CrMHwflr66Byt9cLfdEBvElGmHFt+eshq8ShtRjVRIEW/yGORra/h1y/G8WhUwLoL8UpgNkIqE7B0Rw7Zy+KmbfaNEcFQhZeLm8lvkW6gHAgMBAAECggEAckzisj0yV4XGPSrXjK2mdZyLELTT5n1LZq+CVed01RAYPtY2mNBn/J2Pmg90fIMK0b5aSpmvNrOlA7/3dEqXphfkEZNL02p7IHJIlHARQqX56c1j784bEitltx8UicKZ9GPySzjQ9aBEiqj6UUIp/g/x0iOTAw/8ESNY3sdEmAiUdc/rBjbG1qRC8gi8QOsMqu81zh0qh5hp5chQucgWFzeMfYyNmjg8Pohx1GhG7os2XMUxzaBnI0Y4G9oVAObWPqYOwUHZTOQYd5faj+k8h/QcuTVtBLXvLjjmwwoSaqBbc9diorx55Jo2lg3tyoAU8i5FX6FmUjjC2xAyvMUhYQKBgQDiKiDnjnC6Gkq7UeiqiRzC1OeeBTqXBDcV2kBcDOEy9f4gApZJz/FjZ57WALWRAE6VM5Rd2ktai8s3fyZ99yIwqHfeWa0KdVTmKk8KyijOfxB6KPfajS0cEA8LRtrxsHo82a+Xc5hytl3MiLMH0DF1ALwcQCgMFXVGJ8E608Gy8QKBgQDEt49pjZNrhnRUDnBy3NXzxDGWhquJEbg42+6ZLI91k6HvOr7Pl0KZsDZGKYdlgKUuJFB3agQGzKx7ZWyRXaLZ6CWodMIXvH68ffmylsbMBAxOVpDU+Oa3jJ8rGvWi2CcsqR7SPSTLSPIWTigQ4vJaAKMDnflWSQld3nGpEXUadwKBgQDhvJTtKkIfrsBqqX2mQYagfKrWEXgCZaWpvRbCCeT43YkRYCOrds8Dndhu13RiT0EgMMRkzM6riJ6EPPgpgHLyyCQknbNWnffoZ9BO/6qtOSw0EhIZZRHiUbECW22LEM9hTxGxBCLkVFvZG5Q+NzI2C062j96o+P390Q5P7i4GsQKBgDu9v1j//PhXsfZhGDdZ58QLHkAnj+qlrfvelvx/suWzOyeLAK3MsxY3lJQEQrFJu2Bi+Oj7ElP6Tpt+9tTCyhVBUkZxhwxsW1TlMTLSZXdJ927HDV8QZAj0NNaDbnvRBzyh89FHbmgqNBMgEzzln1JEBT2w+SsCLU0LpBsDSTwLAoGAfteotaR33Qchy3Z5vgIMzY0txZ7vU3sGErxjiSNdmv0Yp/dxHWeGv/jvHm57BV+9iyANDicjrzNH2XtOX13lmYlctgAHRkCLVEdHxw+f0xXMto+unep4fniX78CDNUQAfvmBl9etJMmGXYNsISANxZPgHeMgi+EkMjAj9tdRUic=", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtymwBl1Fp6mEyF5tcv6qvHoCLgDDREbFB1IrkYBDk88FMfpYIJ5WA02pN+hZ2S6BoA+uNBAgae3m+zfsSav1QRzyJJUoAYcZ9XMiGkFKAJyGV0FFsKP98I6ic31yaoSd78iROxei+lhkXy7dvfR4z1maVW2HjhW6mMF6Qz9i/zdc3txq8fOkJvb66l+H2RJz846Mz6h7OrNmdRP1ZkH31+Rllc8/itwqM0nsoG0iKq3CrMHwflr66Byt9cLfdEBvElGmHFt+eshq8ShtRjVRIEW/yGORra/h1y/G8WhUwLoL8UpgNkIqE7B0Rw7Zy+KmbfaNEcFQhZeLm8lvkW6gHAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmfV6NS1ah83o84zMPfo9hCHgZ52X12QJsfixvfc9gw6v1", - "privKey": "CAASpwkwggSjAgEAAoIBAQCrRsmO5XAh6ObYK8t6BTU07TxuJvwttZJ+3OgTI0Ek/dyLztxEKY+/HXd3WLEb2uzvLHVDnvY3y2GFdtE5hFygF8vGtj+bz+fU8ax0kIMLNhyOlb8rv7twgp9syK/4PI10DS2LOAPNyUI5EHH68E+rlZwG/AftCVBp5lLCq7wxad6h0VizeoX/RzPXNxWpf4NiNUlbwAA/EH7rOUgZO3YWkTVHTehAmnJjQW0ZOfHFBQ2WTdhKQkLJCzFKt9466N01OdphW1F7JhVCqFK2SQZysHR3dohrsIGEu1TMOUFMenxeq8OMqzwqO+u8iFmxATygdqM5pC4nKOTevFYjWhvxAgMBAAECggEAHvC/soeyFP4czYpDzLwqG3CLzR5PyfYWC8LeTa69svAFKmBpHAsiA5VQIogsHmsTCDXQzTFnKzcbW9/V9fz6OpVx42jC3uPU7nvl+nysn5bb28ojacTOGIoQQLeUSlSt/PvwcUjiLwefZe2ZmYpV6hoxwHVA/UoEc8z+wFoDui0pEVdGYTvsKSKGfAkD/ZAIl4qDbSgTJhkh3OxEONlecBdocdwRdbqJJijpSN1snInKKFbXwvPfGAdRhezNe7wtVdWcUCHdvMsZuHyiHwrqd84J+Q1f+cLMloDVEyz5RwxlWhsCcFDs7AA15VyMErpCv2O/vaSphmt3z2Dumr+xEQKBgQDfcdTNBP5o0mvqkMUR9ODWEfC4JgZSRvG3cuHkPiUkJAVa1SH81d9mlqS5fzF2e2fUB8C+Qa9ZlqzBAA/S8Oia3D7T1SvAG9Iu+N1o6nrgY+BwfFGQfkJs5jp3bBa8qjVntcA0SeOipO/q9F5iaFK+2WvZa7vC2XOe3R36Z45KpQKBgQDEOyiOLcwRphATycwIRveAtqyZLKd81kYBRLOneEbhUpRRbxyPXstq7WngeaXCn8ZhfhiBgZCjs37Gf8GuQNyUNrVlF0OShplzegbiMRErmXkHe2g9CHbUm3IEwGxJmfOub1nIwCUCAbyp19/lcEnd/N+woEuaLnDUjSkHpZqmXQKBgQClyL578zWTrnQVUJ53KTpcemkhKE1OZIbZdqp1f0ptWzCB6VrTThf39NN5Mg8P+pXZsnrmbrPcg7ffZt1WxBnBNKKE50gTvFChO1KDkl3i+RfAPe0CiTtdsyA0FQV1q8/+B9L4uM3lkfzUVcVlvEOQiJ7FbXKdKlvnxeWFMapYZQKBgF01pIv0oQx5DwX3Qt1jqEkRfGa92UjpFxOfKJ8R+Mkqypzr5GsNoh5Ga5Ze8ifCcR76IHXTr3qy1jM/mCZHVP9qBTvhkw1Utist+XsTx44oNl8hdWAYVymiNMShCk7ju+ZNqh47dti/LniWvBll/xBc/3wMiBzSlnHAI48oUI9ZAoGAKRycRjjgIZrrqeTA8fJ7iR0MB8faqDrP81LD0ykEdu4spF1cw0fKb7rLWAMxYzrbIQkbaYMGQLDIVzGlga4xS2YNvDwcu6YKg89DtpAsW1dHs10rIgkb71ryaD380qtg/ZOCkzR6HVgtRumndDZkZb4b92t3DxlqBGa595DyVjk=", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrRsmO5XAh6ObYK8t6BTU07TxuJvwttZJ+3OgTI0Ek/dyLztxEKY+/HXd3WLEb2uzvLHVDnvY3y2GFdtE5hFygF8vGtj+bz+fU8ax0kIMLNhyOlb8rv7twgp9syK/4PI10DS2LOAPNyUI5EHH68E+rlZwG/AftCVBp5lLCq7wxad6h0VizeoX/RzPXNxWpf4NiNUlbwAA/EH7rOUgZO3YWkTVHTehAmnJjQW0ZOfHFBQ2WTdhKQkLJCzFKt9466N01OdphW1F7JhVCqFK2SQZysHR3dohrsIGEu1TMOUFMenxeq8OMqzwqO+u8iFmxATygdqM5pC4nKOTevFYjWhvxAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmVjmfRFrzBbhAUNQ45RcTik47MJe26933z8EHosgVEsu4", - "privKey": "CAASpgkwggSiAgEAAoIBAQDCIO8WRwEqMJeuKKCtZrpj9UPtmeLVLo5V0u83sklprjxHD7hG35/YzeFJzECApslKNMExJVb0dw2n8TZYHaCiBB+MJQjjyite3OebAlDVmqRGKIFtbz6AAwsQY8f5TViDKzSIjsOKPYPQz9prAAmZ2Ah/vhlI/azfSyi2Mxu0zhbxtw3txtpUXy3VPkSa4pLeGgpBr+oeitQxeBmrWVDkVoKeH9Po86QDuUTIVRiRNYraCbxawomnlYgNEEqWIhspw4LAicvGjLd3fIP6XM5WtRHGfTWzf2A+DCAzMQ9E5E3wXWS4OHISx2Dz37FLdYdn2W1A28/u9NJAu5281xFxAgMBAAECggEAIOsGv8dQij/tKIoZHO5DgvmvCBZFIZMgbas0B0TDMBlsfTxMKjB3YYMfxazN70LY9S1W6SeExDV/6k97wJtdhrueQdxx0naQvihFWcKdxGrRmlf6An2PopNhh+jzmvGjpbJo2RMkU0e1F253ghdiiWTZpBevH/JsIv0SrTqjYxgXo7EQAspYQ6eMwbk9G/cRjoSoKqOxQKBfNPLj4gE7ZbD07S30th2udB2qOiB1r0j7hRR/UuLkbrcRqgLFN+yeCU0nw08BVpThoFDWRF4xiQMqOWjvEIkomPp2WO0b2z6QFfrYqvYbE7kMYU1U7/TFPxYAY1evUcdFBg4bAI6DQQKBgQDmwrfLSWvPS9rwUSYaZP+PbGWikDf1BdNU0xRK9Z3P2rWzxx/jxmrkvRKJYaYoQui7pQKPc5OKcmTqvOOBDnvF2PyTnv/0QLC8n5ifDAW4/bVmS8MLHODsSp6ek9HzIumnvsHoRb47gZTc71YUrwTDNXbjSZVqV4VP2qomqzFmCQKBgQDXXIX1iytD1xvDHnWUfonaDs5ODBiFeriPn/LF9WbKhkSsJPTiGrFepO9tD2gnNOwEKmhyjirMB9fk0Lq1Ds5k1/6WejuSyvCADnO32DzqqDQj/a8HbKpEcoQ2YLPvCwChhyo5zudD3On+6DubJzdyrOh7m0Rmhn2Q4YP/y75qKQKBgCNAxAtOYCX/FKd5/jQyEci7apt3JNVN2ocu5/67nyxN4Uxhs0F84n+nUtmiDVxBPITOJKH9qiCQcVJbIPZqXAZRq+RxefC6oUVvrEU/9O/Z8oh6MoXUF5iBndHkC0L1pnR18/GkFffJSBCoj6IBStz3of3/E9B3JmqYoT3fEWDhAoGAGhagN62DMTWmrE1NSw7FHkA656N5ePnzz5o9q5Ndv1zihsP3UkiPgfqS8nAyWsWDbcHBY1crggnVMmfCplpD0F2F/q6R9udUmP6nL/cm8fosTsvVXx3fxmjk8T1nrqZzjh20lMomo8boJbP2PIZUpjSh+Q9HCvBx15IqDludFnkCgYBWDyb6zJmWYA6f96e3+qAX8XO9MPUkQXCIGvZZllg3hladYgcy/GEi17hWenXPIIB014ldAuYbttclh1dAQYBWhxxLofVnGXnGlHuzkB1YxPucSvfzJeRusyPMbYiIY7V2OQBN2abtbnau+FJUnLKKUFGQdoSZsOxNYAPbCh+B3Q==", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDCIO8WRwEqMJeuKKCtZrpj9UPtmeLVLo5V0u83sklprjxHD7hG35/YzeFJzECApslKNMExJVb0dw2n8TZYHaCiBB+MJQjjyite3OebAlDVmqRGKIFtbz6AAwsQY8f5TViDKzSIjsOKPYPQz9prAAmZ2Ah/vhlI/azfSyi2Mxu0zhbxtw3txtpUXy3VPkSa4pLeGgpBr+oeitQxeBmrWVDkVoKeH9Po86QDuUTIVRiRNYraCbxawomnlYgNEEqWIhspw4LAicvGjLd3fIP6XM5WtRHGfTWzf2A+DCAzMQ9E5E3wXWS4OHISx2Dz37FLdYdn2W1A28/u9NJAu5281xFxAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmWbAeXxuHTEppG9BRP1mw9gYj8ZCHT7RMowEUeQfhYks6", - "privKey": "CAASpwkwggSjAgEAAoIBAQDRAiJ40gZZKj4v6a2lvxnVrmNT7DSjC++V5C+lHnAhlGg6jmH/FBHOfrdjXitqjgkoSj6mcugO1onNOm/yBf4f97J8iSRkNFkuuaTr6b/80FTWfhX73zjM5w+TezQfPzIVxZdi4KIzsJ925BjamZvtksqGurY2Ng2fjzMhCvTPTsqYWn0gyhSNvJYGy5LdGHQm7e72RN5cD5pCpjyrpA07UWo4D7go9OycHqyVxFmz9zQlZOUrx9CsWPCnH2LWdX4H3FOOsi9F46uW4fAN1g75BXIiWkRS21NEkmNBhdOmCY5YThUjT3WcSJoainCa2W07rriezft9dIJo91F+AyEFAgMBAAECggEAfU/XVTMvJTSjllx3hWGfXrMw0HdVU9BrNCZcvpYSSr/NAhauAJ6K0pC86THju/4u1V42U9ue8I6GjmqUBbq8E3SSKgKbtAyCz/X0QJGkTzKlOvjbu2ipiIicmSMMLBPatp0CWAEwnuctpL27fQ0OJRGWpdK6PqSH5HuZ/xyvjL6uVZLYoQBpvFK3vQS/x7iWkGRLJGAEN7OsiicvdPa7OavQF724M9m8yZ/iLoytmI+pBopyHwSYyNQoz8uXr1rGD/gTLIcbi7BBsbOAT7C1UNtaZlhaZkM19aM9ngz6UxgZo7GYe8erfhR28UT55vWbWvXlAvDZU0FL2zLcb92U/QKBgQDtoVFT0z3smS2onuPAEQORHLptS7owGv9FJcAvCYIpjefvk4NBUA8G8uE9X95q/a5y4tnjcjQqgWPsBEM9oEso7moqY2vjVcMNYljH4mkcP8tDAPjvYq1gfEfzh0rFbzO70MY9IfX+5ESBZbuQcXKeTBgd+f2U45HQowSNBMuLlwKBgQDhKmTBr9djZrvDpudoY3whdIXfx54p5ONZqwj6HpVqNMpKD+nEpcR6CpkcRRD9fKLtZ8LRWMvD8iAyRm7tvezYn10zXIBTnuXSnrLumA15mmZUQXZiSKNm8tcGetCWw86yUDV8CpmY33WDHrtmlTRdr45DzetJf0sKWZ276hQ7wwKBgQDXbNiCys2nsZI//JNyKrp2Eno73VwUglULRdb9jXwv2dL7UVq7mi2VWhiyADht/D7rLhbj6EO8iQKiE5c1xhx9Je6fMPS86qHif1cHFo29q2PFAZurwWR2RRUhhHRXmqFm0jT1dNVDV4N3X1fz8bU8JrXybxDhqpEleLQGd+NjTwKBgCmbOc8IfRZjD2MR3kTNzUwpSeuV6UX4g4I4NopxSE69vnt9AUdTEkEy4CP3JzKP61NPDxK8A7sLbKOdnDXWGIPWvtQUzaml/PW0WX/5HNRRkYMULZnvrjIBwXXzD8QsHm+YnqlzE/rJn99AuIQ2Id0F6ZXh4Q5NtUIOWTU2BdMdAoGAZUfXHiQW13yWkYQ1HMoy5clhqmfvTjf2xENJddudBGwU1+1X6Q2rJZpgfXVJ3yJepSetuVPghDfNIXT8Pcgn7LyMhPxZhb8RD5naafImDS+WjM6oxSw1vAouzKa5LaOzEkjylg+Abi3wC/kE6b+ncbzmGP7k0pfZMAME9lQHfvU=", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDRAiJ40gZZKj4v6a2lvxnVrmNT7DSjC++V5C+lHnAhlGg6jmH/FBHOfrdjXitqjgkoSj6mcugO1onNOm/yBf4f97J8iSRkNFkuuaTr6b/80FTWfhX73zjM5w+TezQfPzIVxZdi4KIzsJ925BjamZvtksqGurY2Ng2fjzMhCvTPTsqYWn0gyhSNvJYGy5LdGHQm7e72RN5cD5pCpjyrpA07UWo4D7go9OycHqyVxFmz9zQlZOUrx9CsWPCnH2LWdX4H3FOOsi9F46uW4fAN1g75BXIiWkRS21NEkmNBhdOmCY5YThUjT3WcSJoainCa2W07rriezft9dIJo91F+AyEFAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmQT3xZJ34uwg2aqbSp5nGbo22g9dk6FurgmKV1kFqyjht", - "privKey": "CAASqQkwggSlAgEAAoIBAQDXWSE7zuoPE9GKt/qJazpxZZC9R9qYRpDQvQI8nLNbcmnGnpryPo7jYxoxe6rB0Vpgj6FTvpxDBLAdEq9uuqJq7QWhRdbzi4KZfRTqMdT06QlYNNbkVH6vy9hFaUwJQSa37FYaXNtrc1eL6AS9MU+rU+W0fDo7GFzRsz8m2616RHR9z19CWayYc6yv0+zECyP9GDSASvL6lnQvU36sqpZZiMgbIjDPwBJmpLTDZTz8Tp6CJzZjqq4RkVEWU/E98hJ506FylvwftHT5ufZxLHZRhr0MsLpn5QGeGMnbEEvMCzpUBBMzW29Bz5lbzNwdAh5B5pIXSRS/YUWCY678M0VDAgMBAAECggEBAJkveteLka3WADm4M8zq7PDbOcGbSmEF2V/TA7NQGLnVQm8aRchKPeR8i5ZljQtAPBTyNuVWctutiwWzU/3lX0HGhzm4b3ZhaC587pLFjeIFnzMSq0ZS4Kd2zspZY9A1ezBcOseYBDGEI+OO0Ugvuqd6D616rQV6iBRXeHXQ0K9mkb/1+JGg6p7i0uw0BC3t4+At8SCvZCNiqiq2t/kYfGVJ3fzt5ZY6jWiF2q6C5petc944BYHxCGrUUHwR4d58Z+2fN+mX4fHSquJlyqRUhyH+1auS5/gLzne60YH8BBTAMWSzn0GqGzLKWZdwt/ERax8LUZcl50Dw8rO2NxaT8fkCgYEA/BX3ZzMP7ICvB60a/uUxk9QamhHt+zpK9oOH5yklc2SxmgcyRMoLEIwNiclggc74iIkVKd/68HcPTi3uZiinASJOBW2ID5iLll9Sm9aJrVj50ilX63bqcP/RBVGvGDnZiJlXoM28DmUOJ9PtLq2RhHSeddB7d6ZNBUfQYmrlZe8CgYEA2rEh4oDDfHOpuibuuFdlHuVM0ytkxDSQUlTbdqeZ3RSfdUCFmgOhbVtqXliQnG75S8MipSmTjqR7SGMs1bEXb6C0N4TCmNpRH194/ejrVBAw10KE9KO3ks0GwpgC3ZsOPGnDrk5EF2OUwiwHl4EPPRQOkwpUe92GS/GUZgEpie0CgYEA2kKOpezBIc09PpEzqXR523uu2K0jdvy+wPebKJsokOOjHjCS5ppkwBvy8NTJ2TqBV34RM+N42tDLEK6WFh+mkUXJdcujHZW/bh/0X3d+VveNvdgMBpQ8YkAsEsXpqzkTTsEt7M2UwIXgnr1QQ7UGJD/wnyM2c58qWqMWGtBg9EMCgYAdvD3+PUHXVya5z/dfi0qNk+IJSHowD3GcMDuS+6D5JYe0+qvv0BSP+QESiPpIuvIcshCw4mFU4Np+cjWzbJviKri2X8/R1sV2/ZVG+Peee4EYk8veM7CPPl9v8BlbpmyeHEdmGPA7OegNKs1xdTPsOyDsL1hjazCKfPOPlxLd1QKBgQCUeBVAqy52rJQJqBSeBo+ULjdxWbSvIatU9A/RTcs5s/Zk+T7wOohFfShrthmw13uLVIok3XkNB8QLkxgJ8YBYm1d4tS2pKL7X3qa1QQfJWykiGZY0Dd5jk9biPCBUfzohYCNmkLIM0FdDjwTDS0inzxslRowOTrYMJHAi+PmfGg==", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDXWSE7zuoPE9GKt/qJazpxZZC9R9qYRpDQvQI8nLNbcmnGnpryPo7jYxoxe6rB0Vpgj6FTvpxDBLAdEq9uuqJq7QWhRdbzi4KZfRTqMdT06QlYNNbkVH6vy9hFaUwJQSa37FYaXNtrc1eL6AS9MU+rU+W0fDo7GFzRsz8m2616RHR9z19CWayYc6yv0+zECyP9GDSASvL6lnQvU36sqpZZiMgbIjDPwBJmpLTDZTz8Tp6CJzZjqq4RkVEWU/E98hJ506FylvwftHT5ufZxLHZRhr0MsLpn5QGeGMnbEEvMCzpUBBMzW29Bz5lbzNwdAh5B5pIXSRS/YUWCY678M0VDAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmdwBFu8NAZDKRvr5N5CJNYbQkAqt83MM2CkpHCuRk5ZW4", - "privKey": "CAASpwkwggSjAgEAAoIBAQDF3whIIfICn+U5ciRAqEsU1dFlv5HRVIQlrQHbNh6ev1YtV9l2we6zYcY5HafE1CBISgyKQgI7SYww5mDH/468m1pFPgMDfjCYpFQETVx4V6uB/CqAuQbxXP+QSgSJPEim2aL3UBwoAB48nKhPFRTcePfmli4TLsA3HxmEqdlu+tE/GZNOcQzjaersffCCP8OVLdKecBPxq67tEjw1UZVaJzlhGx8OE4K0aYwgGsL+1l+/EwoUqSrwsaTBpIUtWsXKUVB2kATJmxvQG0oVMtRGMll7C/zFNDHbA5P80ieL+owfOkKVeWuAl25cqx8tshMG4ead/P4OcyO1clivPkPPAgMBAAECggEAXfYgR6ie9LIbNuFF59JC/Rzf99I1m1LoAcAbHo6fkcDIWnXaFXPYNySZ7atwbJ5SyiEnvUvFJYQyZ1Iu6SopDNU006az5ae5yfJW10gpPhhboDkvsbqrWlhQH6OWbdjLoze8FHbdN/1+XkgCALPBGUT0a3IrZP6RVluVUZMaZoEsLSpWLZE3LCs+eKnMw96EmA2WIlgXj3na62pTgKC29OG5yXKV30FI4TAuh5JsSZEASu5D/rrvvZwjKQYkLl5BOexMVtvM2qkQv6E9T8xk49z4ffmL+GBEKvqRVlLZXuoLJL82sr4pbXKRUKreliZiSN6Kg1eCUFyEwrIsYqUnyQKBgQDxf1jyykVjXjeKQdlFE2GA0UY6A3G8p9GFz06OpoT73aTFOva4AEYP95Vzvj/bHX8wBU1821HJ/4gBnIATx7GRr1YACZKp4PMdm2pSOuev6w1LNvaor/jGijs221YCsIR7WHUk+gRhlPL12PcInu1aNmgOxjBwX1ApHz97LcNbZQKBgQDRwP+ikLp1bi1YpaO+rSLpK/HxXTTdB5a0WtT0AVojh4sPj4Fi2g0/YyYLZ1m1xqlS4kvfuN5v0XxJDZbGGpQ9KSR5hXXzM0yZaE8rld9X6gn7LqNPFQp8msCtmzHVTDo4ogeo9I/YgqeVBhEhPB1BG6/zSSTxtnvZ4OhJvpHhIwKBgB1rljp9ydZBNCLzwrRXmBlJZXTL1p9VEoFqr/dQ8gJ9DgW5GTVxUxe+4cYn9z+KaGRBQR9k2KHzL26C0leWjFtjMObwQ53Oec+xj1JVOsSDrirrl0EVrwkA7hXQwrmxJ3KfZCYND1uT+cVZmT7DncbPuf2Sx3PpKKrZ07H98T7BAoGBAKqs35YZLA/HshBS39WUrjaLcphSnmRH+4IP8v4FZ6JHdYkY3VBhW6w7ckaPNzkpSLhPuSt3E1BrZjVPYGMcV4kYxDw5s8tL78VYUiuGDTFNGAgSYAJGfbz8c1IQWVFVcH6Koa8CKVYkolYplKC1eJx0+gv9dZlVQpv8XSc8cRl/AoGAZXbLSJ3ulzrJqQYZSlBJA75VikpJG20qEgvipc2wYjLs9MKLyad9vkLVhsPYrYDPzbIG2JquZziF7qjtWOFsW1epgb/DsSRG0sC0yrwjGH6cWmCkL79coYLO0PE0yrIlm+H/BA6dinIOLp18wJd+qyYcjesRCLB5Z4C9nnvRfAg=", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDF3whIIfICn+U5ciRAqEsU1dFlv5HRVIQlrQHbNh6ev1YtV9l2we6zYcY5HafE1CBISgyKQgI7SYww5mDH/468m1pFPgMDfjCYpFQETVx4V6uB/CqAuQbxXP+QSgSJPEim2aL3UBwoAB48nKhPFRTcePfmli4TLsA3HxmEqdlu+tE/GZNOcQzjaersffCCP8OVLdKecBPxq67tEjw1UZVaJzlhGx8OE4K0aYwgGsL+1l+/EwoUqSrwsaTBpIUtWsXKUVB2kATJmxvQG0oVMtRGMll7C/zFNDHbA5P80ieL+owfOkKVeWuAl25cqx8tshMG4ead/P4OcyO1clivPkPPAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmQdkfy5Cz8AHXfRr22NNo9s6xUtzD5CfZMeP3H5WSaw8d", - "privKey": "CAASpgkwggSiAgEAAoIBAQDNsZvWO6HtI285SOv//ynnZfnVNEluC/to5NffooIqcxiIxaeDR2gXVPxQpG/nkfK7YddqCqfWYM33i+Ng92zZAw0XjtBDcevpyqO6u4i9wF4avI3QTDuf+VRqjvOATQQeivCYnHFAdTbWLbgZ+dpanjQMBoW5r5+XfTZV+WGFwOtys0ATjr20RDiYS4qUrUWs6ougnIzqnohcoCkIDolkmsjZOfjR5OhrTUlSWopt1w6I1mhjXwLa0Rc4fBAVzzZbfDDMCcw7x9aj0lh4hpOXYo0vRiDbdNOOMfAHSlnc4/ikan+yvXJuHIXR8Ru5ObWN2lx1ijTb1E0WRz1ekENjAgMBAAECggEAaLmvxRBRbiInY7wb5Beu5xCFdaaMaEoTc6Fnw4XCzggRirlPg0hc19w+JnTCQN2O/xZeja/lKgHZe9quJtVyhr7F8KOWp3AeE8dHOzB1+14wy14Kue3GQbm44BPuJ/mOSlqlCp5EDvReugdG/3q1UIPRrfm4JgUjtQZcHsO8glL/82ev1TSft/eiEGN+zRzEPJwBlYiymeeI8y/u7BrwxH5nK0F0C1R5Ef4goGG68R0zfcAI/1WJ7JlhljaT0I5G68f+NBqahfb0yDctio3o26Fvfii7LlbzdpsxJxpy94VPsfZmY+JS4IEwdkgU/OvFNMKq4MBoRSnersjecG9nMQKBgQDuwYVNpSwh/oXV4Rpqk74ijW7dsNxfwTEKpj9r2z8/YcyHsE+RrTMP4oY+bXUYyuEErEtbnQ9M43Vx+ixakEplaawdRKqAeNC6uZLlPnzjUPO7LU4+IGi+RkcaB0lUCpl7DhSA2PwDD59yJAXywEj6+8A39mrDYzcM7m/CgOHeCQKBgQDcjMj8a9mPNRHWIQygbL93gdbqltW2HVPYBkz1hQ6YBOIwi1p/9vjHyD2okgClr7IoUCMN8hfBTgV6HPV1zXATDlDc4OybN/59333doKaXMrSNb6wO5jXUUNHdVxBnslOE54vEgjWxygHKrpT3yR0HA1CYZebmCfi+TjDp5XwxCwKBgGgDaNaNua9Jmfa2bXK20KNu6DiuXyNcH8ha6tBLIL+1FIycc92sDc3CyucRem0FnYgSo3XS86J0iWrRKVd++to5ciECFCGKAK0IQYWbdn71emk18JtCNT+HkFw3hmuVfo3McYQ8g3W17amlJe4+dMzatj/rG1HpvEbm7UtYKI45AoGASKm5rjB6RUxezAWne1NY4a7NeAyp7I5NCWdKA7oKzNsPCp9e+boMzQWUCu3PeMciE1YTtoyEdxOVil3wIRfGTQDyc1NHoPwZxK7VcSd0u2vhQJgCQAZoxcK64gnFReTiz27aBaxAtIqxfG14dwqznZPiAdPQ9wliApEQXH9XI3ECgYA0kRZ26i/Qq935wFtGRmoADDH8q2zcGcl16p08SUPzf3dEH1eT/BXkDWMd7IXZrWIk5Bq/vMKf2yIMlHpek6Nz/pZ9llyByAyxGggC+7ZO5PDq4QHWc+WcqDFpyGnHzvPlEMr3nxpfu617fI3GNFUCueyEAJzXOalRkpduODSAJQ==", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDNsZvWO6HtI285SOv//ynnZfnVNEluC/to5NffooIqcxiIxaeDR2gXVPxQpG/nkfK7YddqCqfWYM33i+Ng92zZAw0XjtBDcevpyqO6u4i9wF4avI3QTDuf+VRqjvOATQQeivCYnHFAdTbWLbgZ+dpanjQMBoW5r5+XfTZV+WGFwOtys0ATjr20RDiYS4qUrUWs6ougnIzqnohcoCkIDolkmsjZOfjR5OhrTUlSWopt1w6I1mhjXwLa0Rc4fBAVzzZbfDDMCcw7x9aj0lh4hpOXYo0vRiDbdNOOMfAHSlnc4/ikan+yvXJuHIXR8Ru5ObWN2lx1ijTb1E0WRz1ekENjAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmQ8D7hnLCMEXe9adEwjugWgKeZbDzjX3oUUzQYKbQ6CpC", - "privKey": "CAASpwkwggSjAgEAAoIBAQDSiEBozfLjRVvHlCP8OasiVQ3OLiyGhnm0jkgntuEgaErDvkMtKOSe5WS9PzUhN0n42WB/TGtYLsk6fsfmhL+sfD/GV4boOJTdieuXmL0cwdLUoFXkdbTUXeorhVmuTzhV+7TRKgZBOzf3qNL4L4he/dqFrdfsgZ4jXX0QjTcGpUcFoDm65lfYvoWAnPu5EWcqcwRFLXaHF+//AQOtnxMYv/BU255pdit2eaOOrKVLzAQYWRmCYRNTEOcU00Lr1QgGyJJ7f+Hyv7Y9WbTInj0SOxm4bz/cDZa05Z9Sl4MT7mkD3ixpnFAyQVRkdc4mZbbWbuH3vjdWuoVd9iFNbxhXAgMBAAECggEAEH7qhQu1/0a89TtPQoEGPq9pYIFPrc61lIcdcjcrFo31ZbbvrocouqaAqS9dq1eYrS3jGLZVJtirnbC3WwGFvy8RFCphgKqGR4F5+yvVjX5GVbCmajsqywT8xyIwr663XE1Xkpf3W38XWIla1mVrCv5a8+R2KarSSDUYCob2C8gdBiE+dj4O5t/SkfUYrpetcOSa3ifXM3Ctig9X6mKDxT28MoOGAHMeqNtXiAJQFJqLzp8FAW+k9ATCMqlkSkY60FPMr2BsqmBktHr7rO3AvL1WG1dTLRY+eVuBYTMGvqFkLYSqM71id6QWxTE2RYX/7xvgoOFpWJ84sElJQN4V4QKBgQD0NH6p6tZB4XhKlZfmy+VpviGjfgawt2nOvD+iwMbjn2EYelR8TwxWqKSpL033nryE2OGiLn1tGpEAtqTUV0is9FBWnCZeITfNNs1gVhtGXNXBbDb8+3P7fidV52YOKCi/hWbAuQdu+wbHAxdPri0unrV7Jt3oq5BXotd00dZgsQKBgQDcs2egfVRHX+Gz0ygMHikMAHtEbEhBu/Gu2+mcqetFklJ1IWJdewY3tyX4qPZHoRB5QdGFXSEzEyxiO+b+YCz7E1YiFtTmvRZ9DFjF61YP3Dp6afYHh9bMigzepXPD6CBBmle6PUo88hp5xhMxAoCMWaJh17RGZCT6dK1ttYKLhwKBgQCS5wFLNfmtp/S06Uh3jjBza+zQbP+ZTrxXoOanAVCjnTzLfMtV/Ddv6gMjw1EjpFnDkLQq28yX1WNlCnodQmR1poKtl0F9Xn4y9MSXLzU5Hp93u6FYjes3XqxLAOhjm8TncVhelu/h0yBAl5tuU1jasp55dugHDy3FijASFijgAQKBgHK7uYWPYf7w847emRUjoMcigPKjMDUsFYqHvLy7ARpb5Q4LWu2qBSN1zQGmJNI8AypmcxvXvGim8Q3ogj9/lCK6fK6gG/IQHt7HSmcp3sXEAYqeB08G6T3QDry4WqRfylUQfcbOEgf4/JaNyHBUEqvj9SzUTF3Dtg2WForQL5uFAoGAZAg1h6sim7bNBOLV09JTucuTHndWAe7N31yUqVIGWqxiyZWcSx4AL6fcy9PhwGXv5Zo9ylZ+HaOYWrPAtFt/6F32tBvylpgjU7LX8DEg+95AgSA5rPGvxwm8yVnWnb8r9FWE208ZhUKrorDTun9LKOCRZ5fngguan9wapxTGiDI=", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDSiEBozfLjRVvHlCP8OasiVQ3OLiyGhnm0jkgntuEgaErDvkMtKOSe5WS9PzUhN0n42WB/TGtYLsk6fsfmhL+sfD/GV4boOJTdieuXmL0cwdLUoFXkdbTUXeorhVmuTzhV+7TRKgZBOzf3qNL4L4he/dqFrdfsgZ4jXX0QjTcGpUcFoDm65lfYvoWAnPu5EWcqcwRFLXaHF+//AQOtnxMYv/BU255pdit2eaOOrKVLzAQYWRmCYRNTEOcU00Lr1QgGyJJ7f+Hyv7Y9WbTInj0SOxm4bz/cDZa05Z9Sl4MT7mkD3ixpnFAyQVRkdc4mZbbWbuH3vjdWuoVd9iFNbxhXAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmYKfrXc9w7FKvcQSL1YsBejuuN5PBZKaYuSe3wYge1KUq", - "privKey": "CAASpgkwggSiAgEAAoIBAQCrUJc02dXVwzKc1zc0xuS/yEHTb8hz40N9aRydtRc6Iu5/7UTq3TFQwseg96PUS7NLCkPcCjAWu+wK0Snd1jx1vspRdgI9ChkzCIaG5hh8IBkyV6DMdoWs398uTs2IXbkXNI1bxo3WwxH+IKwM0ZI0UFlhnm2SqAYB0Ei2HVQsH7Dul6bk7f3aMnOTFiIKHwatxzQs+EITa4blDxaecI3vtjeOx6PY2hXD5RtPtIFd9pzo4pEbs2aDakE8Lh9gO8DcjY3WJpLQxYOWjCUmE+AwQ7x3kTVlyOxqlDmpcIMTMLwC4Cii9DO9EWVsKNwF7XqkbxOo6wMeao22iOqDt2WbAgMBAAECggEAJP+wyF9LiXEw2yK375QND0ZmwQ1hU3X/u3QaFA1qSMoGjGZn/flrjy+iAae6ID2BKXG8GiexHxfS8LsfuaNtR1i/RTyhWyF1M8phk3zaSOR9zJuURNRMJnvrLYsjZJIpSVO2O930AC/9EM9pmRMh6l54D1cx/vx+36FmMr6+0RBkoLtKpUxaBX91wcvo+JC6ka0cPW3hwhRBAqETb3buXOpCZkkNAcKDvOYWn0/VcwpNS/DJ7KEl9VydhR0gIu1QqAj+Pi2mYi6dZMrS545EwoXuLZVH/uEta6DtQTVeA5PjozkSOqfF1vkm19aIYXcxCSd0moDMQRPo+cmJPWfagQKBgQDWQrlUEBXNMIBUzTyf1PEAU9yQe5u6DgFN69Vdcx1NERyf56oi1ILDVuQjEEfIpOIAQEThqxkoW/2xRVNoAN+TFSNuOIToHR4iM00oqsnpI6iqGK3+xEo/BFz5suGaa+Y/3976EJZv472QAEl8Ft253S80FET9wB41sA5gONRgoQKBgQDMsCNtHnCHciSDrJtGutirw/JrEk3lUDi3rGQwmrunPk3I+86x1if63D2Rq6XKJI55dRpzQfSX9rAeWSBKokp/XKw+YNNlLeb7LAqTiQzA9Jw3dRJa6PPWg5UaqnAaX/6kqsmFUh8/jINedwZ5b6qHzHxcIHhR8y959ie4btLQuwKBgA3aIHsz0wUCBrn0zt+Sd8ZKpa7dnvLHZwQvpAq3n4RU/+HCq3g2/wE8A+HUcp+hMU9M2GcylZzLXbpxPfQyYkHzEuhUVRtgjostf+aKLCWbfZMJp24aKKasVIp8KyO9qBQnGBZYrjErqxy9OAMCw3D5wMyAJvm0yv8zk6pa4jghAoGAOfYOshGSj+g0iszP04GJZWpBNSyjvjGvPeOlI1ZNmRg9cpJLf3RDMfg3vw46Djm31pDggo7Eslt6l71pNXkrW1FkvO0yL06GP83C2PBQGjuqGNIf9npMwgvUpw5oXC+ergZmtkgA7T/e21sdDDogsf+nn3baW2pfoUuhB8rqC40CgYBSI7a6mtBD5mHivfib+TyzaSAp6Lxj3zUJOq9l79BDb4HYA2X2Akqh+ZAe9sbOK24Fig3iZvGfKRDrCUBU5Z4tRzPBYXeP/UUNXZQ98Ay1ZJAQZdeDd4pKKFN07rO5gpDiuZKlYZvTsiaK/nT2xdJozCCXnAqYd0ODL7QOyQ9oDA==", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrUJc02dXVwzKc1zc0xuS/yEHTb8hz40N9aRydtRc6Iu5/7UTq3TFQwseg96PUS7NLCkPcCjAWu+wK0Snd1jx1vspRdgI9ChkzCIaG5hh8IBkyV6DMdoWs398uTs2IXbkXNI1bxo3WwxH+IKwM0ZI0UFlhnm2SqAYB0Ei2HVQsH7Dul6bk7f3aMnOTFiIKHwatxzQs+EITa4blDxaecI3vtjeOx6PY2hXD5RtPtIFd9pzo4pEbs2aDakE8Lh9gO8DcjY3WJpLQxYOWjCUmE+AwQ7x3kTVlyOxqlDmpcIMTMLwC4Cii9DO9EWVsKNwF7XqkbxOo6wMeao22iOqDt2WbAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmQpLk8DTM5Wqu19B1uo2GRjMJyLQyZh7Vz3ejwfnETNHL", - "privKey": "CAASqAkwggSkAgEAAoIBAQDeJD/uc++aiPTDzGhaobaZwQGxUcBTOD65utvylkQx7DJAGKb51QjlH2O/5AC3wqcMn7D9MkxSX8IUUlDjbJjE0ohCqNnAYh20zWqT4fNTzPnTmcanuQm3aFVxEf9itD6GRiiCcZJPGnxdetkaN3vRq8ik6pg+yb5acLSeEWpw9FwhUbWYQeIeaPwlbmtaD51FTS5jbpIwl/oDQf3HRS1u99mf3XrKVWNDfEEr98ciEn2NJgHQxC3hf702xCD52F8ZIJbd7Lb30vdaxzNWoZbDuzVI6D/RuYbxx/oxtHxnyoH21fbtK2JXJVNzUNEdmgbglQv4w8U30PlZN2UDrjyBAgMBAAECggEBAMsb9eB+3Ks9Yh7MfPWxOpYmpPeOOf1dRezn70dFIaFLxz5XzARORs3H/5pqTEW4kqi2Mkuve50ttPSDtzXaC2ya2r+oR0Dh9StlTndcdvE+T4ar6bldNIcfvE+gFxQWnbyD1XI/iXkOTHvkYTDZXjr9iH1RilaOe5+RwXNtlRckgaFfZSTSODpVVxcJLmx9HUQxAcJGbeD0iOrLZ/5BNRO4tnwfdbgIbAvnCIGmy3SsalYjOJeQYxnUZydV8IP5FD1b8WwiAIbhvWfhFOKIOePljOac5aAhTRCNeXVzQFAH0jNK6paDvMNNIhU3JKJApSUppRwdCv2TsDnucIitPskCgYEA+pe+YLnwZCJYvcW2w4/W8Oyvr9yleIz0RmCcRkp4/qDRcDROO1L7z2wtsMOE9lGain/pQeV3/ho8B3grbe/9S/Ezfz+htV6MNONjH9wNVB10FZau26YGcVdPT/rjM+fl1IcaaVHc0RE8OubKTAvNW1yzBmU/zY+fCrMg+12PS9cCgYEA4u9YDnhIiHdewJ+uD/Tk8FH1OpEsvqkfi521Dyen2J9AITLvj8B/jqUG235eM6ZwYh5YXLkuJmvZgcLhkSbQieM8kb5jbGaFcu+z/13qlUG0HzD9iVWqd2u476aeVW4uGVBV6LON/MZZ3AkkEkcNMQ6baMuZvhLIt4EsN1Nv72cCgYBJho5wWP4kk0NQYxuN471gMUIXKnlOlqTxpVUU9rLrmwn4jxBJLb7+jDIXxDZWA3mBm6g4EnkTkGT+mA6+EgVS6/F9K5Fp4tTmi7VA2tL6VC4ES5MAlYUcak62G9ngF/GCWyWvszpECXePnLnMeEYHwXoxrTF8QeCbRhWuSzRJPwKBgQCkVvfJ4smEKg3wKLMA0zRH5NJWS3O/zvINRXQtOWaPtSPX5u8dhyXYwyGoKmdFuC6Cn78VxvTo1gl5swtu9lDmyiy+zsVpZwUVKwmK0RRkamRqgivZHLSKLvSKeHsJGvU/V7IfBoi4mVvRwLzij5m6AP4Ccg8wWqIIYf8HQeE52QKBgBQCEVLGzF1F6CNhu2LUso3+72usPip3yNP6EUWCZaiv+/r9V9p2KRxYrSNFZgh9O5kOc16XCuFqvEVBCSK93OYuaurVj/lw0+GWL4V3cjcfkEJP/wJbz5ZG1pVsT3ZMYLD5MerY/I7MzpDK+Qt0VgnT78/2B2gxrWL6uSsuTsFW", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDeJD/uc++aiPTDzGhaobaZwQGxUcBTOD65utvylkQx7DJAGKb51QjlH2O/5AC3wqcMn7D9MkxSX8IUUlDjbJjE0ohCqNnAYh20zWqT4fNTzPnTmcanuQm3aFVxEf9itD6GRiiCcZJPGnxdetkaN3vRq8ik6pg+yb5acLSeEWpw9FwhUbWYQeIeaPwlbmtaD51FTS5jbpIwl/oDQf3HRS1u99mf3XrKVWNDfEEr98ciEn2NJgHQxC3hf702xCD52F8ZIJbd7Lb30vdaxzNWoZbDuzVI6D/RuYbxx/oxtHxnyoH21fbtK2JXJVNzUNEdmgbglQv4w8U30PlZN2UDrjyBAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmdhiMT6xxQoJhz7AEN7LbeFaxYMXghAZ8SQVofNbdg773", - "privKey": "CAASpwkwggSjAgEAAoIBAQDkiM7cicZwosubnFMqdW2aWX4pkHbUV21ihnfY2ZWGlsc9/tRyGzjKVORdedDLyIHduVif14/AZTOML/DnlZ18WhfG5XBSu9ksJRKpdQgj40Hvuel7yZGs/HJ7s0p3nR3+CWK/ACo1rvlSIb2UZIG2TdVvDYpBM2VZLU1mR0HlG5aNz12T7DPgN/r9DjMNtxIZCvk5rJwJnyeaSzfkOVwADIlxO7qI7jO4SkYyzDIIyDemsSr6hd0ehSPWp3E4IuZs9wVJ4HvqzlHedG8RtzZtqC3gL9K1IWxY1ioy7WrgMwZzGPGXSRs3z+5JPIUuscMd5Xwb+7reqQg/41SaBhq1AgMBAAECggEAJmLUXDbIHiM6D+kyDu+qeUKO7mxViVUmCmaLuuDRPMoWrVMgXAo2f8XClfDgIVqMdbGsMS0D+E0HW4Sx8jQvP7PiSoY/V6Y11DRl7hC6TUzexmVz0lcJIQVGNYDoAS9i2ki5TVu5u0qoliMUtNgs8XIhZ4XesxTu8Quq9IMDjnfCcXtOOFF/9YS9HsqaTd5y5UJFJrmjiSz3MR0r6xEpXjwNAkgBfT7iopdVkShxu1k7Hd072+yc+3MsGWtTpPr/JxlSOUCqTvsuOEMdYOtCS6kVFsK2nwf2EULTFkawohc3wdqTKrd8oLJez4ZZ+BRghWyzumqjGExgbABo5fZgAQKBgQD/eG0Fphq4Ne6PaDj2WKNJP0/BjvJhFfWmonr96hlMXYEVkQ7gx/yi4f4I5Vm7oR6yj4Afy/XZodKOQT3GnDla4YxGGh/yO69GjBp96CPYufODtrturCy0t8tCM9f27uDNw4gGhpFOQmMLY3yFX/9MulMz4MH4SSCUdMYkRXXf3QKBgQDlAhZxhOUC8Z6Rc+i/HW6Ot8AvRYvaJGUHtdn4TA25sgvvcwkKv3JQDv8Gx4o1kpHte9WnnrzUanYhMqKqOtPeGbtGMkohIleq3plwUwyX9ax2EpNV2tlvmcQbgXYJUeH+DmVEPpwYTaJ6qtbZJW7p4caIz1tObD4z+H1yailkuQKBgA4nburkNBDGtCvv21ASwyE4x8Nylw03+T89O1E8GiC4AYHfYpKjoeSoXrnBc0JI//lmp/ObCkj/hTnqdXC+kRLu8iWkJub11ZU0B/e319yXGN3QTvwnv+ZXVISbeLiurXfZAH1UEVLjrLch0PFWyz9GB3wVVMnby1lOSvgRfSFlAoGAceTx6I9hnm8wn8J31OT8YTp9+ISsI1fKb2U//L9GbD5itToPGytP3QU4TNTcpfw5W1UlU3IdE7/G9IfMYsFTMbi2bRkBySzdUPvYcAa90q26khZ29FIdpeVhpRRj8gqpTMM4FhLVazjhQATLSb/WQ7eoF86Y6I3o+cvyB/9IivECgYEAzqTpNA5ZZXLdzKAjf5kysgvlmdsk4psGu8a2bq8qDAbh41IDWqsNko5Eefv3RIHLqg1gGB7f1qnXth0SUzNOx01o+5LA7jVggzWhq2jgMnSc1yds3Wqj/EMWVaGP9kNyigbGHfuTRhFhgegCiPz4xf9p6tifDoRChc5tIy0mocA=", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkiM7cicZwosubnFMqdW2aWX4pkHbUV21ihnfY2ZWGlsc9/tRyGzjKVORdedDLyIHduVif14/AZTOML/DnlZ18WhfG5XBSu9ksJRKpdQgj40Hvuel7yZGs/HJ7s0p3nR3+CWK/ACo1rvlSIb2UZIG2TdVvDYpBM2VZLU1mR0HlG5aNz12T7DPgN/r9DjMNtxIZCvk5rJwJnyeaSzfkOVwADIlxO7qI7jO4SkYyzDIIyDemsSr6hd0ehSPWp3E4IuZs9wVJ4HvqzlHedG8RtzZtqC3gL9K1IWxY1ioy7WrgMwZzGPGXSRs3z+5JPIUuscMd5Xwb+7reqQg/41SaBhq1AgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmQedrgwE7CLDyJa5Bv9hTjf7Paan526sQd5gVFCmMmsHt", - "privKey": "CAASqAkwggSkAgEAAoIBAQDoGW857wBAmW222eVe/8OtzHX+7QKwOCtdH3fJ1xEbKUrSezIYr5xwUwlm2LC3AvMPVvFFEFauOqzOJwuIitLHx9S++J9N2ML6rjigp6OLS/j2jtWViQPMkojjMMGONirttAvQGKKoyAXZ1ExnAIXBHZ1P3U5jLrQSCrdKKmrI5TXuI7vxeWfEABT2oHTfZZrdBb3qCY7aiQJmkKQDIQUS05jazhwt4wqaN4zByBagiHf/NU0r+tHKsIqWDi2uKkzc6BELb2wYPyODIiqwDC1714TyKqCV7HqT7OlJ5N4fcZ5M1BBk5Oa1ZMAGlXGn4VqHz2agu2Jqxq3tC2FvSemHAgMBAAECggEAU5qt1QmBZsOdoKr2k3S/0MAAlPZc7IsfG6k1JhCBSe5i1FSqI/hF+rP+g/x0E0hNs23W9NDA2HusOYoY/nM7H9mcibnW8FyvR0swfLZGE+wm3vFugDHdm3gBNQ0f+5EJf5xGUQw+s1txuBhf+Q5YH8sCGn2WOeXd2U3g3idPVdODL/IZXiqR47rpFVlUIheJu5pkXCaqMrSgh/JfZVl4aBK9BoXu3hXCgsXE+lBa0w0yl6BDwI9U6vi7USy9VnehW278ZagI/i57BT1Zzgzjj1WExQj1J3t262DbIHhnM9P4uLocXs6PMKfpozmGkKucL4E4MyUGi0YahV+7TPO1mQKBgQD1Hm+ofJIavaGTDtWYoUFQp3mYHodeeOfDbZu+8JqQl32xA2sZSyQlFlh/OPdlKl49eGJjkcibMINrvOabqgdgG9Cw6TwltYawd5HrvzBASjQIVGLWi7N4wVlRwvw5AO02IsGVHXPob7xQWx03/9ZnsxVBNR6j2IJfQRSU7a24pQKBgQDyZwrkP7EGXE0VMGluMNmsLKzuICXIC372+3DlHmavHFakWA3bqXLh/u2/4L9fmtcGTX3u1MacjnfgqFVLg/f7yJ/UmAHnqKklXvOfO73svGwgNOBOVbw6ms6ZzP1GN//RubeCFxOq8U9x96CAxVTHGEsPm8//Z+QvqbHjIXyVuwKBgQC/aAz5HI1apEnPc/4HOaSvPpgM2YoLk44nZSgBahDIaAOWfnzbO3n2HATvE6TcMsF0btUlu2lTBgcZ0mChnZw0yIOmIfr910pd8oDX/mvHSCppdrvXnS+AVDtTRVd/i+GwLGPN9TnVf6sldIDUgcsDHyyxxrEucJsdlsxjn1XQoQKBgQDQoh2+vI8KEXGK9kMYQ1VmmoEw51x9ZF+gBmRx34uz1ilAhEVRNfQaTcel6bPtfqDp3NKyOFLFtt248EmRmIFdJZ1jZn3lPMZw0tvOxqW+V6KcycXxxlse+dUujT/FKze09Crc/i3AaLffOKndi3pfbipUwd/xTSMaXu0rt8u6NwKBgCXXYCSUjEfi2qyeYiwt+iTM1FBNWE4roswyaM+HZBO6mXhFgg0JMbiovCJckfnVa0pNEo2RxM/LMVDCN9UmqOVwhNc6tMEUCerNhei+OTYugKcBrVIhcOt8YkXAAzdYlAXjiV2oU9cHQQ8jWHIMUNiIUsKuZhLP6I8y6NmQ3/D4", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDoGW857wBAmW222eVe/8OtzHX+7QKwOCtdH3fJ1xEbKUrSezIYr5xwUwlm2LC3AvMPVvFFEFauOqzOJwuIitLHx9S++J9N2ML6rjigp6OLS/j2jtWViQPMkojjMMGONirttAvQGKKoyAXZ1ExnAIXBHZ1P3U5jLrQSCrdKKmrI5TXuI7vxeWfEABT2oHTfZZrdBb3qCY7aiQJmkKQDIQUS05jazhwt4wqaN4zByBagiHf/NU0r+tHKsIqWDi2uKkzc6BELb2wYPyODIiqwDC1714TyKqCV7HqT7OlJ5N4fcZ5M1BBk5Oa1ZMAGlXGn4VqHz2agu2Jqxq3tC2FvSemHAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmPxSGSsvpFYHvsNYPnDRe7D8YFp5sFc1DRJyJjA6Z2pZw", - "privKey": "CAASpwkwggSjAgEAAoIBAQC9UYzpcTpZ5pmfeKJ1+RKlXbVBloV6Y2xX4pmxDcKmlOYjLdlPTd5o0/xR1Jnz7yABwNmbqeoEywvQ6DxIdSBqI/fY+Eiq5voSAb8b75o74KjJKIpzpX1IfLjyGpPHF8dNt/2tOe2qWNYfYu/9byt6I3Yj28x9UJl7jQCq4a+mxZnQRfb+7StUEeTOumbYnx+YkP+eyt6a5TfrDqRbqcEVgS64Gbne9vJcU9Atr+y/QV600JHSXVr0l0KzWgN1ihCUSBhAiq+n87NgM1//hFlm771/DwtREvaFBMDIBoGgUP6mhAMMlP9IuigLMaL1Pp/DW12TLcE8vWM6XT12XF3BAgMBAAECggEBAJThFuFVy797GwBPy+Ledo1Y/fuQNXOj0EXky1xzJ8n8embb3XMCF490dY6clF1ChXcbg4Vov8H5M1eb6hxJD66ojnYv+mV7stiKSxHbAP1plRJsMUT0tWtVudOalvAQgQlbUcDyNzapGeog0f4JeLVaQcO9TDiYM7r3jbjUNl/81tBiQgHWP+Bih+nXWk3531gOhqx8abJCWt19W2AU62SnafstJ37WCMrKtM4ZZgwBEJnOOEdmDAOBU7o5oUXicUuAKheJg2p0CNKLpyf5/PFItqLKASmjgmS8i1C/NtcWsza1w90A1j6ddiYfHA7h2zrZGh4kYLffnUdXhmu6LkkCgYEA3YE0wF3jNY9L9tKuRHKRKe/W8Eifu8Be4fu6pxHvAAXWDQtXdnaKZGr2JohCAG/PyWrY3qtH73bXI7uOv0tWQbAZ5wVG1SKuODfFMuPy0iw8qcTinGXGSJc4YHtQeunKg0O4ueea9kKdobDrz6+PJj51iDEGyxMpv9wrU7DtFE8CgYEA2s0rXmfvQft636W4kLlDqx8bIB7oamr2A0gC8zegpiu9OA3ctJSxsym574BDgyhRAVTSYr7SAgKB4rpVPOpGm7CLRReF4gf7NH3VjiOUccO/G6hJ1uJ0XgPl0/CWKJ4eZcgPiEYawMYJMEr3riW3h+uoYmVyrWveD6NvEhQxGO8CgYAbjynbDVNppIyVBx17kq2RBDA/8Sk+mO61Oza79rU/0XoSYWjeal1JpS0/GhDsMP0vWEXnXnQyzRxza7CVCHCQ97IhVjy74/a9M+MrM8VQdQSPMtnnD5qeCYKQLoeS42e48UIYj0JuhVdLeNG+I1+yKG9DJKZtudKl9mTFouu8bQKBgGsZSGQyfbOPdAqq5Je6h3vogu+LEXqdloPuqLsCfJk6Cam5Z1HhAsZO41tvLhyyDEyZh02cV9FyBr/DM1vY1Oz6UoFkTT1haL295l1n3w58oTvZeSM8v3cRc1r1hZqmIvzxG2E553h6tx6zY18TyS031bksLSDkDtMazZBM3+dzAoGAXBmm63xkkEVJlzCfBtlumcE3OsIbQchVaz56rF46O1whVw7tJnALDx8IIl3xT3W6arp01mgJSzqWN/93ZGl5VvbCALcEwX2w9I81SCrgsyXXpA0REo6d2ex/EVavRK0uZQUFD5ZwhWPTPpqT2bXCW8aF1MSh+U4Do/3MkLNgxuw=", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9UYzpcTpZ5pmfeKJ1+RKlXbVBloV6Y2xX4pmxDcKmlOYjLdlPTd5o0/xR1Jnz7yABwNmbqeoEywvQ6DxIdSBqI/fY+Eiq5voSAb8b75o74KjJKIpzpX1IfLjyGpPHF8dNt/2tOe2qWNYfYu/9byt6I3Yj28x9UJl7jQCq4a+mxZnQRfb+7StUEeTOumbYnx+YkP+eyt6a5TfrDqRbqcEVgS64Gbne9vJcU9Atr+y/QV600JHSXVr0l0KzWgN1ihCUSBhAiq+n87NgM1//hFlm771/DwtREvaFBMDIBoGgUP6mhAMMlP9IuigLMaL1Pp/DW12TLcE8vWM6XT12XF3BAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "Qme9BHBNLCqt8kagc5K64JwHHbVYrB1o3AjCZvpSUghUME", - "privKey": "CAASqgkwggSmAgEAAoIBAQDEEH3wzUPnAbHJ/p3hwFbGbu+bGLxdGQrq42iprklRW8mK2IKD7vOv9nMc3i72nXBDeJWtmrCjSuQj0KI70+X0luyPWG4jCUDq39kOxgc2w40nWJwjD63mfVNiswkFBvmQ/ll2nxJRTW4b1qz1EcUNJwXNWSi2Lb+PcuzB8bEZoLpCPsRbiHkDuUPb3DLDm/NUtv3MLqUfPmLGvy1iN3UfmCdItRtapC5ZKeGiUakzjTHBzN39UnNRFHHTfMFcXt1HWx1bLXqru/kAZLgVVZ2yQJ+oQ0CZoiLgJcXP52LGWh95m7hvUA5EjNxSVGcwESXkJc2z8pda+UOJ6gZCcwJxAgMBAAECggEBAJAXHrdt401ObX7p5NYYKK3EscrmLuiskt11K2IoeDGWp1OnMqQLZIQZNxgsIY+UvQCZCkd/u/kF/QxlNBWL8SAEGu5uKuMM1ezHfhnhZ0PUC1SzRmxuBXuy9yk+Mo7DRX5NryoCVc/ye81xw8KHwK2d1CHKOKVKkdG2wFD4cxNFRLU14QJ9ier2b9EWlTpqY0ug5y0J6GfISNTGLUsccY0od9TdCxCy4HbbbCNZfVegx5vX+qaYM3Bd1xiF0MyBpwLMT9+hVOgUkp7BM1UG8DgvnuW8gKhQVqtpwvUvbEI72uHiK4OMruQx31rxc9I0b1g1GavsVYAwsJ7iH2tlRFECgYEA+tuRKfxYdUJ/sntLKnvGIVKJdNID60OO1mopOgryjYZ6pVzQG5ZBoOWq0m/f3gnAjAMDzzIyLUUxaDkEJ0118sCHhUiV04eIZ1R0woKTLRvCcwaekiVVHuPBqD21eRzK7MiCsuuMADbg3glk3C7LRJMzUw+0SH855Kg/sDdblAsCgYEAyBVif5pHW1Mteh7btXcDxuDh8DrmYu95zEl01i+GEK+/Rlz5uoh2RrqXuiO5NGBitZ+pc33e7b9tNq9NPoTEHc4Ykh60X4yVZXlm0RP9wCD28KBkYFwEDeULZz62fJuBZNYmR+Gm6i6UT7ARKadx93H2ceBxaULp/ndusAgh9PMCgYEAoRf7YsEAdVzc8Fso7AFMPP3p87EifySFR8Ao9XMuTCA+Bo9RvUWCo7aZOkZJtycAFWmiOp57hoLWtZ1Xw32E7v0gikEQpiR1PhYIXRjJNsCK4J8xmZyLyyhrpoTqUvpgfipNdGS7JTAYu73AnX0XX9Q/s2l0VtIM9X/uVlVWY/0CgYEAiTEolcgqj3MsJqVMD1Ro8ZA3O+qXGFWOFUZ053w0l/J52/xae82gFAVTjh16m3BPnqu4m+k915U/hJSVCX4tnyY28NI+6ZlSwv6IQmpLvtabnAjOasgNO53GwOdeZ3iVM5gnLXiLY93GchGO4xneakXpLtIv0XZBTeuEqQ0ag4MCgYEA4xlnp+GerKONdky5ThXKXzrxAPqvP8DbKETXc7nhccHkDrzHFjfbcdAeSs9dksbyo2VMw8nfnW3vbWSC/ZRyaDx38uEg8xNmbkr5rET81R5DSlf0zUlkpld5wD20ftI+aoP5QqlSMdcaEFzHzvWSowbrfnEbdKol02VfniAA6qQ=", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDEEH3wzUPnAbHJ/p3hwFbGbu+bGLxdGQrq42iprklRW8mK2IKD7vOv9nMc3i72nXBDeJWtmrCjSuQj0KI70+X0luyPWG4jCUDq39kOxgc2w40nWJwjD63mfVNiswkFBvmQ/ll2nxJRTW4b1qz1EcUNJwXNWSi2Lb+PcuzB8bEZoLpCPsRbiHkDuUPb3DLDm/NUtv3MLqUfPmLGvy1iN3UfmCdItRtapC5ZKeGiUakzjTHBzN39UnNRFHHTfMFcXt1HWx1bLXqru/kAZLgVVZ2yQJ+oQ0CZoiLgJcXP52LGWh95m7hvUA5EjNxSVGcwESXkJc2z8pda+UOJ6gZCcwJxAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmUEi89sQq9Bm3iNEEy77DDi7ZDb4HUJBfdF21wMYcGqoc", - "privKey": "CAASqAkwggSkAgEAAoIBAQC8vTEEHjOlrz0+wkTdN1MGC2LYlOly6bql01OBqf+gS1jC2UBd91ltJwPKLzDNKpGH0dayR0eaHKcfI9nj0tHlf5gPQxlDIULemrYS7SkJExdJ1RHKNLuCUjMDHQgOLtMm48p968YwRHgF3LBUb9pnWHh/tGTkpOs207qSP/yhr5yOwiyeoeOYlaUW5hXHfePpdQ3gzx1TTMKN620MQnaTqRCTI1dIbetKxre5rIXacnMH5RXE9jXI/CJk/+0DR7ZLsAjxjxE3S+OFENWNv/EidxkVguqOm3fukfe3n56j3hPLJsDwFSd4IP89VdtEGSsE2g0q+Enjpz6NCBwknO3dAgMBAAECggEAc6xoLCPud28tVBdwaTwNEDlOPXsWkK0bDaK1HVT5LF7BaboIrw53qmQs+G9vs26RfvJmaSEyiwtgib9JPU3qAoPux/vRscji2NdtG7BqY/tlXITPwGQNP9PtG81hMIAWPVGCuyYTc2WjQcR99WIQMyKPx4TiCRfiaNnfEN9SkCypWEHKMQ8wRXZxnV/BGb8ydDTY3qYs1tnUAR5Xnf33lemDUhkQAVkcXqfkN+Gb0Nam+KUqB5lf7cwYniMuspZtePiwpPidLWkhA+2FqGTJG1FN0dGg+XaTqTKDCjiox+qbFl3SACSt2IqL4SOoBq1H01Vve7LTeJemItNBozkCqQKBgQDk5AQJ+X5LGe2VDTC9eZQApHhai54eVA9uE0bo0yZj637pE3GI/d/4ngr/92HxroZ0SlFrlZHGPWJiiz36zuE3OCi4M2+SX/20KOeP4WEVsJ8iL4pF/4T+G7IQfbSyNoC6iUIHhuPjOqaovt4VBUUgt2mjzQppu9m8Zj13UelfGwKBgQDTF8YwSrPLjjQR5nDw4+tqiGKu5mZSCbAct5wciTpTDlaiQX+/T46WTEl8SE18sEWiRw2s/BBFBrmDNg+FBvmsjIxqrCQ/yzGXLatLk2Vpkv8G7VEt2do2kkv1eQsQui69JnS2kYuhfXqDfkeVOw8/UFMdEXudq5nDZqIHVF+eZwKBgQDgYNnIwWhZzNgHFoAiLe21V4WYFWfyiSr7GDCaCmuG5hNp/qJ8zYrimGNmGydLmW+6ziPU2DGn6QLqYV9n36gNzqK0N8/26Ny24KZneGQItDS7eWkOR3ci9xluaxxY228D7Yvp/wSk+xjnMPxaFOl4MfSAG39KuViwBHXa41Rn4wKBgCuPeGJ2x+t1iOE4wI21OttdEaAuA2digGksqpZo6xRAnTgWdBoyfKYfT/rJoNPePEBkkTnlOiZEYPvmqAU3j0ZAKqnIpCJV+AHOds69t+u1XdM8HchscE9amToqpFHrWcHGsccK+dl1X1bLNFJjQZ47ISuac/vxcWWVRFJm4uR5AoGBAL6P8mC1K8XwmKxSf43QWWi5Ib56y7rQEqihRhNRaZHHpfLDj1oRbb/OFYwOvqNK3IbCBH6LjqO025dRZoAKMmkVt0T+WqZp7Nq3zS6hmitZ/3hZhagaS5iBxDshdB17mtBPD8/WygDf48UNNeM8JbGY22WJ9lPs09G79TK8RQUu", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC8vTEEHjOlrz0+wkTdN1MGC2LYlOly6bql01OBqf+gS1jC2UBd91ltJwPKLzDNKpGH0dayR0eaHKcfI9nj0tHlf5gPQxlDIULemrYS7SkJExdJ1RHKNLuCUjMDHQgOLtMm48p968YwRHgF3LBUb9pnWHh/tGTkpOs207qSP/yhr5yOwiyeoeOYlaUW5hXHfePpdQ3gzx1TTMKN620MQnaTqRCTI1dIbetKxre5rIXacnMH5RXE9jXI/CJk/+0DR7ZLsAjxjxE3S+OFENWNv/EidxkVguqOm3fukfe3n56j3hPLJsDwFSd4IP89VdtEGSsE2g0q+Enjpz6NCBwknO3dAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmfFpRsArhLdQdXHsB8ft9Him69qZedcK5cEbfqvEN2atd", - "privKey": "CAASpwkwggSjAgEAAoIBAQCzE0FP8bilVeNMzjBZpdBGADuaAJlD+PgRA6xchdXIMh6dyJtHbQHFu4o+u0U96m1e5aieNKyNhaDuW88DcbYwkVicT0Z91LQTGhVVStZwmRGx0XNekcybU7RyK9+Zbhnd94U1wycSBj+5MelWEpZfxCKHUWB5eKkurYR4Jp40TqK4EqXpLxwR7LuT0iocOPQDJHRvkz++XSKKi+9AL2RkM3f0a1ldwV99k8Wya7UTkz5i6fz0xjffcbuu5dWW92FZQm5Nl0iUjZwoSq02u2Z+pTU7nQ86IroikE6Xuukkhr24EXUVw6gC5FYpc39segik76zGLfrTGMSh0/729c4xAgMBAAECggEBAIYARaJd/k7ya0mhDTs4Qhbvu6ntAsODfZW1yvfdSnEpWBG3+MJFBsuBH9z7Y8AGOVuGvVvNjMXGFfvnhYxNPgkv6j/lbplgXnPg08/kVX0ifcQzOIKu1Y3x4BiDTinQ4thfjTYC16y8MlkRyUqYVCBLc48QzQF40hjUzUjflQkMAlEkVbcE51OpKoHzaQ+ow7xInCni7PN9OoKpHnWYl3HfsY2cxNhFaLi3MYAPRJfxzdn8Ouav+I8na1Ggd/YoLtG7mFFNPFWAAkhXRVE5edrmhDsDbzUSf+VdAP8eaaaa7Lh0tIgznanN+KflLBR87QBZD7WFM0+SVFjfyxPQGqUCgYEA3IPpcHKsoOVryZE/zMNQseeRRLCjxErEflFRTPJy1ydvGxQP2imiE3b4qjrv5lQBkJmK5mPqC7AgubSaShLVQP5PlJJI0hUD3I5992PXkcjxmnTR5m48KMJTEheFwpyvLgxbLjRflFapIAwJSV/VcwtaeYT7VFYqM2JE0Ga/C68CgYEAz+Q6IMS9jNykySwj1sRX73poN6Qr4+zGdWD2GTAuECPTF30rDpb2xJrQ550rPKF6a0czuaXrLzOPfXhLVpy2z63A1hVVxeBypomCjhM/+zYgBUeoxfS/6Ga7yoTQmgqZDcQaeih+UbDbtPRBT8VMqh5p6NFjdu8TtCjN1toH3B8CgYBoNn8QAWHL+CBkdhxsrLFqIkHo8IG0tpD+EXgWoU3cmGpNpcGIHLzX7hW+fXP6qiDDMY0PLJDjTS1qFgwEjbnyqTz6vddkUUIt7bliPPEXmJt1n1fDSr1rlcqkdjFks5+mZ3h/8YhqFjp/RrDs2DmL0QXFAC+2v7HZ7ssOokAPSQKBgHTZLuLkMjZOfkCkkrBQQ6zS/Gjp2dGOcC3hhfG6ZumjeS6mp+DXcXQoIGtOp9K4YHqT1rruSzaIoIpBZvcTtp0caFrsOv2xnj+E4uDAaSHl1jGhiXdajdMuizbVV/p9InHeW5N11ypLYfJfp6YSm3izB4xYxLNAxa5pkOjGO8y5AoGAcPv7LKtI/y9dABCgxoqQx+sMAazwaeFL1uMbp7FEy3GRx7QyWI7zOzgIAZIUKw9GCEVsxFISeYDUSyYyMO5frZmI0mEN7h8/A13bVS8wuCPK78muCHLiG+mr2FJypNYMXQdGDjZRXb6L3wK0rFsOA6cSGV9dSQLRf+HqjX7LrRs=", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCzE0FP8bilVeNMzjBZpdBGADuaAJlD+PgRA6xchdXIMh6dyJtHbQHFu4o+u0U96m1e5aieNKyNhaDuW88DcbYwkVicT0Z91LQTGhVVStZwmRGx0XNekcybU7RyK9+Zbhnd94U1wycSBj+5MelWEpZfxCKHUWB5eKkurYR4Jp40TqK4EqXpLxwR7LuT0iocOPQDJHRvkz++XSKKi+9AL2RkM3f0a1ldwV99k8Wya7UTkz5i6fz0xjffcbuu5dWW92FZQm5Nl0iUjZwoSq02u2Z+pTU7nQ86IroikE6Xuukkhr24EXUVw6gC5FYpc39segik76zGLfrTGMSh0/729c4xAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "Qmc3Y2dKQEQzv754NMz2pkXCL8ReHCssg9Z14UBCcPQiRN", - "privKey": "CAASqgkwggSmAgEAAoIBAQDNJti1FsTPq4FmeUbyEoWkS+YHypKjCQBt/jMKLyyTXF+9mFi7sO2iIrmUmEO+mhfcAF0VkZbHr1MZwmIG4J8UUpBVbIX4y4aPrK4tUfr7/HTSuxP435C06z3TUxK6e8FK4zuj7e6Z189H5qt9ZlO59DyRG9u8PfVjx+qg8FYqlxGGfGQaZFstPMj6OWeGCHeLGlVA5SVPS+xjtkjrv56eFJ3JhfBr6Txv2CmCklTeQctplAwdq701XHqihDVLEWFrj1EhXGuNfq0EiH3TVE9VBsMODF36cY8vjKPSYCSq9Z31I4MC2BXID9ksYB2SE9Hq4bDyYxfSXtfisE2YPdjDAgMBAAECggEBAITWlpQLvjzKTOvRs8Kjg62zB6wb239+IK0YYGxDx5VTxxq5PxupoPXPjmNNhPAyTyjBg4Sn1P5P5HtVhqv1XoyGObdWohlLkEIQCmiGIQJxoiOhx3jrKoQ7nrjrncDqyWp4YPHw6wLq3ukrz/dO/v/1yhIb+9iUNgT6Ok8j0GeaaKQzoym/rcVq+JVVD+f7zGtMHl2QmplRRQUpNlBWBxacTPOO4PSHmp+xLMohwpZxLxYfX4GCKZtWG61SkIFGtnyvA80BnTi2FJcm6gtIIBlQoSeYtMGDfxWP3pKG9iZFseuOtOgbKjS7WGXcgKD87ZvVPN+DPoWJjWEWA2WFK0ECgYEA+AVOXdmoW0R6MifotlF8OA7eWFiiAJw16WtlEhe7JhMJvifEya2po8Sw8+i/QgWqI+N+pSITE4mYeOz32xgyR2i5Mk6qXe6UkTW0OjaAGaD73Lxg0/cxqxR21ePFLcmnHBlzfcchBvkWwNwd1P5iswmubHQk06oYF0S8IUogmKsCgYEA08B457ICfNNyzUINrKmVE8pdkUZ5SoZGyg5DMpJnmi1Y/Ip+Z0WFN8PQ0ccBZlYSeBySBriZoiIaZk/+Z+4OX+WCQ58xTcUFS7k8Qlg2hV+U4wkx8ZE1IBBH8lrF3T8BlAVVPSaTjEmYOn/EL9WPAjVHGTkDImTkQihSTZV28EkCgYEA5ULiYdZkzZjK67oAXyeLj7YOydOETNQY8Z+YWdUd5eALTX8tZM/m079pYs1unfTmhS4xTyvkPlceXgmOQzRmpaOkLWCSEyoKov/ljTn7x7ULm8t2JfmGLAJKpwRYrC6PDmZoX4fGe8+cvMG7wbs0ORNl7FKgCBhfFIMw9AS1hOkCgYEAvs4apDzE/RHTyp0QkVsl1/VrprJoLP0d4IhFiNZfwI/INZfeGtSMHBm4mq7F1h8M+WpVMvU4it5MB5FhXuklzseSP7i8xqUYBondgLLYPgpIsOPiOxhrVH8XNY0R6jESDP1ZN4cBQVI3d88VSz0WZhj3/gRfjKh4/hwzPXHHAPECgYEA3PEpJbRe3ezreODAIyUpCGQ5E+vvGJsPW6S7XHHlfK05l1ALIx1sTKFXRvf1FiRpWBqbA76d8CPVFcadws9ImyXzppm23pe3cBoDvEGUt55AgdGdd+hztSfRuI1pYkyKYCvbKLKIdqRHuYgooDWj0zhs+9jQZDrz8vSxwLFGLT8=", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDNJti1FsTPq4FmeUbyEoWkS+YHypKjCQBt/jMKLyyTXF+9mFi7sO2iIrmUmEO+mhfcAF0VkZbHr1MZwmIG4J8UUpBVbIX4y4aPrK4tUfr7/HTSuxP435C06z3TUxK6e8FK4zuj7e6Z189H5qt9ZlO59DyRG9u8PfVjx+qg8FYqlxGGfGQaZFstPMj6OWeGCHeLGlVA5SVPS+xjtkjrv56eFJ3JhfBr6Txv2CmCklTeQctplAwdq701XHqihDVLEWFrj1EhXGuNfq0EiH3TVE9VBsMODF36cY8vjKPSYCSq9Z31I4MC2BXID9ksYB2SE9Hq4bDyYxfSXtfisE2YPdjDAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmW8gRFnwbho8VpMsenJrRzur2oMDnRE9ZgEgXG1gNdVTp", - "privKey": "CAASqAkwggSkAgEAAoIBAQCwwjGfo5auOUnxe3jNKGS/L8McRkwCETpQXohTsYz7pXxBoej5tMJc6b8URDO4KIcmDxXUqBuO26GRheZjvehcRY9Bof25biMyPs1WzHImF8rHh2A/B1vmuTzqCknD4d1KHnGrvIA9DCg94S8cuYYCjDOBiiuKFFU/CzIWOv22a2xlfjTDvXKu/o+06U2TZHYkwkzQ/SwXlRJ005xjnWZat5+iO9L9ND5CSXuqJb56qmBMgux0XtAFPR3k9nDj6H2Wv9y0Mr+RKDAHnIErS/msCNaL0HchDAKc9utkARJsrgYwbKtWvJ+bqcpULwAf6oR5VHdbSuoKZ1Rc8hbDbOVTAgMBAAECggEAC6z4LCxZIq8EFGBsjViytvJHuBFoqeZLbM2hwa2Du4el2owAYKYxBIQoLAWJSQvcSYZLkd183IXjeUJYApSjyZyKpvI1WU9OId6GH8qna568tUta7y7kQixQOsFtN/Quctvp9EciTWYFLnk2bHZQxNBQAqmG0LshGmX56///jFIWFMTIidl7XTkIfnvs7RzscPKLm02bNxBTrGS33O1tv4+5/jdsSdShKMyzhr+ypYo01Iv1j/bWDpdzaixNqGQhrslhR/YaU+JfCXhY/22klzO0oTXMXlAgAHpezo/8EoNtRsEBH9x8L1/HXPqAU+ZpW0/eLpyfDiMtxN8+x+9fqQKBgQDgu7yIZHOUBlPFqnZC2smCd9uh29oTkiLhPd1YI1bpMClKTGAnWc7jiZsEuFo5+RM22Z4RF8JAwp2EOl4xky8BGNGlGOmoJsgvSx95Zhe39jsh9avKREVhBLmjPB9PngRO9KorQ+MkFAMXWRiof/tvFiMp3LK3vJ2e6jPS/gniXwKBgQDJWcDi6tkz81UeKSzh2xcp+Ezhm7X1iECm2lTto0B56hHRs66ye6lNY/KhFPe6XXePtavIvQn1xuF6Om4nB0o/O1bxc3bYftqYua+uqmCqr5OkzVndDT0AxKDoxfcDLU9ta8L7BoNgsMC8CwC9UKXboL1tC3feZ3BrOr2TW4YpjQKBgQDcfdWgTE5JsVuH2JNnTJng9A/9YmM4SG0IaVY+H44qBCK+zuiYMzkVbfE2VFnR/1qmuiSnyJPCTi+ViF7abPn1LZCjVyoI3OQT4rTiuxQSXffufccrEIixg51PVrGxv+uiO9Kp2FWHFEtkIPpceBUNDL87V1nRg7FyNX7bSHwSKQKBgDaKMU1F//+qcevxi07CYcvkji6uVuNjPN/1U/vqtJRRavI6kZ+XD4z+/cHURCYfGzu6IgYF7qS8cmcBXMUFnH70O+C7Pf32no+v/H57eCPD22JQnX7bDyMeH9fth7M8mr8w6WfFo+CVAB/vewvMxKBxMd5PtPBxZGonRyKbMAQhAoGBANBSWbIFkqachu1H5ewATo0BjvesEpqcZCgpKEA/lgW48E1oOn1bIq2UoJbZpAC7S3sSkF4ZtAX9sVg7wLFhWOfVeBosg6nDJE77ws+f2g47OunqsuNd9NB+gl0o4/jYvgERQLluVlhFevnHNkRYoMLI3D/uN4YRlAO+RAXjzp8/", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCwwjGfo5auOUnxe3jNKGS/L8McRkwCETpQXohTsYz7pXxBoej5tMJc6b8URDO4KIcmDxXUqBuO26GRheZjvehcRY9Bof25biMyPs1WzHImF8rHh2A/B1vmuTzqCknD4d1KHnGrvIA9DCg94S8cuYYCjDOBiiuKFFU/CzIWOv22a2xlfjTDvXKu/o+06U2TZHYkwkzQ/SwXlRJ005xjnWZat5+iO9L9ND5CSXuqJb56qmBMgux0XtAFPR3k9nDj6H2Wv9y0Mr+RKDAHnIErS/msCNaL0HchDAKc9utkARJsrgYwbKtWvJ+bqcpULwAf6oR5VHdbSuoKZ1Rc8hbDbOVTAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "Qma9mvZgzDdsTf1TLsNvDTHr8Z8mFdTDYNPFP7rt9DMPvR", - "privKey": "CAASpwkwggSjAgEAAoIBAQCzH9tpjgcWCV2Hy/ig1irSoJ5rsYxMeDTTQs4v32N9r1uVvGNDGhqeDOApP6CoB9f+ScQguxO+Y0PPum6GEtltkwGllsJRLsLZpeCIH/e+CxbNf7rGNkAGFk0k6kGQSllVlQKxxcCkeNhQll50/hDLTRNdnUydaIVdiYdABejBkVN7SXW/7zo+8lGXOU8xG1K/V67QXekFB4EhcLS738kVCM6qj7lCa1lH/u7H8UIhdoqBW/FToHeYpTFL8XGc6qnhVo4WF/V4eH27KxZSI6MTwC+2by0/4YN+x61dfM6eCUHF1dBVfAs420fkFpia4wZeC2wffLJMeIcvcs/IFIJnAgMBAAECggEAKQiCNdMAUo8AqwwRv55wHuIGiHsavaXHzCGApDzTSMZz/4AxaPzA3jXq3+gggH2TgEAburfAVRveO+bkTLhisJQ9i1ZW20wP/NXf8q8IDLPznE3HVoK09fAD6hHzxP8TKeTBwkGf2M2KGCPqLXjKFhho+EgBdgmsi3nmzsbLxBOI9E9D+A98HDqJJBI0h7JeQ+BndPyyugBYb5r156yzobTRnbwapGKfUbz7fh4EpJk1WgbFI+/W25JDyqhVW9CHr477t7caokibVFlJyVq4juNR+Cu9+m3gih0ssvmTeNoHuRaqO1AIVZU2ciXnlaWM6AndGnK465UvTyIsiAs1WQKBgQDrZzhsKJoF2VG4Ae8DoOs5/yzkk4vwurvw7ebuWXBHpdU1pIBUzBykcREJ9ilY5i7/QSC6HtbFgpLKKwl+h+rNSjOkt8RKg2ua1BpVs+Gp7L0gmxPVORtEgblq0vUkqalbwxQh/mJQPrfhKLvENVr6WMEZ1qxoC4WppNN82UNcXQKBgQDCzA18ZVzxfYGKyfDHkPT6erhvMN06oI83vKmnPKfBKtmTSOiGVf1W/zNgV3WA3Q2i91C7dzSQNtOT1wShawLWVLDYyDjbNRw0nbKbTDZhJDeu/nnQIxU/e7AaQxPs0yYiSYPp4sSdBgOdt9Gvltfbf3zyMRSTmxDuAhjBlDDNkwKBgQCxDmEc0OkQTyWs3h91PjrO04RjpCqUdQ9ZJscULUdLTIryHvm7Tg6ZDMYBFRqCWBevO8Au3XUy94QK9ZXdisNrh00SrnnAhdqQiMoJ/hNUqNCTzrB7JsnAnEXm+CcUXVwZvb/N1bUCoDnT67xW1r7IH6uWEKZ6V3hAYc4EULHerQKBgDBLVqyYlMpqS0uVdVSE47eV5VPr0W1PkTJIW+dSamTBst+JG9zyRLTk4F/qTv97zn2wwxs3GpkGfr4QeN1sIm/w30dfnHj8WdnRnw5RfsnmqMeB38FycToj+C0KpE36q2GkyEecKRKlAxB/GkVmKG4K1XdWI7vUngXkDy8vBkpxAoGAHflRwYwJNkdAvn94rzTCP1EkjkO45Q7n1SAeC0i5PSYxckpJEJlZ6NOCa0zLWY6HCmI/t6v3/i2v9nhJ1IcqlpRWycv8h04tjI+hHZIFw17/3WsuFdoqmYEupyqcgq5JptN8q8hWNA/EHJfP4eThrhcPlUWIm3+kTuvb7sty4pU=", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCzH9tpjgcWCV2Hy/ig1irSoJ5rsYxMeDTTQs4v32N9r1uVvGNDGhqeDOApP6CoB9f+ScQguxO+Y0PPum6GEtltkwGllsJRLsLZpeCIH/e+CxbNf7rGNkAGFk0k6kGQSllVlQKxxcCkeNhQll50/hDLTRNdnUydaIVdiYdABejBkVN7SXW/7zo+8lGXOU8xG1K/V67QXekFB4EhcLS738kVCM6qj7lCa1lH/u7H8UIhdoqBW/FToHeYpTFL8XGc6qnhVo4WF/V4eH27KxZSI6MTwC+2by0/4YN+x61dfM6eCUHF1dBVfAs420fkFpia4wZeC2wffLJMeIcvcs/IFIJnAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmaccPcfpmfL4LLtcRMotJ67Hc4iSCSAtEUpmvPbjdQUhn", - "privKey": "CAASpwkwggSjAgEAAoIBAQDSAHBObXQBN/HhtO4Xw2RWXgX3L1LQ58ZXReesjYIZfw2os1tK3zM0ubxJoPq7ICjRtx5jfrBUPwPNn81GxZlHp8Zoj6LaQlJL8peTsMQ+3rkJ+7cVTutcXT612b89sPXjl0NlNJMvThzi46vNp3dWSJ2PUc0/xGETTqw+s6xcj6PXlFM8qcDUCA/1YuQX0uSW2zHFrJ0tP/5O6dICr/eAtMXAtOLwkVUi8liPq3JDdZrNmceYdIcmzyYqArRCt1LKi3lmG3AogR7K/DcHo+RGFpZU8wS/ydhgaiiFZfOo0dEUgNFRFKe2lpUh5zvS65IB2M44GgXqhgmruJDjQa9dAgMBAAECggEAPOUbq+JZTTEn1sdcc0+ZfOHu4Oq8HQ/Yl94RfBvcqgAJue2of2GRu6xQSRmBG1oL/CQZj8hg4U0UkT/RisAp3nlsM03Tb27j5loGUjFj9scm6Row0OD9pt7zHFB0ADOcWc63IFXKiGEiRzi1zQDOvhp4deLGncMYUzzw/Y2kYYJPB3QnMl0kjq6m11ylqoy1ZZ+X6fdpFqLlZ1ZbNUbVDcs4roTU1jg/z7Uq8sx/MvHNO/Zd6CGOEJ1JRP970BQ+DaaMSzFUAdu2QRyvqiWCdt+kNINyzV304LQ5Y6yMiH7IYlUkNCrCG2mIIeriTKmBtsNQyMTa2jiSIAk0Y2ZKkQKBgQDrqakDy0aTZUgZCCbcKt75zsUGwidSxWiOo68lwijS6m4/hq9JG02YO1gUs6hsw8S18nhCCTSJcGkibLfTi87Q3CdVd6FIkqi9sUYwlplktHyNyNUigyowzfa6Yi7qCiM+lKy7BFuOoRXOucavdtw/CjcxtcgkVX/dHgU57RLrSwKBgQDkH93bj0nCUJX4B/exZlHxwUiwn6FE0/xYhdZckD9yVRKnt/saMjLmIEZVNdfHs7qXL63qqkC3QfcY4oQJgpYy6aQueCCceCjFlPTliNd2Z0ZdaV3aQEcKQCf3HEqzxQCSKTehI7tUmI1jzrv/uuWxsNCMxiMPxcveF1PoD72+9wKBgE8Kd4qzOjejp7vllQsRQfotVL4AjqnfVkNJOSyD46diQ5oA9XeitbLSbKd83oekXazc52LWrY1Pa6PFLR7B7Jr2zCaJWkn6DqiY9b7ENCynsILpkjriHVuDKTa4SZ3ryohp20lam87JzoOoobAmQJbQOVTt8HPnTVx/fidAkbDjAoGACr+1pHLL9uv1JQq7ERDRK6L/2dKrtqKGcWVdBF+HncuEZYK1wjY7T7yVk85FrJM7Z4RHnZcIFZp2GiYSMqCEk0GPCuF+J+FBio3KPEaGYH3dQumEEpSUxFbhizM6Ed5meHyYsm8MlJ/biahkE1irGgRKz1dGr6eSQ5S1z2lud2ECgYEAiYi26mKhBy4ASiNoCcmbDP7QJ1IA3A0P/zhRE9W3ANTgeMpYXgyablEGmbgIzeC+8sFhwp7WCJSoTQRf0ulEXUTfcbdcOKNl/6inhL7AMnZu5Z7XS8e5GQsGBrQIiLf1nwFCcib+JV7PUbenSDM/Tnnjd7dZ0RcWofON4ul1QJY=", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDSAHBObXQBN/HhtO4Xw2RWXgX3L1LQ58ZXReesjYIZfw2os1tK3zM0ubxJoPq7ICjRtx5jfrBUPwPNn81GxZlHp8Zoj6LaQlJL8peTsMQ+3rkJ+7cVTutcXT612b89sPXjl0NlNJMvThzi46vNp3dWSJ2PUc0/xGETTqw+s6xcj6PXlFM8qcDUCA/1YuQX0uSW2zHFrJ0tP/5O6dICr/eAtMXAtOLwkVUi8liPq3JDdZrNmceYdIcmzyYqArRCt1LKi3lmG3AogR7K/DcHo+RGFpZU8wS/ydhgaiiFZfOo0dEUgNFRFKe2lpUh5zvS65IB2M44GgXqhgmruJDjQa9dAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmSvhA77qtKJ5nrVBXUPjbUkXmCTErkoz1rhS8gAeXzzpb", - "privKey": "CAASpwkwggSjAgEAAoIBAQDYDTAPWDfUR226p8Ok/MKft8zNsuFfBbG3vk88lDi2962fYgwrVw5LwFMEg/yxTLEBJVP0s1pljY7mnZmZdh2rgVyyVP7HEWoKuIuIteN84JZUovqkgw7Ea05cOnwdXofv2IKxRdEaQ5onSLYFMKUR1Impmy7FyOjUYQb5CBlK8tyqHUNruU+wK5bkF1kyjYrwsWzQK5Cjhzl/PexD5gX2qD2r1dFncY8ikzGobDFU+CZFLXG7BaCN4mv6BxXxuisxp45LUVUtfNCMANZLqwNZBmADfMQWplwGki2CFMG7YVmTskHjz8VZNnTOWtisEy6epDZROEwSOighGscHpYfdAgMBAAECggEAceOJyRz02ScKFdHn1SoUokMuZ+R63y9OPpDIjiOIPhMT6Ce0SIhslcv9Ny0oYIIP8I2v0xdUeKIFiVXcqUPVYhogNjWN1Hw+jQY5L8jJ8YMmW9lKDLy1ZR83wHBoCsdRG0LjqfUmxBSMx1aR9Oxup5aFNu4B2usMqR+4oD//rTye0oTpbrBZPyT0Kal6PdF2BXpZ5yQWGGgHKnS/Wl/cb/gGvyfef8sxA86YPnZ4dTiTfcZiFBZxi/M50UDwcAMcWIWf4g++VF83v40GqCiQccsL2xl0Aep5vrqYsWVjozCrcctX31H9vIQMvoVFjFsd/qdSq0+oCvCh2vAOc4hpIQKBgQDurGbzZPeMokdqIQe1klfqhBQ7anvV9S345mJODHhXSVvp9FdCxU+avLA1Gl/cAFwzqByFp3zZTXD0SJUbUS7uVTAKtNkpG+mQpDW0Z8dQeudxHGi3mQX7ucI6jNKmBlS4t2j6BW8m8HzY/zHOpAnJA3vua/uycui4yXGiJFF82QKBgQDnvF7722EU44h8eezVjbN12mh7Plpr2uuIYivZgSxRgoQCY3cxqP1Gj+9cdvAZ35ZNo0T1KGATq48tPyymyd3SW3DVnRE4KyxhYuxG0jyaDFtX/jIiJeLc6nB+xg3/3sgBWF8dF5PlnCGSXUmoicjHA8l1JRK48r6Wr7OvmFaQpQKBgG65xLk+KiowTvlJgY4W6np98/Tsna7RJBbIquqSlnHIMsAC/0iWySt8RjMcnUQvVpcQcsr+vMkDSFfMJICb1S30j2koJWcQ7/aOd+vOCYWovx6Wk245q7DwqM8I7eDgJwXa8PSs+LgT8ZeqLK01JOUAnMorhoVvEdBIhFM4jiVhAoGARTHxJsEl5ufeDFUXy9iI+qrhwdMniscOx2WQ9Fxm0FvpcREkOTbdkeFOtsxo+0DRD5Ot9oo8zgLPONKBUbg7PSHCunYw+xWhJd808By8rb7803R6ocmwSQjT2HbpHTr3e7dYh0ZQCiKpv5uNb/7cbdiKoikUwxbwo+wI+mjBiGUCgYEAnq0mC5Ixt2ya5GGhum4KjEdaC3wT6gpwBzZ7PAtn0ytdVpQrp/2QfcfmjfANFuU/WpOfz4swxgntY5Zf10EQXY915llg62uSdp816X/l/QUwll8BtgmDGfSc+ohWhMZ6n4B0lBZk8ZokJ7QZ8DY4PofgCASoYEaNmoST7n0bRWs=", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDYDTAPWDfUR226p8Ok/MKft8zNsuFfBbG3vk88lDi2962fYgwrVw5LwFMEg/yxTLEBJVP0s1pljY7mnZmZdh2rgVyyVP7HEWoKuIuIteN84JZUovqkgw7Ea05cOnwdXofv2IKxRdEaQ5onSLYFMKUR1Impmy7FyOjUYQb5CBlK8tyqHUNruU+wK5bkF1kyjYrwsWzQK5Cjhzl/PexD5gX2qD2r1dFncY8ikzGobDFU+CZFLXG7BaCN4mv6BxXxuisxp45LUVUtfNCMANZLqwNZBmADfMQWplwGki2CFMG7YVmTskHjz8VZNnTOWtisEy6epDZROEwSOighGscHpYfdAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmbjRBWZxJSBsFhKsNeHhTSfL7rRcuZ2Qd3NR4Z1aiKePS", - "privKey": "CAASqAkwggSkAgEAAoIBAQDILimheFvXtSG32jx0B9DLy5ow+VjWqs9DBylaXNb6UTn3SJR7k3TZJVx983zgOWPXit1Q8k496DiNzrbvDcqknYb32KL8v+uq2LBOWQ+KsRRC3uk5ZGkO92CFBrmbGMi4H9fBeiPNbvMmWInLxDhLqlLtLQeB/JbqzbSRAdAc0JaNHiEsa5CtE2JTC2Eh8v1LZs1XugBN/wKe10nScDJqRDwPJdxSYBC18jxAlRLq5ZHkY5DZb7a41XUkNA5tjuh2WOikcbFzonLgesmFxhOOzARiRzeTAJ43EOrOrWwm0vCvHrgeEnuDHuKPIeEXAtBeYvIveCiWPW1UoAEIcbffAgMBAAECggEAYcLZpffnspLNIsK731apy71lUiGUF1JX4j4vHehVPO5KRs/1Y9yBpkKuxvwQsliUwAEbUJrlRyqP5AFeKaUsn/QmpAfyoUkBSPCGOd0Yz/znDjla4SJ+hEafppfAMVSLQhCbB+wkbAGRUdrPgOoVLC7ETPw+vGalNYq8ckzWXBtL45Qy0DEoEH0xCHON/unJORpLacPmGffPuaqrY7D7bGFQ7XwuwsaFfyV6jyXeQaM2XheaMwcNCoRSX0W48dJIwG36OkBOgvGDwwnR9hrC6qrnTwwnB0moJeXNea28Gpu4V61i30uDV0p8/e4pVB/VRJGOPPk0PxIPAM0xbCktgQKBgQD8NpHbCdHhl53wNkBecA7Ed2OK2RuFsbNwxREqCoXDwM7did4/1Tfkp6er/IsgzCH5QIuuaCsyhoIQFRsyd+uRrTG48qhElqoQF3zRFORYdNr+6Gs0vA/yg+nuPZ8gXZV3Ip0EAbRdTXXXB2K78JS+hBnYd/K9U0g63DE4OjAK4QKBgQDLL5g0YBfRdtaluL45sFduaTh6RpIxvskRky0Ob/H9bJ2v7EaJmtIzuJZoXT8oFP5/RjqlzwxNaQpksbzkBpGsedmgqdkYoQNm9LcYg/n1SXhS/3Rl3ZLJ8A2i6FeAA3h66dXX6Bo0pZ/sqy/RYzWSvL+do9E8KggzdJNuhhbavwKBgQDFfCUxEbtZnVJ56MD2MWAezi0PZ3h5cu9Cecw60wpygOJ57Z4s9VNSo0RTEugNwklH1haJdd99LH1jAmPNXMEDzE2Gt9qx+hcninydanJyIO3pcyuemzMRfeEKPw3+VcjXBC9WF8+WzzRaLtpMttCBbQafzSwwuqlwDUIs+MLtgQKBgEBjNLhkOygFoL+ja6ScXRh//4XAF1PsQYtwODb7ApRsdwvos/GnPjVlqUQpSHpLLNroRm2Ez0E4qDKAoHsiGceuVWi0ajeDzrAxnFQIfo1cWuTyTtB5Bqs3hxq4xgGrF+LbdwiUZLmKQsOc++o+pht59L7fys5mA3NK3e2IUHXBAoGBAPlDAIzBX8HsKVjMik5EuJKX9V30xWQPFx8LugzvHtkDj2n2eZQduy2wyQWkTY2BmG7xzEX2r6Nlnzp6iJBEF+Sa4Bhh5wxVjYs4/n5TmJfL2FiX+wOW1ULWwSPDsnWZTtVLk+ezezmgWV3Defirhr8VSct4qh/nk5c7z8nvkSTH", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDILimheFvXtSG32jx0B9DLy5ow+VjWqs9DBylaXNb6UTn3SJR7k3TZJVx983zgOWPXit1Q8k496DiNzrbvDcqknYb32KL8v+uq2LBOWQ+KsRRC3uk5ZGkO92CFBrmbGMi4H9fBeiPNbvMmWInLxDhLqlLtLQeB/JbqzbSRAdAc0JaNHiEsa5CtE2JTC2Eh8v1LZs1XugBN/wKe10nScDJqRDwPJdxSYBC18jxAlRLq5ZHkY5DZb7a41XUkNA5tjuh2WOikcbFzonLgesmFxhOOzARiRzeTAJ43EOrOrWwm0vCvHrgeEnuDHuKPIeEXAtBeYvIveCiWPW1UoAEIcbffAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmcaPaHp7MhjvWdTSCyuvJZ3H2WsudgBbFLDS9J31eZwSj", - "privKey": "CAASqQkwggSlAgEAAoIBAQCzd4cl9ugYvYGTbxgMF7Nj6GUKPvBafUzuyrfKGwnlwGtVirfmd6jpCLX+OknCS0TaxiL2CLtKK8z+RL0Ecq4iUG7h/D83StOnRlcp6x3fVODGz7jwRjdFivZw7Y8WZ29sadRmoLb2gFjmwsuV3xPKkx1mk/1Em2wblf2TcdAQSdr9tobSCvb2oZE9tk/k+llZ+X2WZKvQdTpiGNbFSM3+fuB+HO4qthxD2+a1nTgzn8qKvisW/opFpB7L7nHhJla/exgikzWThN6l6i6/spqcAs2N5rzFVcfhdzhUwTjIevt3070phf9ck2YugSfbE3p3CHAFNNqs4z+gLkDi1RglAgMBAAECggEAS+GYFSdGj19hMDNi2YoT4YRbZG+kNL6SDs1L1HqGPsyTFYInq5ygoJd8S9fdY/drT41DLwAWIJBQhpoNyZmrovqbR5XeLMTIpQuKw0CUSt+agrVFnuIxcIgHF0x6maB2bkJ4+kOt2J//9uIaLm458gcuATdFeQK2PRu4MeWHcbryZfl+gBNgKTttaHDYZVEKwCzkcKB3ePfsfTWdSybB34o3HLzeDcAnmn5dX9vL8l5WoTGYfuFYTzkHmPizVMFXT6xSmN5jyrlCj87LmI20LikE4F0uzJY6JD4tl+SuzMZd4/Z/EjDZSdnFE1jKHb7FzO/pX9Ibq/vT7hpp98dlaQKBgQDsngFwg6L4+k0SzhVMpry1ftEJnFsd+4Ypf+Mxfox6X3JWDORff129JHbyexC8PWAbnUbH4YRoJJ4x5U3w6aQ3y5oHHIFffeVsyO1N1RW77nZRCj1ZelAHcGuNRY5X3T91ad0j6/JXmZDfRVz8YQ1bFA/48ChvJIDoBELWcVCDuwKBgQDCKwx1/A4UmJN58oSt+EqvK9nKV9GRSs4D9SqEi9uixjzx2YY1jHMYbF7D8i6C9pQmvMI4+/8QoD51K1vlQ/XS5EA9TWHRRSeXLVUFEEtErDUUh+3om3sOVi1MSizPy65tLmyHPM0t5iACA2FHTFchXcf6eak4pWEa5N0rV6flnwKBgQCxKvHq/DWX9VqmXPZnyWT9BLKiXpd/EKj5A8/qbFXk/viOY+LPen+Gsvn5P5pdSBthMdcgrMRGcjydIZPFcjvKp0FyV66rAIo7dQryPz2h1MB0l5UuHT41A8EUK2OUeI4ebSDu16lCXDK0aqxgMI8ehhwbij7MUWnPz/j3tirSJwKBgQC34/VlOFZNg0MI13p5GRICXNFjJVDA/cunS+X8qkhVHNJTauQEiwPmOZx2j0MlnUoqddKsDV0/7cO5TFs4Aukp1ipQ5JyjiY85SiGfLhNa8o1C6ImVJsughFVaT1WpZwnHNZRrcFYSBkSCI5lZ4R8T5rGist5lW5tf0Sj2B4pnmQKBgQCqf+CCjTD1/QdMsf39ZRuu3BclKLPbDaxO6MZQAJU3m1wGjMl9UxB8T3+ZXhr5NTtSbYIOTyIrSpvMN4ZSA9sunkiU6Fxp3ac4WlWTJ6tIeGj8IuMJ+RG+SHl9YXS1yMUhddOBpl8CRF8lDsxbMEYpKqJuoww0xqHR3Dt9p95iKw==", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCzd4cl9ugYvYGTbxgMF7Nj6GUKPvBafUzuyrfKGwnlwGtVirfmd6jpCLX+OknCS0TaxiL2CLtKK8z+RL0Ecq4iUG7h/D83StOnRlcp6x3fVODGz7jwRjdFivZw7Y8WZ29sadRmoLb2gFjmwsuV3xPKkx1mk/1Em2wblf2TcdAQSdr9tobSCvb2oZE9tk/k+llZ+X2WZKvQdTpiGNbFSM3+fuB+HO4qthxD2+a1nTgzn8qKvisW/opFpB7L7nHhJla/exgikzWThN6l6i6/spqcAs2N5rzFVcfhdzhUwTjIevt3070phf9ck2YugSfbE3p3CHAFNNqs4z+gLkDi1RglAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmP2nWtwRR2TvwCxC3CjhRPvkiyV7bFc9MfHRU1fdCeBeo", - "privKey": "CAASpgkwggSiAgEAAoIBAQC0OKmKroyraUdXs7KMa/zE3q1+KDXXecqYeZ7iqOEiXs1KcRpDr85XN8vJsR3NyhFArM9b1LpH5LV0+dEW+zxFy+PH9BdhdYOqpeW2gcLhM+uZyfqXE/C19Cf0CFtbUOsfPsTQ5ODJ+5zyZlxhq/wL/zaj3DPCTrL0VEc++BJqLSsQcnkp+7vKkc2NQ1YPg4D/Y4JvH0Hc3ajzIc4xnooEVdYa4Zbi7EgVG2DLZL7I3TPS9nbE32ysmp8eIpjgq0s6hONynAOYtzhRPDVMF6vs/D3WuI3wvWPoe8mAupscmH1n7S7EBuLrIJ8dsM797J9OXf6+PZHzwbb1Iy7sJR7rAgMBAAECggEADjV1cICoiI8pV8nMJvQQnrjrtsmWzSFGDtVv6HDmJx6QUvEt3+5Jd2jnwUQclG/9AjtdseDIuwhWIh3cFVLDgsE7eTVObpmkQt0HimcapUTBq4NYJXcmAEJ6r+vEwCNWFkWNoOaarnIPArF9URoNKij59ttSnVw1EbxfTaCjWwmIsx3nrp/pneYlbvq3TxXxlbdsS+j7gPWDOhSeL93C0BBJZhsnJ2XC7TKm74dXrOmgN5oywOk2H6ms7CfHr/APSYfXhM0sqk3Ca1D05W0d+fOjjXFoMpjtEruP2LJij3myQ2Dt0ewSQz083MNe0MkStzdP7ucIUZhNWq200BVx8QKBgQDi1NCb24EQza5OlwmZfIMJybF5kpEy/SUvfBeCSro+u5g6CyhAFBu9npACFrdEDYQ7Pj2xTyMJa/w3PBvSuDo/jRZhZennw85yQMOcSdEUWrSw00L+m9t77vDuMc930B0HZLmLH++Erye3/fErcqKmeZjzcDVc3dFB5tJ10ielkwKBgQDLZXfPgbqeaabvbFV3t1sARMG9Y0Ekueq0PunIFnUuHFfe1DFfzD4RuTQdP2Pxugtgd0V2SAHl3Bn99rOYcRZd5KsMvodT/gK/xLKndQWRn2DkmvhtMdkNy28VKgugdPKt/q7/NTbrUtZdY2pxJh+JVxJ51FnMHi688msbNh54SQKBgCpFSH7TABFWkxYYNXTB7FWFnaovMxnSbPyVXngsXtrT8MFYVO7kEGtcwi9xdkObVToJFkwVmEzoL79HV1QEeu5e533NFTLYnX9TLGDSrMDjSmrtY72448ULuSBabfRA9zfqgF053VPXpEo4a5oSKddmL6emEHu25okmb6//Mt47AoGAdQsp2+ZKTrh7kNFliWOg4VGvr107cnfuINUHUNXjjqpOwnKXCwqMOUS7QY1l5QdrXpKkDUG4nd5/so5RoQqKlXNuHwJQ+7tzN4loSUbk8nyllEe9Z5DE19RWUvaEBEzoDco+R6wGs3pS0yDPctc+VJkfj63sErLXsHFLwzfsZskCgYBtZJeHpKStBoSlk8jviGe4stgiGpgycytEpRX/l5l5431lfQwByqrUwoxmSh8JIR3q1H24a2VM+Nmy4r7IGvFMudjGeN56hbcrlN5IZyc0GOYS5C8c61yGWESm362xIQZ74yNSQuqDYvV6w9FdV8rhVTvonjd1koJHtNNNKJG7hg==", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC0OKmKroyraUdXs7KMa/zE3q1+KDXXecqYeZ7iqOEiXs1KcRpDr85XN8vJsR3NyhFArM9b1LpH5LV0+dEW+zxFy+PH9BdhdYOqpeW2gcLhM+uZyfqXE/C19Cf0CFtbUOsfPsTQ5ODJ+5zyZlxhq/wL/zaj3DPCTrL0VEc++BJqLSsQcnkp+7vKkc2NQ1YPg4D/Y4JvH0Hc3ajzIc4xnooEVdYa4Zbi7EgVG2DLZL7I3TPS9nbE32ysmp8eIpjgq0s6hONynAOYtzhRPDVMF6vs/D3WuI3wvWPoe8mAupscmH1n7S7EBuLrIJ8dsM797J9OXf6+PZHzwbb1Iy7sJR7rAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmThR7tbPk579XSHjkYY2nFRdHZgnnBCF2vUDb5jB6w1bx", - "privKey": "CAASpgkwggSiAgEAAoIBAQDQLavVsy5kL7DhbHHYobrvPRRq5CV2iARuh2AFYxRWDEUcN1lX8AXXX8/mTOP7578lIo8azZnFjzLXhw+NQ4GJgoHecMHSrGSpK+4ZpCNptSy296taAPwLgV0/GUCHy+iZ9fOkcZN4UaDIxJbnJP7HBnidpVd4aUkcoofA9cbGtUzqL23hdj6a9NGlMR9bc49YZvqDyEncWkAuq/b+iF8kyMJoR4LMT2xBNTnBWtpqCqvRQKeVqQ9KWuOFUykBzyfP91iGBU7NWd8uhf1R/w6nS+ZSuHRAd2IJxVrA8NSQVQl9Dj4v2fDrTVqKmAlXR0x9OdczSeGHwPDXOX+UU0KXAgMBAAECggEANzHkvWQkiKucWihGhwlaZtPq9exHgoXNpwB9lPAQFEBskm6aYZZh9hiRJp58U+294DqpdpHMk3TEJiDJHssnLS5NAI0k1paembvsBSBfw0cl89z2sYZRTTufXXt0gIyvvyJW1uLGFsCNwK1e1SoZ4ur0T9fmuSYxHEZ7d82yRjyQ3D9LOtcjTdCTZGzsrOjAp1EAL5e4wPDo3T89DdoI3WnVftUFDnlo/inEegBriotYpfFVGSQq4GNUwXZnHk9xAGkO7VNr2rh3u9PP+f3w52wU3k0Td2U092btFmmVwcyE82NRP9r8MqZ+fMaBX/IHbYfDx5/DNxgQZ0At0M+aAQKBgQD2P3+ZyT+x/vzrCO0o3Dm0ULFr3bqhl0VkStW3jGFWXcuiI+kKQe1m+oY4t/nekJAVoHPuJO2iKPLjzSMS0m2LeKf4Aj/8as2ev3JXW9dGGaJWXXPgzdFB19ibTi4XI87qCN9ZBmncntgSD4fvFFQbBUa1xuvxHNf8IcKuuXV5/wKBgQDYbDd/UbIZB6N+HAZr+ucmspZUpqTfERQ+NypAMip4o1qqv0OKZ4SQAgvXfjo+KcKsGCMS3MtlhSU2Vm6p6rAeOqOKnaqMoxLQ3xznSlc9ZSlhmj+ZgslNgTCSOlLTaiITMod12g2admDCEWIpuXiBn1P+x8bp87OSL1OeKLHHaQKBgElPeDaZkov0ZOm4O5rZjZhgGaIKXgCzn2YPXXcKpQPoYrJ/zGZQYFQzK3iBVTNsiGjX3wu8FL8dP8qQDOwSl6hZIHCWguQsC9FCH9FgN0PYZ9sccV4xCCZ5EzSRXulmsLg+Mfg4D5Yt+BfQZeDIhY2R0Y5WjXG365lVl7ca4Z2TAoGAT0B5pi8Fd/L7JNAgbeRIRzx4nnETyPfZINtUpoN4WAsBxasakZFM0utc6MG5lE/4kMqZ9WtTNE7ojJhkF+bwLXGtt7H65VtGJaS+UdhAUCQ+XhZ9Gbrx+mbHoZSoBfFEnyEOx9JczuZwkkCJYNwhS95LhO4lYkCyzmJ0TWN7jpkCgYBHabtedfXEPrcUfclXB4o8gAgMbXYJXJtA7UqOigw/a0Sxv4aDuSyQCdagBicuGZ6waZrtzS9q9CHZi6ggkI2uBlpBHHFuYfFdQq8qFvG2YUZqRcTiDxFvOkSbspNRWi8CVMITZeVjx0PgUF3IPFDHM9hWI0LQTrmH5aR/r5oGBg==", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDQLavVsy5kL7DhbHHYobrvPRRq5CV2iARuh2AFYxRWDEUcN1lX8AXXX8/mTOP7578lIo8azZnFjzLXhw+NQ4GJgoHecMHSrGSpK+4ZpCNptSy296taAPwLgV0/GUCHy+iZ9fOkcZN4UaDIxJbnJP7HBnidpVd4aUkcoofA9cbGtUzqL23hdj6a9NGlMR9bc49YZvqDyEncWkAuq/b+iF8kyMJoR4LMT2xBNTnBWtpqCqvRQKeVqQ9KWuOFUykBzyfP91iGBU7NWd8uhf1R/w6nS+ZSuHRAd2IJxVrA8NSQVQl9Dj4v2fDrTVqKmAlXR0x9OdczSeGHwPDXOX+UU0KXAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmPbHJAYhY9wvwU8uct7PZxWoUBTn1bjjTiSqGBLRLaX37", - "privKey": "CAASpwkwggSjAgEAAoIBAQClxhN3b1UZdp2r5ge2RiAU7a5j0CQ02V4yXn8gll6tyWNOrblu70pZNpskGLvBFPlM6knavV9BrJwWCucw5imwkZ86Lz9phzPERft/aJWHu+vraS1OqS1TVuq6Qhhmq+fd05+0MHofnnsNQ4HgUnvz3t8CEpsadc1/JaxZEMnUolHf+9u2eBxemMQUr3/6vu1GQiXnzveMa9BBXCfKEYZt3VLDRLBAJXoJ/fE7AGyF1mVKVc2tcX8eaxUannc41lGUVgEuqDGqvAqSNwVBilsjsXYcdCEWtIO4v1ofJzjsX2piUL1apxbHYWdLiLcjk7BXXKFpjv3o1hJ+oL7NkmrFAgMBAAECggEBAJMjnv+xx/0T9ZswT8QPtkYdOV7KznhCP4PBsGECVwM173lUZXT73CgXediuQ2h771O/2NHYqIYoaVp/TvluMa7Rcl04trY6FU6vNy29bIvP1vVao6ZgLyT7ztiH9hSbnPCd9/D93kfWaS46rzqmu/KX7aVvUlBII6ApljJv3lVmV/UcV2cE3FhuQAcsyGdn0zf4m6utsB7It7Yi83yzE5zsghsLwCf0Fml/TtgasGZxgng6CwjXzReDDbA/pHITkLDllH41pEqxpVQleLvAKOs0Yw4IUgBGSK5xQVFBemJDik6PWM5wCV0pAbjMkpmdHxa5icv0NXLeO8m6hJMHuoECgYEA1sIwdrMEFGi97p1BJukrv29DolYrL7zSLI6nB47NR8pOc0yT6qiZXBsUaljYzyZ0Wrhl0LwaBTMia96AJ7NSuozujNho/RkL5e/XMTgl9kCeDO0W3weUIKXiVQMYfWu5zVUB5cov2XCn4tZ1eR4bjrKq9apGB08mxl7i5f+ss9ECgYEAxZu6qz7qvlzpjqZeDsNceSPX5u5X0CupxVx0tQ/+mEI6NnilKDDfl2rXtee4MxGDknQG1ztjMRQY2nCdflAmE435NLfv9ezf9o7N70ybhEkpec9Lp4GBJk/Opc91/N/h8n2RR94F/yYcW4wm874Ef8431ZEVm4PCylnAuUc8yLUCgYAxHGlOy7NUI3vDtGxwxIO/nGcgGYp4uTpq/BhQTyS8lRQJo+pzkCi5+mtZwoWaIZYcJO0LpehhZgcqGdC+w3BYvt/Sj666ql6hL47Lb6amwLIkDJfdWvNR3/15KWMRU3BC93yemvUESZHq+tYUY4EzycH0ugKXq08XsB09MZHB8QKBgFAaLnMYUAPWmf5vRhVp7+RTOUOtPf9uk6UjM1PqJeQGhJ5sDVbbaOdyMfrU8YASC2mkitlYg37zjJePquf3CVhH5ssN/MGNwcOqY6QrQ6c+GQf9lcdS4c1r8HKaRFO7VVX8vJWLVJb3FeuuRmPrlNtR9qQl6cJeiOmJtGvmiqc5AoGAEl4NSQcTnoS1V3fSCbYODYANvqdK/vOCIHY83fCAuztL4PmI//yP/9E/5Q5JGfuhaGlhs4/ponWVKkPrwntV0J+pEo4O+wzeki4Y/kzTIRSYNSEDhctYG3mQYbI9UvOQ9scy2DShuY7FRKmtOXeijP+AynQfA8t24fXv9tiiLs0=", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQClxhN3b1UZdp2r5ge2RiAU7a5j0CQ02V4yXn8gll6tyWNOrblu70pZNpskGLvBFPlM6knavV9BrJwWCucw5imwkZ86Lz9phzPERft/aJWHu+vraS1OqS1TVuq6Qhhmq+fd05+0MHofnnsNQ4HgUnvz3t8CEpsadc1/JaxZEMnUolHf+9u2eBxemMQUr3/6vu1GQiXnzveMa9BBXCfKEYZt3VLDRLBAJXoJ/fE7AGyF1mVKVc2tcX8eaxUannc41lGUVgEuqDGqvAqSNwVBilsjsXYcdCEWtIO4v1ofJzjsX2piUL1apxbHYWdLiLcjk7BXXKFpjv3o1hJ+oL7NkmrFAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmPx9N7M52T2GLn3xmNawQZWzFhy7HigYqNPCqU8F6LHE9", - "privKey": "CAASqQkwggSlAgEAAoIBAQDlZOKQHk8yuE1qNuLqTUblDKF6KUszKRyheVJ6Gmc35UXj/t8QwZlHF5VpOMTklWEYqYX1R0MD81esscEoiriaR2SN1hnsEVScm7kRqdhjHsZi68deiXkMfPA60NMLWxIf68/xASKq39XMYyr9MGMytQzVPqjtAThls0HwkMZAnD7uK4BsUVRbtdk3VJSZG4xJ/atbYPocDBpLuyGDSbbNqIN22E9dybOPyYo6kUvsRTyP7FrpcCRaSxIMoGLZT5feW8X8uqqx/VfavgKTxDbvuDVnZ0GdCBmZFbBB86hBprLuiNfoekWaafvPK/LJn1mtNtM4431dwz3B7iroMae1AgMBAAECggEBAK0AguYSFcS4vpnGPyhZk4gXGIlbLz2sWc1mBE/WLdY38Zfblju65nB5VtN+Xu/NwOaqoz6yudX25j516Kk8xbCE+08FE5O4FknuH4s0vt8yTIg6LagcodBLQZn599BupKKyY6btJkocec+lUryUi5uoc783fIsSCoiYwrg9V2dNgWkuEIJKFmX4Q22EHx/bQ+y560rKQGU96tkw9ncSBNge4tNrP1E3TFe8VeQ1gjyq/tYeOkJIsiB8xxwYVPTuBZzIUPm2vYUqV6YbGDH34mNZjlUIE6tEQwjGBMW5ancrW+rILr8LEs+vpkOoA7BiRZwcdeOjv0Zklc5aKOYeC0ECgYEA+k1ihyddwiHuPv7MdPB7lvtK6QbAx84IF77aWVV6XDUa2/UO3efQ5gwy6CRwW6nDjv8DLHFi3pVQpXdqXMdydqXRwKZP9I7gIU5blDs5UTC0Rd9GWPIJOa2yLjca9yYXbeeqbpvEAVhbBUZ6EdiXonUHyomiNKzv8nP0RkCq/r0CgYEA6p2ozO5rmLB6qNP64wct1cut8S4ntKykjVKxZus7DlfaWMxe6IN0vTwbvKIvM0/qO69HF/IttuyWBw+YHgNYiwsdmwtmJawR8t8I0ydDyBewIjcEHieKkCvQ0/jvVd+lAePS9PEhM9h3s8uHdliempCQz9BH+JhGg/E7n9lY+FkCgYEA0DTc15YMbLbyyl4CzudXtwCzkGE4rTuaCb6NPLBYxyi5few8AKSbZTESi338JJNzg5hnGGn9Fy/XVLyfsiuJ8F4Au6LccY8Dq1DV5tjY1cuQuWp/xu8Wc28j/0OBX8LEzHxfjgBuK7xGgn3cfsnPYKi+4WBZmD2enuyLboDOfHUCgYEAmmIWcoutB7ORc0jSPdQ6iAXYNu1NOWmlek1g6T1/BegviOEqzsu55NAJ3G3Iq3Y5xv6GxK4bANTbwFe1nIJNIGm3GJA+ril1QiEbmH6s7p0PzOPw9LrGRipe5y1WqGZbGUxGQ+HsHEakNg6G3AxiiYj5kZYX1fC17hquRnhqQDkCgYBb+u2ykhNqUaCyrzFzizBYFPFboUV8gz7V6reI2uxNvVXQXPAzLZS/jVeiuQS/HubGEwc/yj9PW+DWFjqYKFnVAaUul4Z7o2MeFsEz9jf9/jOihFg+S+Vs+hjTwhRRCfRdWq/8olp6DfuIw9usN/72tmmjUmltXepDP1VnxuSG9A==", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDlZOKQHk8yuE1qNuLqTUblDKF6KUszKRyheVJ6Gmc35UXj/t8QwZlHF5VpOMTklWEYqYX1R0MD81esscEoiriaR2SN1hnsEVScm7kRqdhjHsZi68deiXkMfPA60NMLWxIf68/xASKq39XMYyr9MGMytQzVPqjtAThls0HwkMZAnD7uK4BsUVRbtdk3VJSZG4xJ/atbYPocDBpLuyGDSbbNqIN22E9dybOPyYo6kUvsRTyP7FrpcCRaSxIMoGLZT5feW8X8uqqx/VfavgKTxDbvuDVnZ0GdCBmZFbBB86hBprLuiNfoekWaafvPK/LJn1mtNtM4431dwz3B7iroMae1AgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmYRvGtLMb93Cbbaa5N39rGVaMUzpzkKK6Pqpqr2fMvyh2", - "privKey": "CAASqAkwggSkAgEAAoIBAQDHV1dmaS4iKs8SiZ+/Pt6QlGG5mnfeu0wriTdH4ofhYvYg+s7/Ci2T9ok5jkq5RWv9pl6vq1m8Lg8LRWZu3NN7zRxkPE1CvH9knGbUdSYh+cHWHSHq6u4/qANd93yAl6mhkmX779KwDkrHz88uMHagrKUM6Nto43I1fvPQ+U+tfFFHned/OKXvKiuLbOpKkHdW0vc1vXIZRRzHGsh7OjBYFLLrKMGwLkAfKBmYVC4W9Gi9Uk05aSTOynjedsIZlp9Jp82BByTKn2dycU3DVivr1dHXQZhCJgPk+qhRsRMv2JcHJfsgVkWYg1p1/MmkMviaWYfKjIE13T0yjdU8pWhjAgMBAAECggEAJeSafqM7287bciCrN0WSNVWfhhKw+qwL/LKmyYlsXxHay8YhlyWuKFRTHZfI6JMjxiHcGfSuqDDxNylIIYbkxMHmxb8YyLjgVpXMjlJ+nzLFABilm+xwwbUEftZO2nr6Cfa0YEHkgQcWfAkqzxLzWfO3pE6XdsbVrQmm+3CJDuce3ltwIvitgq6ZnhLsjqS0eviYjucm0poHpNhGTHwd+xtmNAZcBQXTiXTMrkd/ppKPFIHEMdk680sbOwFORaCOXlklnZ/aZzS8PcksE2sQ1HirBSFCyr0uTDPENiAkxG4F1PcHIg4mUJ215/YNWKgT1Q4kmKRu0CikxSd/EiUccQKBgQDvFjHZ1LsywGuAyoCMlyqazgHHeVAf+7lg/bo4ZIUDCINrCuTYL4EWlS3xbBu6g6bB77HGm8r7bEkqZgnZ8Ekq4E0cLWKFBkSdL4AdTA0y9J81WARqhKucLanfYkkg7KluOwDsQniP0U0JyJkKtBelL1ixHZ7SpV0zHqVlSVD4ZwKBgQDVcV1J/VO5uFbDz0FijyofcuPFwR5A8QTiusn/PlFXFRkANno+LWoztHc3MUWaswbYwbDzXoDClnOxOhGPiOEz0+57bmipUjOtEjY8qPL9fvh7QLkXTPO/GnFeAAsg5aD0YIhEUCIsAGuRSk2RLmHXEq8aBOX3ddayNtX6yYeCpQKBgQC1DH2bkvhfKk8+LBrEXASrTa0TPM5sKdbrl7fY1GXVMjEycgFxpCeAzl8IHvGwf9lbqwNYfslrM0kEjliPbOI7UbeSytt8GI8E6N9/UAP+vjeB0bEmaGj7z6h/vJHcGNsE2jGMt5lMbxaDfiBGdrIhKIVlOiT3Jro459AfrzFdqQKBgHPJa7IXmrPFLExMwkuVHmSxDp7YhHD2TpAwhCPSyo1TBJz48JeKS3KBE6r9L6UcOTqc2EEtouvschZSSfRzbLeQ4G5VFrHDxgS9PG7rt+WMW3+BPOdG93NUBOvZWjAeYZIwS7vDPMZh8/h9NlbrsmfZ2uNihN4ZLr6+wJWrfbeBAoGBALVhYcxFkxxEKeDe8cVBML/c0qPJExMGGomDOtf/9QSIsB27DfFhO9juGxuDH4ejxTz7ME84ZU2JvdPz5NCxGmn5wGnixKojrjcl+0DKTpT8TuLe5/LGyvKs6w0BoX7WeVUHZSNjIMOUjncxGqR5fpJ6rD/h38jvSvHThiNbhL0S", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDHV1dmaS4iKs8SiZ+/Pt6QlGG5mnfeu0wriTdH4ofhYvYg+s7/Ci2T9ok5jkq5RWv9pl6vq1m8Lg8LRWZu3NN7zRxkPE1CvH9knGbUdSYh+cHWHSHq6u4/qANd93yAl6mhkmX779KwDkrHz88uMHagrKUM6Nto43I1fvPQ+U+tfFFHned/OKXvKiuLbOpKkHdW0vc1vXIZRRzHGsh7OjBYFLLrKMGwLkAfKBmYVC4W9Gi9Uk05aSTOynjedsIZlp9Jp82BByTKn2dycU3DVivr1dHXQZhCJgPk+qhRsRMv2JcHJfsgVkWYg1p1/MmkMviaWYfKjIE13T0yjdU8pWhjAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmbxaZzmNDdW3krQy9tQQhE1QFuuoRrvYshihX6QgNa4Xg", - "privKey": "CAASqAkwggSkAgEAAoIBAQDBE7PBwQ1+5VT8oPmS6d8bTPSBMinWrG8UJ28Y8d/Lf/pjz2iCu2F88FRtiOcqkqhGU1jyj8jAwRX5YeQF/OjrMYKFv3SGs/mmmWOwA9NPVIxAI+MYDsrGZSCyOq7bbEwXByEgcvqTWbF5mpTb+n8w81lhdTVQ0pw3m5EJHKcVsi6u76QvQ8CT5ZXyF5fgNLQSN7Yeby/w2AFkNnKbtN11ZOVs7k92AxxwH/z5iniJlydY65jsRQeixDbUlyJpfJnyx5SsYe/Lxc/rcnhqirgTXlDhcjAiGPiQqlTSquxCacQ0yWauqJFTLi+h/KcUBK11d0nge2wPufelqZ+e7BRhAgMBAAECggEAZ/SJtmqRL5+ekJ7DgXx2aaaXhvBRYopZDErnIFEqo9D2KcNEjA8DwFdNveQWQu/Ptn2tyHvuJQpRIIK6WRcA+ZEgq46X2OcSJcc0y1Jj9bSaBvbLkOp19zf/0LaT6wR2O3fVODlv/OIwEj9OotpOnTaJC1YmLKwY/D/AaV2KAL2M2gosgcZyyIZFot1AenVcoxM9WXLHeY939BNWajyOHqFnRqt5CW3NKJVukM1KKtn7doaYY/63t1RMhgMxyiqRZlcw9iCzz6Pd9Ppv8n9ZaboHwhovHPVT6jbajxff/E+Z2bD73kxbJQ26SJV+jsnyKWkIyJFQSA1vb+AHTZBUgQKBgQDy1Pb9AimUoK4go68Xl+sB/RgAdPLBKjqvQoaj1PyXdWNzlZEegc7et7ZlZYXa0L4JSWq2Q5g0K/fiMO7/io5REE1PFBNLwrZvuR7AY2g0Dx7h5dQK/rC188ibSnfFVOh6uD0YFzSaFypwISP8JA1ldqcujeIykni5fO4WeO2sOQKBgQDLjAfXTmVQn9HqgoWODfG+F/f+HrnYzEf2RaXOiF0Av+sdTjIOG95LSM/OhqBYTiA+QSu4MsX1nsmiAR4lw1DPj1h0P+K69W4sLA5L2Viq7LO8JWgwJKMt0sQtP+weJdmyAdht/xioiVUF/ZrgN07XmwTZZF9hrA06Z/En+lX5aQKBgQDwImwRLZdC9FbdziBzO3daIxgeM4hwPzuDX01YLGKRwLNVdP3qZkHV+2SzBt+E0NJsyp5tmZClXymmE+/04ub0ASQCZH7kd6wD9dQUOvmsKZvHlojHSrAjbu3dq5mfmeTAnvtDnIcXLnt4IT29tUVOJjUTk5mxmykpfQLRVErs+QKBgDZkeB/wAiD2ZFj/ggMA9O2waAPPYChwBnboC7PSOtAdeQ2+vJ+KkO+bSHTPAwA1+GXKco1pe/7z7LvPAqhitjCRBLkj7Um6ljNVnohkT051rF4FvP7Ie5aeMPBKmaVAxhjMZ3KVbZh0AnV0XLO38+inszcInHh0SqCl8AqX2eupAoGBANPWOjAv98XqSJYimaTfTSs/Fkf6/xfo3bUOsarittbjnAz3NHqPTXYeSrgSSQC8UhzkD2aL4CA59A0ZIBsw9k1j52LfZ7+PT8V2JTXJxzfGU1bBqyyxJluIqoEn7wrQTx/n42RTBeIDuP3CKNwsu89rZTWzsAZ/eDsly7dSIaLQ", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDBE7PBwQ1+5VT8oPmS6d8bTPSBMinWrG8UJ28Y8d/Lf/pjz2iCu2F88FRtiOcqkqhGU1jyj8jAwRX5YeQF/OjrMYKFv3SGs/mmmWOwA9NPVIxAI+MYDsrGZSCyOq7bbEwXByEgcvqTWbF5mpTb+n8w81lhdTVQ0pw3m5EJHKcVsi6u76QvQ8CT5ZXyF5fgNLQSN7Yeby/w2AFkNnKbtN11ZOVs7k92AxxwH/z5iniJlydY65jsRQeixDbUlyJpfJnyx5SsYe/Lxc/rcnhqirgTXlDhcjAiGPiQqlTSquxCacQ0yWauqJFTLi+h/KcUBK11d0nge2wPufelqZ+e7BRhAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmNvMtvYwxgzJwinp9br4AeNSJgtK2XD7AF5YECR72R9b8", - "privKey": "CAASpQkwggShAgEAAoIBAQCd+JwQZTdHX/3+YyD9DB6i25z1ObF8j7T/BkXF40eX0AmIf5sYjGBqDnj/Ybdnitq0qQY6NQM43fKwHTxjcJ15z8rr2T0jXDLftUx80Szs/WOFDeVjGkj4jMfEJTHpNstrtS4DvAu7zj9VOzuwQZ9dIgitmVDup5hDFlwXneJnMGQfgOBoQ5+N0pcpHmPhyGRiUUReaNP57uOm27cqUDRR58aT1vdmIW5J62UvktsGjPgA27kQbjj2yEi01fQux088pKHahmQsvbIYPlMYoZ8k36zmY38Qw0RVwaOcQC8mDLE4+Qp3JJg+uXL62b1a/XSwlNKzEai34RXp6aaRaHuVAgMBAAECggEAFidyg54eRYVB0rZGPxa/CSnxdja0HHru8EEJ8fmw5aqIW7tBngy5zMXg1Df5B61ihKmbtPgQTp5Z1bcT7AI0I4wvsinSOC5K+DKt2mdffJEArv1G6UIbb7gWn/xzZniHyMAtBtsNbjY7jZF0CoD5f48xVl9FCWM5qFbvbWR4Bu57BFrvXkdMrPpO/efcbmbznKLRuDK7DumOdWTz56l8hu98yGL3Ve/28bs5trNGBjX6dvkHIiEA2EsFLU/YkyVeFYjDvHJI8GYYkCb0yYbmbke1Rw9cdTNMBxaqd7//q0SMqPM0hMYqWa8b/nGMoHF8DjKVoipIXUoH7rf3a/IdlQKBgQDJLxx7mW/InrbaaWU3Myq7ugo0pb8w+RZkTDOhWu4DKsU19GhMq4NHcOTDfuEk1S7zQSV01sjBhDuvRmT3IkW4GEcHhqrMjwU6cAKVFvyn8qnm96X4QwplD/qcYuQlZ3osUn9J/pTHUasQvbJRc+5M8l1h0Tg0APYuXNi2cO0yVwKBgQDJA1VxudXYpzgmB099VohvJtTE70U/BQ/xcVh41uSfuAcU37mLXmo1ZUpi6S5c7/CQYQZXhe7OHmh14Z1AJdOVjqFcyy5OUJ8fN0YMtNyIwK0NsHFHDz1ymyDsXKL/JxmN2uuTSx0rF2iGrvm2U68hg0LWcUTCk5arenaD5CoF8wKBgCd315Wj51srT+IPVSz8G8ESYVgswBJie3MXw/U+unzikifgl+maqDmGu0pjBNZOAFT2jdubG21jfLYJEFuvXJAeKykd0ToqQLNTMB6BkPV91LkcEnJe7JYhCWBOwkVYRI6XbKNej19+9RlmranvHWv5DDrZabZCDgnQay93fgEnAoGBALvMto567dTtXeMBn31dVDhskgqv9QUMyLltiRfUxWKHf248G1CfVCEw0g+ZBazkqt9pFpC828CM3lGMCOt+q7AlwpI8bbXTUubKMFL8wrGtOcD5YMvf7CvfzSGm5s31jMVgjAlf+w9gXlK+tSRoCM4JoW9SAci8NN9emc1dZPmLAn9dvNnOAYPPl6I5byHgT6m3qOq77WshzfpqgRUCxQ+kSKKplwUZ84T4+cM8zsEnxePMlPdNaRzKvkHzxPDSSAWiwtPIl7hPjr5cnNoj4K0rokU/nkq+NiZ7pfEFroajOzBJNMtter1kcfGpWsjuCM7DXt8VUgN4JrwJsKxwYFDC", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCd+JwQZTdHX/3+YyD9DB6i25z1ObF8j7T/BkXF40eX0AmIf5sYjGBqDnj/Ybdnitq0qQY6NQM43fKwHTxjcJ15z8rr2T0jXDLftUx80Szs/WOFDeVjGkj4jMfEJTHpNstrtS4DvAu7zj9VOzuwQZ9dIgitmVDup5hDFlwXneJnMGQfgOBoQ5+N0pcpHmPhyGRiUUReaNP57uOm27cqUDRR58aT1vdmIW5J62UvktsGjPgA27kQbjj2yEi01fQux088pKHahmQsvbIYPlMYoZ8k36zmY38Qw0RVwaOcQC8mDLE4+Qp3JJg+uXL62b1a/XSwlNKzEai34RXp6aaRaHuVAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmXuRygXg4acMEZbuDcTE2DKXSJyMbsJ16Bg1oKiFDnCby", - "privKey": "CAASqAkwggSkAgEAAoIBAQDRMeNG+9qg0JUaJpJYnsWljTuIJ47SfYOhQ8SKsfBYlcjEAvWij2F9KKo9BpN2oI2dqLCDL5ArGJ+33Dm+e40WEhRetW6Oeev6hgh01CetNVgGS8LseeaTzpWN1HjslF1UWCNs5NdKVFp8WWhIELbyfnCjymxafpRkczckfUMcbhvAJ1xmrKwC3ItumEtb/GqxBln/4be0O4aM90qPnXBDIbQ9ddWL/31USL6nnbjy6t7AOie7pXL9Bb23tBXcsw3vYprGGCDcluAdMiFt792bpF3HSZlYhh5Gq58ac0YDoNWUL4A4b4FUb2YGlmX6qIqT/3y1x9RDZ5ws8cpVR463AgMBAAECggEATQRQ6JFQrGQegMIynu3VVl3ozPfDXTtYesa4VVetZO/AOmnchTzEZ4/RHSaOo934RVMVqTaZnUQziT1LBRX3m2iMl1G0oj/A4Tr3Ygu5j8tT3P2HhghbG4+y/8R5wJ/evG62nCCkInlr1twTyHRe5mgmkCa2PZrchx7j7ksvqgc1RxGd7T6IjcH/ed/5bMMUdUhR+IPDfpqJdATu2PzxgsGyfY+INPIsd4PfJEUKA1WIqZL59ArD69v4es+jjGQ4Yjw2Eo/wBhTje7FZHucZt9rrIrTeoqa6syAMLe3tcOzxelC+mwU3GLg/SLTByKBXvHWtPkmz5bDu5wwiu/s6AQKBgQDqL++xlpYWdCpPG6AmkxC8QOYaxqH61UruQ8OAnYvFbZdDGbTreESun0zqKVjZglu6mPsgQfRIvZkyw41mAOBENKGwHFyOUMbftQIfD/KAf2ysT4csDVd8DD16CPjHbPR8xfc5aPo0RYGSHnrOjeFFrrAOLZx1SIrktz7o3T7yiwKBgQDkrgW0VOEMQof+kyUYbUlgbciSvMDMdrovjv3GO57MHEwVefiI7RVx6h2Kuv/MVmu0Hs3uRrauwtIDlP+oxVUdLv3F1nxk0QXiWmK3f/CKRx4ygvay/FsjpssubTZMsOBEslr7pqzKXTIoGSA7yBZfwH/haxEI6tp2BFnScZq2BQKBgD35B4ZIYll4zkV2+w+aNYCL8Bi/3deiIB0jY5Yimv1Y/gFsyRrTDeHkGBeTb4bH33xmxXYI3httyR/M7htDOhXyk6MmLjwfFjHXFcOglbz5e4mx1gSLV05lctNbknI73As03DKeHDA/AIXpePg2RZoKG171JQVIeDEEaSp4ehL3AoGBAK/hHCgPJCuOvBPTTjOUUlwk85/QJqTbJ+XOH2aokkC//tCBx+JgHh9IBcKegoDBcwLMsmvx3S1aT7ZLkbpXU1gnvSy9A11y2gi2pbgmYXWorxQAYAdXSi2Iajrh6mJfo42Sc6GbFshpl1r5wC3afULVxkU0WJy4LJ+aRw8xKuGVAoGBAOUPMQG+tf3Veqay0/4caZqqNvS1Mve5S9pOlNXoBhWnHAFOuKAgKjzMySPqs58giVMCFDDi4ZsrmDECCzBQ2/LF5w6uLnnR5GRLw2wAGYYyGXViKOCunbRmVHSyM7yhGm4URobU2RJjBLULGtcZ3dDQzV25EcSLxaWWLlQLOlYC", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDRMeNG+9qg0JUaJpJYnsWljTuIJ47SfYOhQ8SKsfBYlcjEAvWij2F9KKo9BpN2oI2dqLCDL5ArGJ+33Dm+e40WEhRetW6Oeev6hgh01CetNVgGS8LseeaTzpWN1HjslF1UWCNs5NdKVFp8WWhIELbyfnCjymxafpRkczckfUMcbhvAJ1xmrKwC3ItumEtb/GqxBln/4be0O4aM90qPnXBDIbQ9ddWL/31USL6nnbjy6t7AOie7pXL9Bb23tBXcsw3vYprGGCDcluAdMiFt792bpF3HSZlYhh5Gq58ac0YDoNWUL4A4b4FUb2YGlmX6qIqT/3y1x9RDZ5ws8cpVR463AgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmUFeJFeHPBFxQmAuiXxajjywc7UgEgxMfqoDfL4Bt6jGX", - "privKey": "CAASqQkwggSlAgEAAoIBAQCdqL5vlmpQHlWrkpjZiz/hZq3HK6PKTCCy5/q06uWenHU9aPsq4nCfCR8LGVciqucq7wker+0WLmioE3evR8qYyUHnkycxXiEaTFSymSk9DhCtb3PGORDzmdNRlCndLmDLitkbbQzmD/qia1v0uxRayMDZn7qFsmI3kSgLmyv+1GidBulu5Y9DURgrWCMqpZBiTUSNy0SWj0u4BC0GHDPDmYZNHBhhilJqAyYNCE+74N8UeJqkpQalx3zQuXYg9Tm/OfyrRXWJPuETECU2rORtKNXqEK7iTFllRbh3nq1hLcFTVcgq7qNXUogMu3c34d7EPeGTbNOBSG4pE4x3mBYdAgMBAAECggEBAJwS5qM09n3l6c11zJbfgRe0PChFjVnAz0YM3GWpfDLulCl8+dhUXkUyFGc6aMZLBZm9FPwqELy6qKRq0TrWCTwDUJjdVhlLI94S3m4HrYlhmST4hlYfPCbLiyThVig9t1kIVTEPXYuLGgUb3uaBJP9SaYeG1nFwTEbSDiCfNoiHVFJdp/3AXUyxpwd7q6dNoPcquHsU9gE420LjnU8+bD1aYez6mkRbZ0pOoLHq9peEJw27j993Coidfr9DOyXLM28AmCWHhOrF4MVAim81pmhzy1hamfy6xc5ye3IsnQdyMt/GOjH/e7jPxwWNICIfx/j6RXaa4KKMSZNVNcGhX0kCgYEAy811aJ+A95+W7FkIjd5uFB495jY33d1j36CU6FSCL0tmiJh8ZvD9XaJCW8ebLbPzWl59s/LXVs4oUKHauYecKgJhCE8xhn5vrOVzQYdED678LDS7b6+4tGvFM+bep7gKyuKu+g2yPBz6oS9Ggui48AjgTuFcgcs51l5vVDk9MTMCgYEAxgnXTd0JTfNDp3j/UdvnhnmBDORr+k5R7JHrNxO+it3drylhQ7ucNliRFqk5/hMHTmyPkzo9dSEN42UF8voXX6wZ4hkD5V+qhFaEt98LLmqPjBMEGEUwujDQZf2Bf8DkzFZ2Y7N+8IbzTnhtIIGCbq13myr2urJ6ePPBG2djO28CgYEAjCJHM9xRKnNSrEsABcTG/hBZUZ1ARs7+6HqbSTEqnuiCpTPsfkAAh0yVwlP60K8miqHkX0KAbRCuSdsw8VdcusoN/E+v5yGzGjhfStR+qSYSATd1FnPGVlCwNWLvAHYc/apm1EtsncbzUreWDVeGKo5/5d0x5ZFewJcIh+ofuF8CgYBI3TwTkP0oahX9W36Nbtyr1K7PwIeeDA0GftXNaP1VeLZlCVOZKUEbmdCgRtloizXH/BeDcw1DuEq03Omoca4B7H+FefC+B0nk8TRZtr4VcO2p+yEpkOORzf4PWIu6Jo3IRRPAMT3GX9DLkXGNYTlNYZO9SryHCr4XHJBzdcHEDwKBgQChVvIRDj5TmmO4srMrsMG6BUH+HrAV13Hz6NSKMN2RGH0WwXNC22EYCEWG0liSNRCopJnKEnzGvJxgQiDY9Lph8KKf6ibv6u70J0d+K8Ae7nlU4/Oa1pc9FcQUW2OrIJ8ceeAsOpOikUuGrKHwIwh9zKJmaRLj/bufnBzdG3nhcw==", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCdqL5vlmpQHlWrkpjZiz/hZq3HK6PKTCCy5/q06uWenHU9aPsq4nCfCR8LGVciqucq7wker+0WLmioE3evR8qYyUHnkycxXiEaTFSymSk9DhCtb3PGORDzmdNRlCndLmDLitkbbQzmD/qia1v0uxRayMDZn7qFsmI3kSgLmyv+1GidBulu5Y9DURgrWCMqpZBiTUSNy0SWj0u4BC0GHDPDmYZNHBhhilJqAyYNCE+74N8UeJqkpQalx3zQuXYg9Tm/OfyrRXWJPuETECU2rORtKNXqEK7iTFllRbh3nq1hLcFTVcgq7qNXUogMu3c34d7EPeGTbNOBSG4pE4x3mBYdAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmThg3JJ7BLnA5GVm6oxztaL2iq6k57LL8E37i5nY1JFxj", - "privKey": "CAASpwkwggSjAgEAAoIBAQCt7YDoUaRHlN11UD1G8tSsXoyJQp7PlX7QJIkd7kCeZHp6MwT22ATBjXQlGewovzoo4Mod7g3NjbCGE4Todurk8tSAq8d2lVZeEOrQ8aaC0TG58JP88FzIOzJHf3SrJCqo3uGQ37tlNYFIGHnS62/aWC7vcZLvBzZw8qUwbXaOjqaB48Rz92q5VbGEPRReYsDG4j8+0EqJaw/dSv7K6xgNiNgMSHioVVkrk/oYNssY1Ue7BEjUWhJJaDebsomZk3F4w40Ycp/Gttq7krCTmmdT75Kw1MVVaSrp00ORE66TJcS+i8PYAOe0frzuxfUmQMyq0y7I8LlrEwO2doSa7h0zAgMBAAECggEAOMI3/hiefsmi16Teymd2ZeXZAPYfs2h64Nv7bywQJGBv468AoLlwG+XYkD78ZXO6PBrXepr0IC9r+uUly2L7Vsmz9WWZiyZC8CGfL56ckzZHfwF2meWqsaE30ENUxIDh9wf9HnUUx3uFfAyYvO8eKmf6sSMkKyL0bjmRFNO0C+MRmNgw5bpx20KQqchsQQOlECUXs5g5Ro8a4szhwr85mflgR7kdMwW6pfNoBZYmJs9hHqXVLyk2y4XW8xPYOfgI+sdMnyUq0HYttJnlTnG/HMLwP4U+Gsxm2H+wzDqSx4uqcD8o7oXiRy+elvJs5hn2Ik4tcMzj/14kfCoKhter4QKBgQDfpuIrVOErEcwVdaCP+sUCq/d9/TK+MkAbUmNqVSagdarhZgI/si2qKVoOYRo1dpnneaHdHR5v5Eyh59qTKz0SGmmYqyw8NoyrEdFpSnzGSWddCQWM9NTLnUc7NQjY6SLei8XrQ/JtsZ7kqnhb16ItGYOaHLZLnGUvNvgC2saDwwKBgQDHFX5gFfxWvGVgrqjkwOMC7Cw4ViUJc86jXSuiTTnBhFILXkI8yk9nVjrzVAXbWMpSPQQZnUcHvDR9C6g8K3hwf+xv/6hBVW268oZT7wecGnzBGWXXgSTB30qaCLcqPe4MQGxedBAw0ISvT6BfWsVOotGvO/VsyWwtT5iqLJ+Z0QKBgBfLzNKpdE+91AYQfuXy25VeMLYSA50jAZkmmfdNWg/GlUjoLqMSVTN+tNtEz6ISnWt4kJVTLNLg6pprbeEsv5G2h7e7trgtYagt/CcEyuPaGYpXlGScBCwp7tNI4Ekb/R7KpmNS1m9/b5WK4cV72wCLb2otVeQTntx4L8k199s7AoGAKdFD+F7l4Do2eTZ214YEqSp+p17A7NlcgEgj0DW0egeXTDgCZc6BG02rmEz/5fEinl+eqtq0ftVzmQiH0Au5grf8LBJhf0e4gtpKiPreeFW/+rehAsFnvSlv/Cb0gnT7uasWmEh81iQWmtR49U6Vv0zICqzngnBUvrfHc4doBuECgYEA3JvoqCVOaBTZ8LYgZrnFPjlVRKvfOwTjK68ezxfg8VQZBrll/hM5rLjTEN+AccDB3a79cMV3FkXAKV7Wf+f9FFoMjGMtUwpicov1j+Zm6nlLd4B1iQyhruj+XN170cmxqYLJGw5AKRPGa4VcWpvJTOtF48pO4WZ+riKPRYjXK+g=", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCt7YDoUaRHlN11UD1G8tSsXoyJQp7PlX7QJIkd7kCeZHp6MwT22ATBjXQlGewovzoo4Mod7g3NjbCGE4Todurk8tSAq8d2lVZeEOrQ8aaC0TG58JP88FzIOzJHf3SrJCqo3uGQ37tlNYFIGHnS62/aWC7vcZLvBzZw8qUwbXaOjqaB48Rz92q5VbGEPRReYsDG4j8+0EqJaw/dSv7K6xgNiNgMSHioVVkrk/oYNssY1Ue7BEjUWhJJaDebsomZk3F4w40Ycp/Gttq7krCTmmdT75Kw1MVVaSrp00ORE66TJcS+i8PYAOe0frzuxfUmQMyq0y7I8LlrEwO2doSa7h0zAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmPXXypPBfaZScvDjqtfhaAwnufqgBdva23ZS6vFnpRE8H", - "privKey": "CAASpwkwggSjAgEAAoIBAQCoasuDjj82xdcy8Y4SiDWVph7YOC0fX3MF+6NvmixvWiUomzN7Gvmklgeg8gMWcAmWZuu2cdKWltoqCu7Y78oFCXSkOCD6xKiP2q7gQG0yCS4b1YVAsK5vP6f4GEixO/orRA2uuqJgn/7iy/42uNGt0crr0e6AlgVSiGNyw/lgMg1GaFF7E9S21rO+/Ezezp/hB+R7rXgZ5n0YwivxblivZw0JEadhwn+ror7GpRPu/bJYLo3sVCqYM9CtAE02cNS3/gSYf8NV+nRFWY2DSIlsbpuH0awfFuEszIj42wCqyLos1D7+/UH1A+4DHaVsp2JYQZZVU9sl7oFoY0j2XoTvAgMBAAECggEAI7J3LoxBA9gNVAP1LCJo0S5jzUqi7cpqc/MxYh9YmcWOqLu0vrwp++O8/DUvyFq4/YMVJRedHkQdO9oTZDH3LPgjHAe1ndF/NPaSKIAfZQKjHk00sFCCuJvSe3iSN9bRoMgM6mMutbJT8ThxyqGD+AbGrxNRLTofKK41/gZh3iyFqqOYDlaiWc7mDGg2YVlGEjiaAAm7ZDHpF733gYuwVQa3f7dhKQA1ptmb4t20LQ9OjLbYeocQSDmNfljF+l+7xjsXSWrLxjf2nVhd/PYNfKFJGNGouaLovAaDzESz0g0zENzpeI1LRw12FwEg2711T9rpMVubOfjxhOIBOVkhYQKBgQDVrOXXmnUYYuwxECGyFSGR9Bb8p52azmo3HkEaDn6n7NxSi32LTVfCNbXLD2iiYtIL1ClysTQ9ZGF3Vv8RgWFOjHtQpC5WXdRaKZrNHvpejij8Vv91G+IC0tCoNi6NNR+jLvR0+VC5RSpZErp9jwyirqpqK65NVQaYGLOJOex2XwKBgQDJxu7O/BXUs5PSec9Uc542DiNXm6xuVYFfePbYp4RXtx1qTA/A4pPcrblyf/l4l4OVVtxysGXslsVl/6i68JLnM0pIGOq8ETLuiTu7cl22pCHM7hfw4kx4Mxy0IQDkV84Jxt0Je9ioFXktED9U9t12H8qSFvHbx4D+YQmEACXbcQKBgHAK+nakwnPoI0vS1qhn1jOPV6JiTg1H4YBHeAGuyhFJ7XnHNSyfgL4QpeP1j3te8B9Nv/IpI2hxw33te1B1lE248kyl2rpk9x3UJR0b+lMsnic7gzaoSUoLu2gJCT34Nj++NmdD+GU99GfCn1GJeimwByInB337cLq+cR4q5mhnAoGBAKSn2ai+vXHdOPvAuxfHYYvq7ZxIROWkkPY/1+/kg3Kw0ygy+YgFXXPvsC1nkUR/H7l2MF7G4+W1A1DA2Af02WwhxrQe4S6nOlC9XCkSorawKYT5pj/D63MLAplbdUbhABmqViWvEpXXMBM99vB2ozIJr1yXrLYUj4cF2KYHGN2BAoGAKWfXfO/SqZxm+iButeCs0aPY9X4moP3NKRxR5wWmEuXD0xuUMmYf2iaWytD0RAdIdBlurswebdArDFDfdku2WCX4yX/yWYhO6ejU1acfTROtXzUpGx3XqA3Of8Dg8vf6VbJfKtZi/IsaqhgDSZj6eI+CDgal4uy1w9EOYUT5m2o=", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCoasuDjj82xdcy8Y4SiDWVph7YOC0fX3MF+6NvmixvWiUomzN7Gvmklgeg8gMWcAmWZuu2cdKWltoqCu7Y78oFCXSkOCD6xKiP2q7gQG0yCS4b1YVAsK5vP6f4GEixO/orRA2uuqJgn/7iy/42uNGt0crr0e6AlgVSiGNyw/lgMg1GaFF7E9S21rO+/Ezezp/hB+R7rXgZ5n0YwivxblivZw0JEadhwn+ror7GpRPu/bJYLo3sVCqYM9CtAE02cNS3/gSYf8NV+nRFWY2DSIlsbpuH0awfFuEszIj42wCqyLos1D7+/UH1A+4DHaVsp2JYQZZVU9sl7oFoY0j2XoTvAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "Qmb2ettwafKhxxyNNCAAwZVfCBnioFhoArRA6EZqcitg24", - "privKey": "CAASqAkwggSkAgEAAoIBAQDXzYCtBwWGMpp8RVuHik+S4QtSuO7jZbJbx+LRzEx1Et9rNDjc4LxGUpWzjKLRyKTgoj82j+0TILgnRcN6vAwagrf2Je53QxDXfZtDWC1JF/2FX2b7SL2Pv+cQ+4OIboJrYMf1FyLeg7Q6n8ea8S6xV5NPJB3up+WCc/ntwBEySt9HNhFXsod3EgyZqoHpgcizE3xeZcU78cLPDAYUQi7V1vDXdvSVtTNG+AFIUDFuc/uU0yHAvatcA5pWGsxxHwdweAkhnK+uSIK/rhVTFuT/tHR9xgxbE0jMWvuHQukPYcPLv869nxQPA9qQ7MGiWZWvJRttNgZcabAvrGyZvwiNAgMBAAECggEAL91c1QPhrco7iaS4kG+VBrbzk/2Avt8nmEPVg0MVEkKFW3nRwuv11oMqwRBIbM9cApb5/lgd9UgkkFFg8jATXy3vL6FqKvmtGp65eU5tfPDdQl/Or52Krf+aeKHQosogE0D8GNhw23nK19Xop+0mth7+hWc1XGHQ/gZLQPiA1+5rG8JE1fmcYKN/W1kZWlo3kuj5JDmwGug3vOGR+Gz1+aXkkvfTWmR+xp+YgCI9aMeDgOn0M49HffBvOH/fZivw2VEK8ul6x6d9JKadrAPTq0yDXU/aDu668mdIgdEjwV2bJz6A+STqx/DmVCUGAWzwhCgVKbcPP+m82/Su/v1u4QKBgQDwwXnsGPfyHyz89GilqlZ8NWoLmL3y1TDE0OuUYrKuvm95ZW/N7k5RG4MQLcXY9UlzI6Hs+xQEITg1q3V4g18E/E4e3PFf0HZ+yE7ltWc2qsubR102g6hlQoe5Us+JUCk3PVnkGoIHQkDP3KuXWWKJCgizHUMMHLvdl13+xVRKqwKBgQDld40TxSqxDzJpIcwiEXUXkPRSz7/6nY4VbokBgj1jg9KTU6ikblftqv4LdIWZOvI95ynKKTY1lEMPaSWujnLReMejeJSHN4dCKt83LYKZ6vS0+KRxjE71jXL6eMEMS4Zh6+vCfUA0Kfef9dnakVTZV1QJUdjlRiSCm/E/aWb5pwKBgQCrJwAD5eQuThdvZFkYnLWK63YN9HHktcZLxLIU9O1N6Lfat0/6N9WZN1O/JqsmB4pFvikZDY03Ol55WQDTwaDFLJBkxHEbyljS3JeqGYHcjSLdqqgLXyFRizBtgP9lAIWsbYL/9BBIFMN6gcfCeprgDTAOFVlavPqZF0iNG79GrQKBgQCUYvbr7fhpfzZOHfjvnvJlRut4EbhHzFLxMQWP4DTqgXhOpS7NBj3+BzE5HyS1rhSwSygO/w97HmEvOgOQGbXOF5ih8Xu65QGmnCq0d82Y0wNjc9aDRwRYbhwINMZBuSUxdWqD3pMCKJFk84rpeEmyMnK5hCAKQ42gmE8tfm+EyQKBgFdfx1FBrtCRBEY9fGZr7Kk5VgAThm076liVhpu2dr6Himpp6QImt+d6RpgeerGuGNrbcphQ5KHXDPPq/yyTsZHQ1mk6P8wME4ETBV2ACK2AH512jaVGyj5AwGI0FUeU+NLnY8o/AWAxCqShrWgKjOb6zVaVviKE3/YUNyEGGwRd", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDXzYCtBwWGMpp8RVuHik+S4QtSuO7jZbJbx+LRzEx1Et9rNDjc4LxGUpWzjKLRyKTgoj82j+0TILgnRcN6vAwagrf2Je53QxDXfZtDWC1JF/2FX2b7SL2Pv+cQ+4OIboJrYMf1FyLeg7Q6n8ea8S6xV5NPJB3up+WCc/ntwBEySt9HNhFXsod3EgyZqoHpgcizE3xeZcU78cLPDAYUQi7V1vDXdvSVtTNG+AFIUDFuc/uU0yHAvatcA5pWGsxxHwdweAkhnK+uSIK/rhVTFuT/tHR9xgxbE0jMWvuHQukPYcPLv869nxQPA9qQ7MGiWZWvJRttNgZcabAvrGyZvwiNAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmSasEmREwPDaLqB8nmrcP1w6bSd3Zg9LZeSUVzHwaxETB", - "privKey": "CAASpwkwggSjAgEAAoIBAQDC5DJehI8wKoPMeXuh6xs3IS9W/DLrz5pKUbrkU+YNUtCTLAW6J8CB1clGh3F8dweJ9vXlfmd6ihLyQ4EM58yFzYQrG5w6YGA2HlZO3KZQ1foDgbEwR58wy/mfGOO8zbftzkAP05siee8ruVS33mNsRpclntsarbZHNEPWAjWXyqKFTP1aJW/r+ovnOjBgTGrJLSb0rOrPt5F1muyTh2ymkZWlrBrQboU7YBeDZj7KK8FaN/4gNJmBT8qI7kXZCWMGk4SWamB3VJ4MAR51asom2WwQl4gqAcPLrw440CjxzSOiQWIJGPIHSw9AlEWW+xFXmbXIi4GEK0HRiUZ34zLFAgMBAAECggEAChS/vj/hID6yvpryGDgPGlTvG/LDt4rvkjSUFEd6uOm1vEckrLJttMmYNbu/1Q5bJ3nM0mgtdhs6S6nOPRqoa6tr0McG18Ywc9wx3rZvK/NFkXTd839g7qc+bEpfTV7eysBGdAsgFTJ1eq+FgFVSk0E7hEipUMH3kctUTveiSg2sFkGDhtYmGKBO1TaDXnbkCyQDaKspSDBP7fEDiP156fRwMRT8CDtW4BILaLs9wV8x0/PLYVlz6wf56i3/gNhvqDsbd3U8axVIQmrXullosqI0HFl+Uhqhakyw1umoLA3dsklAUmfUmGue/5fxLNCGJ0QFyTpVVf5+zOLRaS4rwQKBgQDywKhnTx2vrL4z3ltiWMsZfzD/mIse0QpIO4UhUboFsH7bsm/kn4/vwVUzFo1tXDG3Mp2Ph9AsP3PTlRcCSmPeGKyyNIP1et+MMxnucIBqrE7JFMwrq0YLdpDxynmPDlfZ1H0agA1YSUBxjf+ETFCCSfglHiZCwfoMobRgRdYVWQKBgQDNhuaajDABleXX2CLgYabi4cSu738jfeB6JGqQDVFWFqtoux0DziRG/AMT8RotqgQRd/skp27AlzNXTKePw9CGilBD6ogIL9S5EBkEbo1osD20dikaEjhbreqsi8CLQpsQs7eypKw5NrM2TLbUkRHxZCjPjTy500WJgnmnNxEfTQKBgQCObpYgz532dp+/JUdvQ/QfCK8COUnfkf27dhjd/Ort7anxVBgtB6ZXoZNQ/3mJ4h9Vg0BJeAGgBLb8PS0b7fP823NwuDl47lh+FXmwmpfufx1XBHnrYXoevbm79PYwBtVq/S9OPjYWSBykxBFZWcGfQLF1beQ7JT+G69Y+6pr7OQKBgB5HTHviQURKiBT3c5Po7wQnzKkVAX8CEWsNKGHWhHARYOlJ/6lK2k9W20E52Oh3TqggK/CndgqLe/XVhi4I5BSeFdsblzTVjxpAg98CRnTw2fZXHhEINCNViOgoopIhmuSoBV0dI34+T8KlJJ5GTQVqAxUospSRyoHKpg97bltVAoGAY49CkW6RUuSD2TlrdlOWRK2+0n4sJDuCGmLeYAMSm9kbASKQY2kqwgXBAU8Ch/SmGxYfAzhYd56MDsTbcoRS4eaw863uPFNkQOyHZa2gdnSho8Ry9PsSnM0geeRI3qA/F4y2ishDZnNahOcL4Xi9WXX+UGkSVjwLva9H9fzD2W0=", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDC5DJehI8wKoPMeXuh6xs3IS9W/DLrz5pKUbrkU+YNUtCTLAW6J8CB1clGh3F8dweJ9vXlfmd6ihLyQ4EM58yFzYQrG5w6YGA2HlZO3KZQ1foDgbEwR58wy/mfGOO8zbftzkAP05siee8ruVS33mNsRpclntsarbZHNEPWAjWXyqKFTP1aJW/r+ovnOjBgTGrJLSb0rOrPt5F1muyTh2ymkZWlrBrQboU7YBeDZj7KK8FaN/4gNJmBT8qI7kXZCWMGk4SWamB3VJ4MAR51asom2WwQl4gqAcPLrw440CjxzSOiQWIJGPIHSw9AlEWW+xFXmbXIi4GEK0HRiUZ34zLFAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmVzpVsXAKu8ocLf1h6YxiGaoGzaoow7mgFhnSayUkrtos", - "privKey": "CAASqAkwggSkAgEAAoIBAQC70DVI9M/iZV6F64OHkYCzatlI32bP6bg0pyVnaEAQHEG8RYMm55w+SlDxfO7NE6MPYp1objt0PkB2MUvmhigmKsYlRoOO3kxPf0fgsB45WjRZO+gxhIejmGpfMmQS5+cYx2nWcv6WYXURlIMwb1YkV7AcvZIcqdNspSrlHVqP6BPfIC+dbRnf1xX/1Urj7Z0X6wUnWAFn8vsiFEy4t7ox66OC/uYQMB/lF8PkZcccAeZIlmflZ8LeFqtuwdUpBa2fS+00tZNd2Xav3OnnLU9fnEG8MfdYXqQFq9LD4e+nBEIARVFkaRo6vT7ujU8qdgA+y1bZYA2Y4MBTbC1tnf4RAgMBAAECggEAV0eS+6yJTzS8kI+6OC4uGTL2dx8asFR0/kMO5tdTrijzg4LqSBIqUehHZXIhp7wQcv3pGLbhekvTuRl/pEmELviBzKDQUnyMCgWkaY5u/UgmO7HTXe+w+R3DkSnhx8dtZd6GGNqn5Uq1FM5niQK0jX8SoMiYNinVzw+St5bEl0r9xnQ/0WQz2o1tb5wkX3WcRlWhp2i4OHuuCcV5CWrT0biXzAOGl+6releHceqWSkv57rIYMXOlSp5Eu0JoWsgdETn7kP0GRfHny9pqKNZ8sT/cw/Iq15+cUYELyMLfXLAPWsTkfdM7PVGSVTXnYzr+jsC7o0yqWZ1q/IAO73sk4QKBgQDzcqdvX7M6AmcN8Y91LYV+SvvtNu0igESFR1QcHnF7vPKSbDLIchimi/vxVfAZ548hVgvwoQ8nfocMCWf5ZCn8LSXaZVNcHJjFHnmaoyXeRxuSv1lSGuanBSDBRx02FYIr0K/CHjk+zjim+CHE/RYJez8G2ywmeVGx0f3wGUW5ZwKBgQDFfzdE7zfHiG4DpHvdrzHjyzygZzf4Pv/WtUQZnUSWCdC1D04lDXi/ivqWslnnIO2oJPDBElnlJ7fXIPh1PqJVtote5+xGbV0W/dW1laiUmmF/rsjJM2275mhCKEDbdp19C9d3LYKxhqK42vt4wFn20QDCizZGsTZjwWv92r/JxwKBgQDxKsS5rUlkjxq+Em32O/lBqlC1pzL1ebHngkjNbk8nsH9xFCSes4C+BHC6nFK1ptIAyTgc0cCsdEieYPcSdOquuZ8FIlmZJ28j31PCIBsUfsbO8iYvEx0pmgff0G4ctOP2Oc7Tc5NsJ2ix55+0gK+DBwfh599t4cNPb+KrJq4OwwKBgFHZpHVMUyi90SJvU+qPRjTrMQglXxviODOq0jtvY1JvZPD1E+TlTWrM1YgJCJtymSw7iw/pZBpFuLpO7sngmHS/f8logxK5FoCF2ME18jUMOmYpcQt55fuexQzOE/sgkKqXcsfws56RdvT3xIrJ5T8WZaM7ANaRcUIskm4V77BXAoGBALNV4aqbu6rnMKyl6i2JTvQPsoSAY865Z9M5o3WmGSyR/7DUOPwkX0hUtMZgVQXeto0ENodDg0vrwWsUvNc9tsB1yk84tE+NnAj2EecBxdcgygsyIJh1U+SYAlbEGK+gYuk+cD3K5i3+gcrMYQ3tzv8Ee9TYJkPKod/LEQgtmmhK", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC70DVI9M/iZV6F64OHkYCzatlI32bP6bg0pyVnaEAQHEG8RYMm55w+SlDxfO7NE6MPYp1objt0PkB2MUvmhigmKsYlRoOO3kxPf0fgsB45WjRZO+gxhIejmGpfMmQS5+cYx2nWcv6WYXURlIMwb1YkV7AcvZIcqdNspSrlHVqP6BPfIC+dbRnf1xX/1Urj7Z0X6wUnWAFn8vsiFEy4t7ox66OC/uYQMB/lF8PkZcccAeZIlmflZ8LeFqtuwdUpBa2fS+00tZNd2Xav3OnnLU9fnEG8MfdYXqQFq9LD4e+nBEIARVFkaRo6vT7ujU8qdgA+y1bZYA2Y4MBTbC1tnf4RAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmUnTYhJiofsHuBysR5163GP4a7AgXno2N6Xfz13gEhKLW", - "privKey": "CAASqgkwggSmAgEAAoIBAQDSO7EWwmXElyq2y87TjaG58rEm0ZZhhuIBVJWhvBFOqmacmaFaJWN3OR3QTtEZN6j9ymiOpDWkgz9Vh92Ozk9C6ay7BA9mKoqd2G0UESL9btPV9sQECqajhrscVQPq9amra2wy0ECjvKPMGQEBchnva81XT60Gg8Zm2ItzqopHZ76FsPYGPGPSSTJ1yv/J+dsyjBOmNRhR95b8fqsQGYJVLKwmfKxRDeXv9wMbB557g+/TOPodDeR4q02sgyr+ONOq0PU40qrL43PqtsQBRT2nLeoQl1DAHouia4AP1U1ZwTuNPd5YgCXvHqkr9YJUiagEOZn/a5xBQN4dzeDE6irnAgMBAAECggEBAJLVSBlaSxPkdNvZOyp8uGEURXCUX9DcEUvWlO+yV/A2iZaEorJAfNkPVmhgNCDFxE0FqsM9o420cW6+hxsvsyJL7O1tp4e23LvkJkMmuOaDGodNY5hjDAIYnuTp5+OaExf73kUbOJpjrY9mQ1KMK9sR0whRSMrNDKxWQAfYK940LQXQhAd28T0gB5Scie9l5oQnIRgmaQskhdOTYvFYyH0AEExR4vlx1+clQ4tV7FTf4irqJbh9b1+x/pvuWAOKxn4oGPyndi/K6HRNBIWu0HULWLHQJMWK57APWTBNiVKC2w72ZL7iLcme8eujIakrwSgUK5oHtqC12EggvsdZPgECgYEA/VxnvZlAP2w+CY3U62+SPIHipUIsizmswRNlwj/Qes8h8jcdOv8M0QibAgVHzIw4z6xXUbBlaGI88GfeM5EOpnn6GDt5wGGE/Pfe2L5+iljYm6j7qLj/MXt+m/0iw8Tl8Reb2fMjiQEuDT9XLsXip3ZtixusdBLqPRoRaSYBuQECgYEA1GxI8n5FwamjCjtD/W8jZMJnfCRVgGshRRs66S27STT0Q44rK6jyEY8unsmwob+Hk/884mbQEkmevv9YFRX1Bx2B2gQBgDe5u4ESW/xZe6zO5UOz+1nyw9KrW8W/VI5C/Vuii2Y+8Hnfp3s8KNwnwKgqV/m2qdNip0/wLGK5O+cCgYEA6JrVg3QXUCMIMa1NNXmRQIvekOpYCtpAiGJOojAELzvLZpzC8U8HbUIBTbGbYWe7IK6Q3Caec179o5k4nw8l7CFAQs8X0E+30KegqEz7z/gRpZdWtGhjogJHEt8r85/pm5aZN1fJ4BZ9ORxV5lM265gGqhgWE9rpwn8UTPzfyAECgYEAhT3288Qo1TUmw4AxQYK43LbkWoYf65FHKSXPafv5gg3pOYavpY8vZ7w8LfWtCYgt7rMm6Yw773ymSn+4LGG9dF0Z2jqxBk/t/KMVdQVwy5a1oDE7b+oX0KUQP1xmiw9BDdKwvmfACu8nTtKKBccyWDIjfVNxNE0XkIMfz3eNYPkCgYEA7DBOsXD/lca6cJ2gyb1QRrESqu2zVgzA6eU+Aj4W2f5JGJiYyED1+Z096PdU62Hz3YORW6STrxF/fvl5dpSY++FktTjQqNvoogCIaz1Tv7/2I6qmghWOV7ykznqJV757H7W01LIbkihkTUD5mtrSLLuhZC/eGixBmD+SC6WQbH4=", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDSO7EWwmXElyq2y87TjaG58rEm0ZZhhuIBVJWhvBFOqmacmaFaJWN3OR3QTtEZN6j9ymiOpDWkgz9Vh92Ozk9C6ay7BA9mKoqd2G0UESL9btPV9sQECqajhrscVQPq9amra2wy0ECjvKPMGQEBchnva81XT60Gg8Zm2ItzqopHZ76FsPYGPGPSSTJ1yv/J+dsyjBOmNRhR95b8fqsQGYJVLKwmfKxRDeXv9wMbB557g+/TOPodDeR4q02sgyr+ONOq0PU40qrL43PqtsQBRT2nLeoQl1DAHouia4AP1U1ZwTuNPd5YgCXvHqkr9YJUiagEOZn/a5xBQN4dzeDE6irnAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmcMTe658zwQDgHHnyoRhATfhDtugTTbCxwGXNzkZAzdeZ", - "privKey": "CAASqAkwggSkAgEAAoIBAQD83VaAqAvo1ZRSQIR7kqik/V2i/9ywEVPdZhl7O+K5LvoD9FSGKGPYav3yjGQTKwvE5eki8Rq4VwthC99OaaogMUygpAsLLEiaoxoRRHvEM5tyzYlbPTlnAE9Xd0bNto34yLV/duaQFKh2wFrCypXZ4xEWz+vjRvfm3+/hSp6cqBTUGV6I9qsQO+8q7Sqxdh+wzC2U9P473pZ9X4EsqkUq04Ycvon4WVbdLGZkrxGWX9sKfRQW1xPVkZtVJlp/aDq9ZK8VB9dCnxNWOmJlolekfmc3tthe6gdcPEw270rSyRhxuqvPgvhgosu9FAkLKSwA2o5vHsKtKTGlmcpFSBqlAgMBAAECggEBAKRD7UPa5xG0XYwpWWclWOUFquSOroC6YO68uuTxfFGskMIs4RPd/S7EIoCEbyZ8mkKo0JDga+lAsqWynrhDsD8Fh6/7oSj69ZdvSSnagURt+hfUKdzZowakjuZVF+vfIc9yI2XQiesjYGT0hIFyNXK8LYfSPn0Ax152L1D9tpgw1fUF69pHbBI356m4pQfMq5hp3RSI0TvRlDrtmb3Eab5ItCDmtz0prqZIFv8y9HGa+3KZXunnhJyfTapheF0PAdoI/9+mkcU5yXZ9VgIX7RL8M75tUMWLiy3o/mpJFcQbneNkTyucEKzKyk32Z3olP+iqI5sX0R0MU/fyK4IyTOkCgYEA/7P7gwWQDbtomKKx3h7gsUbkopsBeculuhSVoyE1VZgJL9YQW8Xg4oQfNA61nsu8km7AJJsnGB4wkAqU8HxMZpk9jjLsI+GubRLnArLLrvHFwnKOqw/PeIxcrJMBzSHYkQ7aybcv7rolNbABdC13t46Y5Nyh/kpICmJUzLftH4MCgYEA/SiC98LtWmVo4i6D0vlM9Bn+gPffD/MV4G9eK2BiYeneawwuJkjhtU0X2BWG13V11KGHmkIm4eU5ILAJn2q91s4EQ+lSOwjW35PrgFEdWn+y5AkohL7onv2MNV6vLu/dKrT2sX96G91hiN2sYwztarGS34aoOnEDeEThUjzm3LcCgYAkbAecTxOI0TQB4dLCF9XbioSQoNGh/p75lWsHFHjbW0+br7sex13UBgvHx3yZRN30YbAexrbX2Z0DN26lnp7nUlaRRbGbHs9QnAupt7wJjEil/NlThmn/+sZMkpgEFxkY+GuzpdM/Bua78fkTClLuI3KlzsOITB5c1ErN6jjtbwKBgQCB8Bc44D4/lal93m4fDYKoD+eHfrJpV1W1OrRVA0W8B/P3cesGD4Z6LjW83V+2mz19g+M8FBQtAiCOXIyz3G/QHzIlQU7JqkHPw/auh/PPDZheXy0C5ZI0eONMSWsVZlxYnUW52TptrvVu8IiY1nvNtZMzU8RpKrSjOIeGVGgShQKBgFPUX9/rZjR7mbzZakbXwcEZCjSI1C57VMLIH3m/IfE0yhO5KsE8rSm7lpHy64K3rMH2hFViHlf+RyUrzIXyvVOEToQUWdDcT/QxbFNHFolPJCjcssQvejngG4IbO4clRMNmTjCdQZHJoz6o4XwKrFN3jalBPeLrTqSF1HyOV1bJ", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQD83VaAqAvo1ZRSQIR7kqik/V2i/9ywEVPdZhl7O+K5LvoD9FSGKGPYav3yjGQTKwvE5eki8Rq4VwthC99OaaogMUygpAsLLEiaoxoRRHvEM5tyzYlbPTlnAE9Xd0bNto34yLV/duaQFKh2wFrCypXZ4xEWz+vjRvfm3+/hSp6cqBTUGV6I9qsQO+8q7Sqxdh+wzC2U9P473pZ9X4EsqkUq04Ycvon4WVbdLGZkrxGWX9sKfRQW1xPVkZtVJlp/aDq9ZK8VB9dCnxNWOmJlolekfmc3tthe6gdcPEw270rSyRhxuqvPgvhgosu9FAkLKSwA2o5vHsKtKTGlmcpFSBqlAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmUaekXpyXgarUkAuUq7LgeXYGFMxsiuy1zkUSiMDzY2ge", - "privKey": "CAASpwkwggSjAgEAAoIBAQCV/mBz7chASkoZWB9N0Z5y4bkAb52+88XLdfE1PsXDmgkPn3txZ5En5gq/pCQ/x7obG7zfb1Deppu1hnkK4imsCMW5Q8tfx3ar79SoX09gMUj0ULcQX84Ruhb/JXtjxHrY810CMVeBO/okMIS4JZPrTwfygodpcjz/7CRGT5fBn6D5yT2b2ADAjcmKFc+cVjMiU33ffLGXXc8dn3vUEI94M8fAtYc0Kso7deLv5wd1BnTS4sa87DMNSbexrmjZNmRCd9OvXvf66UH/rVdteU+8o6bwFbjtx73dfeacbz9SWqIHpnxBRk5TbhT/ahsRxa8gTPpZNWR+o/d3YOGUTDCjAgMBAAECggEAQFNpdg5B1SCHCrt6IVuGgmo/dupnUl8lMo6QNW+ITMygmiyhOg9adyv27B0u1pOHQtzwcTpCClqVaJIVEw/PI1JXyY5Dh/347N/b6aGGXxCD4xNCjyknLP8LobynYDABJ02nU6tphaj9K8wK/xZOi5nHJL/J5vTxKChTnjvAL27n9pqo8zXjEsNOrsdQoeVRT/aq2cexzFv6nfJvS0uZ+hfQ+96LcmBT52OFtf8ekoSp7CcLCZpjmgHW452Mi75vC3B1VRkIeAMyV6DM1knPclSI22Ph5YP/xH+SAVriin1j1i2Y6fAqSYmRLRLLlQZpDX/4d3d6AhyIF+FOOKTG0QKBgQDHXt31BVRedo2CJ/vOkpAwoNMVsyYHeEs/uQL1ZoSTHFR7K1s9Zv5s7M1VCQnpWxfSw4jipmPlHoYvKtxkUcH8f8uBGeVpHHODHsNWQTSTO3uBK8qaH81zvL752h0ElY2kDfZ4yUQjr83AaiKjbl7/0lWzyNWrZfpxVJkV+AOa/wKBgQDAmRd7P/oc9GpS1zw9GyJuOmIyNgsITS5eQ1s07brdNgUNe8Mvwuauz8QIipadE0o9tvRlBTIALhJ1VMZk2Pc7GBiT5R158lZkMCWDtEl+SwRN6swCpaDoEAdi8OAHkoUyaDa3awc/Og/Beh5F9nzfQHnohBiqDZTdQ0jcMC0eXQKBgHQxeuRZBdHEADbx/JRo4LYmlL8Z2LkTx69MsUe6RtvB8A6UtykzBGcRH55GlUs2Ns0z/mwxkxiuUH/e1/FzoL368Oy93fEDjuLFJAz6FZ0VVqZykjJ/BGtGfnr5Pl40lwccyB+fFSJDTIOul59uLNmliSMtkjHBTlOMfWfLUrabAoGAYQ5E+QU6g1DgK7LvVlPQPAAL8AWv9ZT/Yt1KnxeV7VgFn8/Ygr8TBNEKlstQLwPDi+ogqq+9jL2q65m3CKcVn5/68ryo6AUpZ/+jSAWYa55eIu3JtSPGPGunbUK5gtdhbA98U14KHuChg/yIOPWH4/FX/cZjr358oCwCEYPtmLkCgYEAvWLMa7TXajVv+2lb01VmpEOjMnl9FfGtYqnp5LQbbCV5xpecjwaT6nn9jXbuhhTDvARacR/7dYbPCG+hOE5kuDDZ0LeRZuhboRgq9meE5F2ujG2NMfkz0cz83KoDR2eHbVd2M00HRtxSez5AV9y2lVH/i2YWJkI3kIsx0X6m+NU=", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCV/mBz7chASkoZWB9N0Z5y4bkAb52+88XLdfE1PsXDmgkPn3txZ5En5gq/pCQ/x7obG7zfb1Deppu1hnkK4imsCMW5Q8tfx3ar79SoX09gMUj0ULcQX84Ruhb/JXtjxHrY810CMVeBO/okMIS4JZPrTwfygodpcjz/7CRGT5fBn6D5yT2b2ADAjcmKFc+cVjMiU33ffLGXXc8dn3vUEI94M8fAtYc0Kso7deLv5wd1BnTS4sa87DMNSbexrmjZNmRCd9OvXvf66UH/rVdteU+8o6bwFbjtx73dfeacbz9SWqIHpnxBRk5TbhT/ahsRxa8gTPpZNWR+o/d3YOGUTDCjAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmdAKuU1tkALvTgazZtQtey8UQWqEZD7hEqYadQpfQficW", - "privKey": "CAASpwkwggSjAgEAAoIBAQCg2t7T0Wino9kgXWcv6reFcvY9QQEjcbq/+viXSOWBuMkIsgt3d1Hl4D6RKVWL0Nq51eWwubEzNeufN0XRbnZPvBX97xyD0TJhhjPWEf1iQep5+Ioea8hs1tCW47XkCDmfp54jR/6p5JCoWyQ5473I8nV+7r8I1aptygfnyAmGcHSaPf55t6ZlPbz4M6GCbxo/Op97N0xVlAEFIl7rYdYl3dCTFYUcN9TzhWTUxWKPhPkpEg+iO0W9luHnGtUebEV2lLeZskid/+NZfXxSayueS0B1mwGlPZlm53149RWXyVZtZihkggaXDXVUKStqWQt9o4W4xAa8JSuIol8ZnCKTAgMBAAECggEANlugf449wqERJ+nIjB3SpOtDoVGNU/AD/wqN5XoB7QOIFEMustGEwJ02J5IDUbtjnvdUppMp+bdYB7cDBhJBMxLJj8W1KiqQzvouHEJ6ETFbTpqZ+kvMMFOrq8IJ3qSU7IoVW7Dhs4IFDI+4P0PiB70/zYRa1F54OJ/UahRke6SN6Jepv3Jgdb5e5wC3NQNvgdJi/hPl/Y3d+T1vzctR9bPg/H12uY0OJTIjn+FkUAQIhiskkj4iGf1MKtd94Oojnh11OUcQoabipQH/4hf7K9Js2uizr0d5qKZ0VQBPJwyf1qlP09WNPwZJIcHh0/cYePA4XosKlWhspEFv4NIC2QKBgQDNx02e/nthoFJ7gGH75I8QANTHgvHiAvtkpfPTFCHVbzkQK89FeeQLE+0rgbEXOoRSlDTmKYRoLxU8Qk0mP6lF6PD1Tgxq1f/+zuROzRTOCvdIbpMLB42jdOuuOLCdFjPngn9dwdmUpB+sYM4J3HdHnesfXSTyGKtfG924HoRojwKBgQDIHNF7qUkZr9w1uDsWPYunAXKhUw7MMznvew7FC1WBb4WpPwgpeZ5cKa9ywviwV7EbKh6FM0g0uP5ArFUXBlcHpEh78x/xfipbiER8P73OGxsuvphdWozSWm47zCv2rnV2RwaqfN3nt937WRCIg10tR/y454RL8Z2J7c1DJER/vQKBgQCSdobC4bKDvA65JJmZJgbFhzHrh0IOcbzo2E2BMVUbivx8jBINC0LKt7YZP0gCln3UIPS91VMOrGRa7X3n+WvL/I50qsafzA1XGX7ar5FdTeTPwxQZx5iCfRe6e1MJm+H5p6Jr4yuwZli84nIEBs1HRhkxy6QeRHzFRxo6kE4B9QKBgClb02v0g/g8IY40wnmJRNjCctem2/MWT04Qp+/PtN9olj5xmZVA3pr7vphAdbe0mBUeMmqjO7Qx29KwC3ITzF729Egx6pM12TlLw6POZMM5VPfnSoRY16wOJqRTQW7dhcdpTJZl8lMW7FkrgkBErjhSnYf1yaEMkdvU+0x6LXIdAoGAEs340X3I2wgWm3gpuhhaEbkg6d8XoilBSNd2J7djFHlPJy7vrNYWcKyvj4GDvOJS7mH4mFXFxM9AVD0xIVeXRK3M/tj9V5Ak6e8uXmyY03CLz38gqHVL0VfU7z5LV71oseASYNmK80+RyhiTZz/+6MdKGoAPSnNfr6DqfViVT7U=", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCg2t7T0Wino9kgXWcv6reFcvY9QQEjcbq/+viXSOWBuMkIsgt3d1Hl4D6RKVWL0Nq51eWwubEzNeufN0XRbnZPvBX97xyD0TJhhjPWEf1iQep5+Ioea8hs1tCW47XkCDmfp54jR/6p5JCoWyQ5473I8nV+7r8I1aptygfnyAmGcHSaPf55t6ZlPbz4M6GCbxo/Op97N0xVlAEFIl7rYdYl3dCTFYUcN9TzhWTUxWKPhPkpEg+iO0W9luHnGtUebEV2lLeZskid/+NZfXxSayueS0B1mwGlPZlm53149RWXyVZtZihkggaXDXVUKStqWQt9o4W4xAa8JSuIol8ZnCKTAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmbjYN3j93tRphkpoPYYNPtVRQ1PfWKBXbTjvPb1YaQie4", - "privKey": "CAASqAkwggSkAgEAAoIBAQCvPzx0PZ7I7g7946nS71ihYr3S44ilmZ9Zb6Dkt+nTf4s51ZXF8c1vbeH1rVLqImSXk0nQyuyxtZbXzjhfzz2lkIHWrld6/nFJWrDj4iL3+dWyrbGduzGH5RxIxo90zq9/pvlQomxWLW7C3dfNqTcyEuZPlh09wAsvuc6ymokDjia0WJ48n8tejgv1XotGmJPriDTSQ3lyecMXh4z+fM2EVwSum+8Sl8MxE17WdhBTduYcEzylCjO55ved6yhUEGQ33KcgUxDLOKve0ZWjvipPha3QyKYFs0F4oQhxXJ1KvvTQu6vIvqEexRPufMtF6USmfNWzJ7VEnoPErKRAw7r9AgMBAAECggEAGDdEw0tAhcNfjvXGob8xIBvk3x9R4pA31MP4F6LSTMdzFarN52xiVuN4Ndqden0GKWvQ52kjC+trzKZSY+rfOeGeD2xH6lb+kIRXrSWyb1G2ldoqkQEs9vpRzjyh1iI5XgpUqS/IiJ/+ji7ZgzG+zsyNxrGXmNDQuueSCFwSUss3CRgeLY8X3jf1tNqw8RrVJMK8dQOn1udJVHwi+4BwDoEvWD8JQ/iwTK6Nw9PI9oClKG26zk0zc/nw3QiqQuuRjjh3LEVW4wBnCSiehRIYl8YY9kXq11R9dsLlMl/P1ak5YsC3e4ZqPehJXc/rN37/ypEl88gPSxQRf6hywomYAQKBgQDnhFjXFvif4H1QL4xU7JlDp4wJ3iTSvJFhS6R0Rs8yqSSdwezcC9OGlglFsxAmOSpy8ax3unxyMhBUUjS5X4ZsAr45eYe2ma9pru0bMqat8V+e882Mtx1eL/AEQwUylENAlYs6rVasmQdWVXYlLuO+Kh+YR9XPr8E68eT7M9nMjQKBgQDBx4rhOEY5lvJRkrNK0uFJClIR27FIyWGXSXiYgGa4SRHlwNtbTxQsK86NT63rnwCWMDMdrbg451jDNi9xV21ZLLG5gOZmnzz9Ku1Ool/rWpE7SnfmukBEqXkLuJpLWYGcX4oo2IaMXzl23fcD9UNIs/hc8M29PQYWg1jHfv7kMQKBgQDHCNqvn4oDOKXDB/2nDPj+Vs5ntVkG6yI4+STa6f07WnqmPY/55RjmvZofF8AsfDzoMKjLDcHrEutC8qFtNJiFxx3un3JzI1DQlJg3J6ZwJ/DC4Gq4LLzMun2nzE5tm1Tt8yKNQXQgUjcim7pEYTldxS0AZ9GDCWAf4tGuvHbkCQKBgDa7tOd+bJ9xmkoeJJQ60jU+PAYdRornjrAbqXtxsRHWWb7KZWr6ABml2faiDd7ij1jcjmOQoNs5xSGGWYorBpDMhfp+hRVxXtmnWVX/mRYyA5l6pDlAXEzIjY8Y+kPUKT7Q4YY9+msFroZ7lXzBttp/MuSVg5cy+Fg9i0L2BOrRAoGBALSoamMnSuj9U+XySBgzR4CFnEt//x09sv7and2Ds6R9v8KTG7hfxXQeSCSrhHzht7zGUOl7AialE8DvwmFQGdk/MXXXGLNLc9BKxDsm0SOhnfpd+3gj1tETZ1MNQwvEvH05/YxisUM0Tf7jaxAoRPQCTK5RnI+/SPRM4WKxxAk3", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvPzx0PZ7I7g7946nS71ihYr3S44ilmZ9Zb6Dkt+nTf4s51ZXF8c1vbeH1rVLqImSXk0nQyuyxtZbXzjhfzz2lkIHWrld6/nFJWrDj4iL3+dWyrbGduzGH5RxIxo90zq9/pvlQomxWLW7C3dfNqTcyEuZPlh09wAsvuc6ymokDjia0WJ48n8tejgv1XotGmJPriDTSQ3lyecMXh4z+fM2EVwSum+8Sl8MxE17WdhBTduYcEzylCjO55ved6yhUEGQ33KcgUxDLOKve0ZWjvipPha3QyKYFs0F4oQhxXJ1KvvTQu6vIvqEexRPufMtF6USmfNWzJ7VEnoPErKRAw7r9AgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmYUkVMesm7KC9DddFyoUr96c9ktBV5TX3PJU45BKHT8i6", - "privKey": "CAASqAkwggSkAgEAAoIBAQCvad/4sTNPjoJXFONa+0WXqCiWAr551BwdkHFVEKXS2E8AhtSqFcdU5CANEZUca0c0RPTJV2RhQJuc7HjVbx3jAADyLUwvPWEv+oFAHdgz+/mopVeJjVhmEYWV33LkSwcRFhJdJ3m0gA9c1FRFZbFOe/q/lULyEr0d0sQOTmludDyjWs5S7kkDuv32E0q23wXfEDvLpime3KDO5XslXGVKR957hziJ4z3ZnHII2Exaqbxp2yUoZ2gWrqzTDhZS9Vkxh++yJIRLCwF3co1z0E8BXOYFEec0ON0S607D4AfCvyY1Z9FB+NeBJNPd6Hn1UE6rK8MRRQaQpWLoKw+TQEnRAgMBAAECggEAe2qEeJdEQK9FqTs7E2JC/ocDtzfLCDBib7KW6oDCCuzB+N7kdZ7JFkNDAa7jOJGKEY6Ko7ZnG723Pttp0NFTN8li4QFZ3srSvE0F7zSQT1LzvuJGCrN2BKpDUMVcMp9PI4hh90S07nhDVs7VU9ZOv6efLng4F9VzVa5a3q3wpBLdTJkhzxHCCnA/mMYGhrzemL00RWhxe3Y3HUCfume6W4HdlScvoBL71GZsDTdvq/jKY0DTW4tSK8MS6OulBm4a4flTNCah/+RKsnxaTK59B+TvW+4LrPtAIpCvTNVvvhGtw1LV1M23S0/h9Ti35GObETPF0ydsIL5lBSozYFxoIQKBgQDi2YRIUepYumHzE/J//D5Eyu9oVjYFOILzwz4UnnnJGOJkNaH6kCz4KNXNc7RHgx2+UVGhzHTjK7XKb9cdb2pBul0yMibL5fwUx9TLSiJFiQttVsQG9Uar0w++11SqIVFmVMOG5/sKBu8M4rPnGPuPwKHMPiub3UROPawQGMtkNQKBgQDF9E7U7eLD4Yk42QOPLLZr7NUrbnBK7kkjmsjOf3iHz3srJveGnwMZu9KqeDqh8We8hB2uEAmVY9eXViGb0NDfTTcgKS6N6IyBlbGm+YpF4lPDUP2QU1a4gcEw9IYkyuTRP+Bd/DXmH06hvmtBDV5aiFe2oL9S6k6mS71o7cWKrQKBgQCKaFCvl1s2e7GbkAYbVJnhezgLHt6i3NH5TJyqE+8WZVpr7dVAfYsSdkfMrNXH9BXHsvHtmEOQ/3BRbV+AlCPuqniGUdcd/NqLC0moJzk11+Hi+ldsL2bJG2O1+serbdyuZPVPcGbYvVZJNGCzlaiXEt8lMKGG3b/5ROOghqBCKQKBgQCVu2o1nYq9Z8eH/H64ubVyhT3pECxYQU2JZPcnWzwsXkBoL51jcrvBp1R+JVsUS6mP6s8YboERQug8TKY3WgfkIF/mL8BLDu/YxQYPqwlwOvXo80YY+TDLdzpOcWdWRTI3JP3tmWybmGq95W7zUc1g5WiTd5vAeALtvrSSveeCMQKBgBqRcvMPYIvioU4wX+hfUTK4pZswbZAKXn53UVqVLo3aPXI9NCtIwuJt1uPzrk3knd92zGyHQIL1BbJd+ntrFmgySzq9NfcfN8Aw2qBCbMaftY2VZPcCMl/ZKniI73g3sS3PTyfTtaTLDmaGnTTs9FTwj910BP2qcqM1CyNj9bEc", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvad/4sTNPjoJXFONa+0WXqCiWAr551BwdkHFVEKXS2E8AhtSqFcdU5CANEZUca0c0RPTJV2RhQJuc7HjVbx3jAADyLUwvPWEv+oFAHdgz+/mopVeJjVhmEYWV33LkSwcRFhJdJ3m0gA9c1FRFZbFOe/q/lULyEr0d0sQOTmludDyjWs5S7kkDuv32E0q23wXfEDvLpime3KDO5XslXGVKR957hziJ4z3ZnHII2Exaqbxp2yUoZ2gWrqzTDhZS9Vkxh++yJIRLCwF3co1z0E8BXOYFEec0ON0S607D4AfCvyY1Z9FB+NeBJNPd6Hn1UE6rK8MRRQaQpWLoKw+TQEnRAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmPC4nWM2FExdfdtFPj2Mo8CgD9V39EGYJ1Uy2KfJByQ76", - "privKey": "CAASpwkwggSjAgEAAoIBAQCmJ+uNs+bUavj6HxRVCsoCb7NsfAs/RPm+KyoYP9zMAYAeo389S20cdVJmLEUcv47a3DsvK7OlKBlMlS30nYK7vv+y8t7yk4bgUIcKPRSUWUgRnjPBiC2VA0rdxHCtp5f5BsPoOayN137bv+jPanPWba6B1mN1zQMLZt6Mm+Kgf3xWlSJv8boUx066s7zAGQ1+4neOfejdSboFA6faRjPBZQyePgYkaObgeaGpkJN7KnlLcmMdN+yqvRTAo2Lzzty3GmmNg5OsPwm8Sh3tsGvRtC8Mk0m1KpamIaxOlEQiT3pAj/XLmvn+I19HSAU42ydHzDaaQCor/Wlyi0pxWd3HAgMBAAECggEAdUfKQYxRi3Aya8JSRLDH5C5aFGH+Qlt6eNvY66LwQ+NvPrEjF+3Mh4Dcd5gZ9G/V8u/uqp4LQLFsIh1OgdJIPCNWM0axTcIKOv08RGLWytu2PhFP8PQhUIQxbRXCfyDD6Zf34kwLW1dXiN8OApHeT+W9fpIIRFdAJeUng1JpBeWzgeys+JjBitjdrl8BEeDaecYGTDTVcL7Qle98J09vuBU0AEjiL3wHZ1NOqC6e5LdqJTDBBwd7qwlmJYKKTP7p80y7AV46QPkhGg0IuxtSFz053Vy5zgxNmEhOLG/6t9OViTqr5q5z6ruW1fDDFSup5VwT4OucGRj8i7mcJOh+YQKBgQDPkA4EB+jHgyOs6r9cywa10Y3eKZSiTn9q9vc6YiTmzf/vM+3edzXBTp2DgE+NUTNrLX/KBjCcZ4MJO9pXv7isC1M4RzoheNcEXt/lQiwM40NWtyoBtZmuQsd9VzRoRkeEUfRD30cIGftyLynQ+T9Vs4ohO0i1nIPTHodRVdRydwKBgQDM7jHmf/RkrYdRIUYWmqEDmxC30bRdzwXNuxkgtodjH3O5by2dRIna4VHHC/0Fajr5tDsozLE4y5onapgoLUR9BIeQ0zdHpfhknyRmPJTAI7tmOUAtyk5Ag8V21UAnQa89/2LkiSOXK563k7IbcfhR8QUpyqmHtydXjpTjfn/zMQKBgAbcJfpwIHtnlChE4eo5M5GSyXOMQENVANUSMH2XfMy8BjdrqfLuUbJ/3KjZ9sce5eom6NBOgBDLQwNtHPxFc98LyMZVZFBy4/hbAl9bXoVWhYU6LIM980RVJK650RuZJwfyhXYwzPIxmaPedy1W74bvliMfCHooIBs8KRDBG3JlAoGAZIGV+6RZql7o9MNK6p8fxPLyOhUhTrjP8dyHMGIU+GpeiV2bk3wf2DeVsfeROmylS/423YW2jVJd4mMHCP1aj63/BupwPDWMI11hrrqbgbiEmlgNv+duhXmbCPMBqb8vQUrVp5wS1ntQNly7h3ZYAWghziNVDfin1Ota3lAWVKECgYEAmZZxr4Rjqrdl+Opo/UBO7z8hZCiMVCydjOej5rV0LPeJvPUN26hZxbzI8T/kIjN+ShwXErkbOdm3YLSiqSBD2V84FiL8D4GF/fQstq9kWMHe05bABegFdZPG3eRUTTeVca/CoryBSxfrYv43sNE1wPhBC/mcaKl4a+Nf7fb93tE=", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCmJ+uNs+bUavj6HxRVCsoCb7NsfAs/RPm+KyoYP9zMAYAeo389S20cdVJmLEUcv47a3DsvK7OlKBlMlS30nYK7vv+y8t7yk4bgUIcKPRSUWUgRnjPBiC2VA0rdxHCtp5f5BsPoOayN137bv+jPanPWba6B1mN1zQMLZt6Mm+Kgf3xWlSJv8boUx066s7zAGQ1+4neOfejdSboFA6faRjPBZQyePgYkaObgeaGpkJN7KnlLcmMdN+yqvRTAo2Lzzty3GmmNg5OsPwm8Sh3tsGvRtC8Mk0m1KpamIaxOlEQiT3pAj/XLmvn+I19HSAU42ydHzDaaQCor/Wlyi0pxWd3HAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmRD7p61F8WLQxxaXSUkhSiCJD7BvEkSBEJEmDRKsFqKx2", - "privKey": "CAASqQkwggSlAgEAAoIBAQCwqroKmBCeb9LTRqENrnRehdCTTk8eJxRaF6SGUiQDlL7waBkXKGzA3FyA8Z4wlx5zUCjKtwObpRd5Ijvp1bWvivc61kA+RSAMtSaSBXlfzRZs35h+GxYNWvBfZZBsgoEVTQ0zOr+nvPy4C+ADuSJpZ54fP0ZjDnFDy+qj5hDUW2S4qiror1Q/kH0JNwCmEdcQeoo8JgZwvdl8pFegiOouamHXqOi6pw/nrWZBgGC4kInmz1Ui6lszowSI+39nDsRKYFequ8vnhCYz5linF2p+I1q2V6JwNEc5eGXNMSUwdxulM4+zJ5agQ3e5vDjQ+AeRO7NwpWyHyekCfdK66KztAgMBAAECggEBAJ03scxPuypj9UhTqGuWfrTHfPA6VipNOM1cEOwAGVCehLVIzltPfEi9Ugzl+JLhSRXxlfuglrNiXdtM3eigaMlJb+6KUC2aMoVciHCWModQ6c4FxZ0j2aIU9ajPp5EJKnqcUUzv0TMi+fuHhdmKXddTgOHp22e3qJBe3fbxfLSdDb/jqVzQeaOg4p7xvwn9cghRNWkOqRqMhBL/5FIw03+HCsapAgLB9i6+y038dnbhlJWqVhfIdoTRpc23s7vlra418U+Khkak+F9rymdPDoh26biTyenQSTGhoXDGozeuletJyz5x2uszP31WV1jPkECJa6kW6eyQWCRHDZ3gJIECgYEA3sNF5sogTGM7EMfoUgSSMYYbTrs9Gz5Uk26h/8yOZl+/eEzO3gFgzVTlu2bC/jJj777gRg23oNMu87f+Uay/gAcdz3c15NYm2wUHvFciAk1kE8QNhnIvkC2Kzk+kORgWD8x3qHOuaSLT/Khmfc7V23DqgWWKyFOyF8NoXmI3ob0CgYEAywbCQXdQRwqmCbaZlVcUBJKVY+3R5l882ZXcqtBrCOShQ1Mx3M+2m417ZYQDulDDddLanTG8WkuhFLVLO9iOK0CVgMbQt314cTPa8Ro3hW0T7kYGOGlATSpO3Y+46/9MfOsyehT4WdPK0lXu/Rcnow2IKLNQJOCa4eorZu2ksvECgYEAnqBXCn0seri+urhfyufOYs2obGwQm3HLMCE74rd7P5M2+SdYt+YrVIv7+3K1r+WaHILDmZ7y/+biLFL9GpP02eo3ZCDzk7ybdqMiWw+A/Dq35Qtaxj5ReE215iv4OV/Zde6X1rBpphxS8DvKoBPFXboOg44XQYe37gwMKgmuq9ECgYAdOO7S73J9lznI4iB/D1aRRev8wylYKFMg2mI1r+QIFqhjgWEG8FrPTvD47qR+t8s6dUwEHjmHIaWgzmtyxLvJ2/To4TT/hC7G1HjqBSUCrm2U+T1B91xK/xD08Q/j4A5JWK0eR1Br1YE2/yl0AlYxMOxtN0oM1MtWQxdWLFRtcQKBgQDZu5bIG3PrKR2ySDnTwlY9nwPqKSVkzSlzipF3xs0cXtZv+caONI3oAeX4V0CUZ/q47wvZ7b36z3iEQUQqnfDDQaGafqHOaKpTVBuBrRGfwRb4sO/9OLAU2vyYvmoYzuSkBCVNqB3pP8Hc3/Yoz8jJ/Dk8UBkHJuNPGNdNCrhFvw==", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCwqroKmBCeb9LTRqENrnRehdCTTk8eJxRaF6SGUiQDlL7waBkXKGzA3FyA8Z4wlx5zUCjKtwObpRd5Ijvp1bWvivc61kA+RSAMtSaSBXlfzRZs35h+GxYNWvBfZZBsgoEVTQ0zOr+nvPy4C+ADuSJpZ54fP0ZjDnFDy+qj5hDUW2S4qiror1Q/kH0JNwCmEdcQeoo8JgZwvdl8pFegiOouamHXqOi6pw/nrWZBgGC4kInmz1Ui6lszowSI+39nDsRKYFequ8vnhCYz5linF2p+I1q2V6JwNEc5eGXNMSUwdxulM4+zJ5agQ3e5vDjQ+AeRO7NwpWyHyekCfdK66KztAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmWEukYe7VLXaSFG27QMR2qMKLuSJGBiiFQCVA2yrm4LGx", - "privKey": "CAASqAkwggSkAgEAAoIBAQC9IEf/7M1moPkYTNHUkIQbeqbPS586tVtUxJiqZmfua8G3lhEeee41V5P/4I9ytGIqWldkLHBynG3asN4mPM5gXhz6vJdvROB4b9FmGea6XRc3zd1ZWZe7aaL/zL0bdKFLxCprjaF5ijW11tl7fD1XuBtvgh9FyJpOQi8AfcMQfy1sBbr1TX+a0C7mshEjMfN3B3OKSo8eA2RqAcXq4GkVdhBFuMyCFzhMKV43B8s/MKEgkAAALLNpEfw7kZc2pHIqAk3T4tYd/cEFCrFPcsjxo+jBicZg7kZEA+9AFLfB8734o4jJi215wTPwCVkc5b6ssEP7yH3BGK5KsVHpHjObAgMBAAECggEAXx/UdvnhGeSPRVSmGXcSq0uWiR8tGHdNV6aGbvaRAc97IN6+/4gucu/4xbNqEzR9R3YnDIB5knvxmRRqt+rPlpLfmpGuzU1kZc9AEE2oykW2PuAxnBY/BgmM7YJJ/3w7AIPLHkufUyVb/Hjy7HRB2lQEoKJfHldWnVQWlfWrXijrgLf9FqwcNC+H5j0WCga/A0Tbf6pTGrB16MtaiuThZdy/U0S8pWL1ByMB6mjsYORxXBBa5QSHecyltqQCn3mBnbZdI7t0Q1O9wo07bHpz7QxqxlAume1IY2uPSSpI8QMHBMBNKNpa3525tBGhZVCNPyDrXXF6NRAwgC4Zkqz0eQKBgQDse+5GJhocm5TO9sZ3N+PRPL9PeMn2WPURClCFm7iS1zG+oDzBxYu8ZOPAjQi+gx0+xlfcOn+/TFidFBqRLleZBzg1+9Qys62Xe4IdNOZpseZz+sdg2N8PYqBXBoPEyIK0DQt1CP9kSqktG2soKdsU/52mug6MmtvEHX9zmDvVdwKBgQDMu9gRwsXsAenSApks7xzDXMXbA7/myitn3/NU9zD/s58+wD0otKRRPhNONDbON69uH0aB925OkEUZQ10TCYS70QS02K5/4uB7gBT+PW94eFCU/jf+jLpVjz7WzsZ/Lz8h44W74BzrAx3TW3IfKEGytfLCy8/kaJES1oK5oTjr/QKBgFfW5LeLuZE8vPZvNWLdELMMpGcJj8MAYe71bNlj8Rgh9KlA7bBwBypwMyS3fjL9kqRZmhMEa6UL37Jg4Eli9Ei0JM3wf25hzS4CQ19D4f4KhXY5BUvU4m3djX8lvVYfwGTOn53WPL7s+I/3qkLd4TGYjN98JqFVeCINbuTp+/ebAoGBAMeVFz25MliwRNCF1+0F7HRGrFqlfR3vWAEbQItDrnCXGlaB8R0NfGH2sbs7C3JctpgTxRhNrSrJWZMXKFS2or61NHFYCkSBV3UNl2mBWnmGUIfui4eKiNt/mTKuwLKbzF+s/WH5SDeSAjFYpBfblrAwz0c2iKORjFtg4m8zy9nBAoGBALiyvnOoHqnuh8vpSu/icuEROGHpmcp8PyGMlbkc0I5dmIehOaJw1bLfWoL6GLChufwDO6djpIao8UJAPCgHsxYMXwZjxkhZEQ3RL79tg/YiulRR5O25lXNk61vMALIcUmQeCow64IfBcY0PVDgtQJXb2QhCCd8s3MOURCkqKHa3", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9IEf/7M1moPkYTNHUkIQbeqbPS586tVtUxJiqZmfua8G3lhEeee41V5P/4I9ytGIqWldkLHBynG3asN4mPM5gXhz6vJdvROB4b9FmGea6XRc3zd1ZWZe7aaL/zL0bdKFLxCprjaF5ijW11tl7fD1XuBtvgh9FyJpOQi8AfcMQfy1sBbr1TX+a0C7mshEjMfN3B3OKSo8eA2RqAcXq4GkVdhBFuMyCFzhMKV43B8s/MKEgkAAALLNpEfw7kZc2pHIqAk3T4tYd/cEFCrFPcsjxo+jBicZg7kZEA+9AFLfB8734o4jJi215wTPwCVkc5b6ssEP7yH3BGK5KsVHpHjObAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmSe3T2985rtAoiy39uahpYsjEJd8GK7sFgXAMbnX4AQY2", - "privKey": "CAASpwkwggSjAgEAAoIBAQDt/lYSWdZdTVdNmEc15XXHZFPtPutZEdhJzzltTuN38Sl2jWymsn8CagHepR3Scs9FwzV/tn81ROZtRS3YcNZBa7w54tWSb5V2Jvt6kyGstqBQqoWnuyEmKqYUPasvQEY0Xg69IHsdtvL5XnheQAtOGiIQZCwyrwJBU36Oi0Vgp9+n42hK9tDwcrqKweYL8/Xr9V3LT2OufZ+UXd3NRsd2G2u2bLQbBzjkbo5MjEWGJwbLTDRwg+VI+lIz9vqnMk+Hq32ymwUqqmumzmkkJ0hQWTxQmt0vBbOtSdknB4be7B34LZaf6HMlm04J0gJHELqwtRK97mD18t8yZBL0eCeTAgMBAAECggEANGE94FwVagOTq2hQg/Q0r+XM8vJeKgRbbiNFqGEsf0F8trL5rtaqTYW3U6FTpvXN2LTWGX25EahQbsxDAtgSz+M+Uh8ykkAszQxXXOr1BmZLcnWVZQ0yhovscZgBDS1ARlZNOCLl9exGHcxFAblmw5HM3X6um5kZDfeqawUMB/F9+Oei5rx0DpI6z00zJjT096Xu/TbOTSDLJUhGqHUC2dI3bo+VjVlBzSLdot+cXfsxiP0kWz6Aico/ftWEZNoG6eXNJBpQifPvCEMGImMIwI0jpolW4cUdu8tSO2q5QbpCHhUHwAQC5jmQTrHoX3pl4s6aE37xxCmm6w9TMZDpwQKBgQD/axU/3KRs60wmdD9uLcGmP2BIRt0PeNiF/FO3p8GTJCHwN5/uKtxnh54oilWSKYuAPRO2MAZenf1Z9LsPt8vkBYeGN0gx4CW6AeH8hT24VqJUbjniPYEZFnLc8ooN9uFxe4/eukq0RVCPwz79U3HHupXYXoHSm4bpA3qDJ/iNLQKBgQDuiRgQmzNjZgPo034F71/5aDxeBCF6DMTmXwruVU3pzGsMcuP8X4FNWDAL7AGci3h2uI5MsS0YVwZ1vjjg9EFv1xsuQR5+BiTWD2BQIKqbaaL0B6Hp2ZfsNvsVpJIehksRJgUfUJtHtearYxL3AzCsTV0MDFgooupqhx3X6WP/vwKBgFmrXmptK8yRTsqxRROJPNMArOyy9CjaZCmlzD5NxsfBh6it3pfetEIkeoIBDsmhjDgZOTJc6d+N18QdBw8dl5cV2d5kyhO4fYYv4wakQGbXA2ZgzDGBJjGIkArBm3YLllog5wFqpY9kRkQyZ4rIIMnd131+sFUgBN0JO5mQDtKBAoGAZm28RbU/ddliqGHY5deKkOCvu3duoKhHDN2XJgy/bjv3Y9saB09DiODrkNMBRiWlzuUlRc13HdKQ1ZKffgmk58+ovk38OAWPX9QueXntiNrtvHhikLZ9RFO/seV/UVg9d9mprW7BnyN/L+1VQXi/N93orLnISXrbym7G4+Y2qKUCgYEA2Mw4KUSDpHP1CJmSqqQog09vPhPPT0mU/8fj+GdVxwP6nv6u8z0Re/49ly9+1RD0dyXerKQOAGd9rvAjkmmfqr+S18gNRAkepW7DPiveL1rvRH0O1fdhSUAKDXDiWou9mdPUH6U0290AjjbI+ZAh83Af2VmJOvPW+hs1sCvkF6Y=", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDt/lYSWdZdTVdNmEc15XXHZFPtPutZEdhJzzltTuN38Sl2jWymsn8CagHepR3Scs9FwzV/tn81ROZtRS3YcNZBa7w54tWSb5V2Jvt6kyGstqBQqoWnuyEmKqYUPasvQEY0Xg69IHsdtvL5XnheQAtOGiIQZCwyrwJBU36Oi0Vgp9+n42hK9tDwcrqKweYL8/Xr9V3LT2OufZ+UXd3NRsd2G2u2bLQbBzjkbo5MjEWGJwbLTDRwg+VI+lIz9vqnMk+Hq32ymwUqqmumzmkkJ0hQWTxQmt0vBbOtSdknB4be7B34LZaf6HMlm04J0gJHELqwtRK97mD18t8yZBL0eCeTAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmZ2rBVRQBbSKaDDAY7WhzcoN9AjvcebTvUPhRFyAk6Lj8", - "privKey": "CAASqQkwggSlAgEAAoIBAQDQ4LLRPd47qKngVWvUziPeFPDPISVIi8HgUFJ1Vq/CbLKZDrB20JjLvXwA0RpYkFfZHC84Fjmh4+LwYuV5qJy+5X7IdtnXjUO2yXyykkJ55jDZb7ZEx/5jrYSK+FY7GRaYpQU5dP9R33FnRdVm7GfSETmSwKv/NRQe9KTLKM+K9NvkLPfemau1YaGA2lF4h1TgRbHZYaz/Pp05Y2wv/iRXPRX4wE3k7b7k9oH9TL1UkoNn7L9G9/ZXA7zO8F51YNk0kGfYSmZSIcT6CyfU6SgZdojOYyQQ1KrUKy5lbByaHRh5PW5H8f6f1p8V6Ja/iUMwUg2YQobkKKsY4G3sB86jAgMBAAECggEBAMQsdOU91O11D+7oazjXTipywmPWfnyu/axd48PeYX4Ztnc3q5Y7fXXEhaUCvlq1XjxDUzm67e/U5rvcNidXq7dCNRuzPA9M1m7it2HDKfnwrqpYV/grWQlm2xfl+p7Qhj9gpRJ8hprvX0Od+7oJh8xsbwUcPa2XvUkBfZBsyNd4RAcDR3i7hRo77be1KSVfsGmNFnI1eGkSCS/QPG4aiQk7Od62V5ie0/lHM0hDlUnrl2SRqJGQRdwCp4DPH0TmxhPe4WMl277Pmfd8SRgcRW2WNiLdNXY+PK0cecvkEyr9kpK8eJ0g9loAOkFdcWTHwYplq8NSyVtnMKwvcji3FtECgYEA8h4Pxj9NH8teqfWqIQlDDGeDKxAE3EyPgREGCaFngV/OJ06fB05zt5uyoX8tLXotzSQjjgRk25d1577OkahDJGtcgq/zBDLE1XiaE8eICXn0/MksaMT2ZY0Pp/UEWql9cnnLY3ts/+q8qFloe6Gi+thdUYtWl+1hPhZonKtxn3sCgYEA3Nq5u+fdILAh+DCcWCtAtyZhsXENNvxmJAgVdsK/nbsrl01EiBYXFouxxIl9peHNJaEs4OAMky8EZjYbTZbcFltjh2KdPJiJ2vel30iQBjjPS46uQHh1s5f7SsdXJ2xI3YiheuK0ySiS35vl6AZbrXCAn3Kwi9aOR1wDm+XkEPkCgYEA3Ch7vZBICBY8YR2y8tFiN4BUpK6vTMcNYpZhQBaVcO32HoX+U32B+b5JY1KqeQT1aulmrzfNomQKYY1+drJjQ1WgzHFD8Fhd5aMBr+SrDbrpC4e+qxIW32ayis5ghDREjviy+iX8ioUfwZFzUaA7/A8MZB7owcOnvfZQb83xxssCgYALEfefYJbn7Yw2WZFspfZfd9ALyePkrrAb/D+/LTHXoSslMV1PCPRtT+FAPbgLmY7j5PlP6EsZEZFB4lJqCDbN9BTAE4RYJjk6vZEV6Rg3B5/0ZJl9Z8xWjTauX+GRe08Hs7KMa1KuhpceGD1k7PSpc+suktwglkeZchZIOTS+WQKBgQDvzWu1RFnaWiSpdkVguMP+W8RqAhH+4QW9NJC9S7RrXZ/OvEOdBp8BX8Gm0FbBKEpUl6hreaf/sPm7fudw+fNTDmRZL8yNOTbrCc9CpkZcgl/lm6Q5t7z8Ot2thlZNmTgdIkdXX9jIh+1BmuKpynzP31unvXWczzCnyiy81l+HAQ==", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDQ4LLRPd47qKngVWvUziPeFPDPISVIi8HgUFJ1Vq/CbLKZDrB20JjLvXwA0RpYkFfZHC84Fjmh4+LwYuV5qJy+5X7IdtnXjUO2yXyykkJ55jDZb7ZEx/5jrYSK+FY7GRaYpQU5dP9R33FnRdVm7GfSETmSwKv/NRQe9KTLKM+K9NvkLPfemau1YaGA2lF4h1TgRbHZYaz/Pp05Y2wv/iRXPRX4wE3k7b7k9oH9TL1UkoNn7L9G9/ZXA7zO8F51YNk0kGfYSmZSIcT6CyfU6SgZdojOYyQQ1KrUKy5lbByaHRh5PW5H8f6f1p8V6Ja/iUMwUg2YQobkKKsY4G3sB86jAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmdbyUiDo6RdJKSP4yiM7FkycMWx1DycYf5prttoCi7nkH", - "privKey": "CAASqQkwggSlAgEAAoIBAQDU1+WqcQTmVY8dZbZAMaprvG8QzsVGX+ssQT1jDQs8G+GQdGK6yg4OwCW6ZGebGg8Wcnhm9izafQGc94rk7su4+jKkDttQv+GEpZf8yv//jdTzV1bhpDIdE7+yMZgN5ViR3YrFi10vs7Sl+E9KvBQJPD1vsDngjsa3rGLG9CoKtY7EpXi2Rhe7Un8+tp0J7uIJRtyZ0Tgu0K3cLir7Fl3YVBv1PzsY2qaUA3ZfQNy6Yux0ANltU0oO+P2DTknfkuCIC7+QpzECV9z8eIc8vQzRSFLXKRDKqN+1Ucmf698VBdGVUt7Nmyo3q/jeHJWyKce0hArUGhwij5/uoYg9TZx9AgMBAAECggEBAK8KUuVmBxqKWKVbhZOrhLUPheOzuMeUkKqXiK2SB6BKaanMHXnyO7djzGNKuW3z816Ji31ZjS+uSIpXhhGaVU5t7QHA+hqhgwz8xk7uf7QiZ3QsatYsm84P9MHOSXd8Gufy43Jsl5loV/N6j3Mt0+h4cyoMKr0Djmd1TNLD8GNWxhahfGdhFsBEp20EcAe0BTMwHVUgKKOpu7kElAiJUYRVeqvozNC+kkzTLC5m8dcNW9Y5mRNHv6IpBDmUBCxnUubDakbCh8rI759+XNBE3SrDDBiZE9CO2DSZV/QM1lCIMnjcoEmENVZj3XcJYoAiGrMAVqGlxQYZmjQTBI0TzCECgYEA+gnxXm70lJCxPjRMqzI+4RPUeKc+TwIq1RLPOP8OP70VpG1TvBGny9YkuBxP5xsDB3W2S8jRYao3nwmKq3AJurfUt9GRJY1OreN3iX7aEBMMoPe5HTEZOrTm72RCt8eaeezxJuOLYxJLD3PYT/JHuuUD9d5VDHawE7OLlYbyEGkCgYEA2erwlOoRZ1hh/2eddfeRZEkeKhur625wt+29Ga2T3HmvLzVorHsq/krEE+qqZTDNk9I64dylbCYS6qJ6cJzEBlhyfimyQOooYiuUlcP/FvuXEAoOc1BwhmRNl1DkszX2XUMhNyWDNVCWJym47M1ksHlTExMOHV2cz8d9IToHqPUCgYEA1+KH1XpFkJSRhFzRqaq7YcimVfpIsRz08H3KD7MgkWXn7s06VBKGZ1eg4poHX0oSRnmbCTn9lq7KUXWCll0o+V9JueCmyt6EBV1103CERQa9i6n32b2PxAF3t1BAzr73oLg0ytgCfGrKBjCGnxhYWITt83agxh8gDhKivVsDW6kCgYAwfJvnJmWU7w9u+qkIdHs/Kx2xFNMd4UbnRdiLfBmoNtMJ2AJgTk90oUIbhF1BgqhbOa2sT6Hm/Fm9J0XDBL6BAvEGrVRiKTevEC9RW3jIrlYgVXx9n+pJnMu+3VrlnR4iBiu/z3LwS+v87sWcut6qfXREjDrZwdiASszGtdi6eQKBgQCWc2lKE2IapSzG8BF0ai6d6T8PmgwOcLnwSD6YU4Oe1lKaTBbtaS5KaemPnSAV7CJ0KR9Xf7qxDcdZBB7RnSBIIhi8sz0UW8s529nDKYhSrVi7iY1EQVK2YmXjDg+q76qTl2+/C+6EyfJHrU4tUBDbpjjOEZxcwwMYV6vydiR2sA==", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDU1+WqcQTmVY8dZbZAMaprvG8QzsVGX+ssQT1jDQs8G+GQdGK6yg4OwCW6ZGebGg8Wcnhm9izafQGc94rk7su4+jKkDttQv+GEpZf8yv//jdTzV1bhpDIdE7+yMZgN5ViR3YrFi10vs7Sl+E9KvBQJPD1vsDngjsa3rGLG9CoKtY7EpXi2Rhe7Un8+tp0J7uIJRtyZ0Tgu0K3cLir7Fl3YVBv1PzsY2qaUA3ZfQNy6Yux0ANltU0oO+P2DTknfkuCIC7+QpzECV9z8eIc8vQzRSFLXKRDKqN+1Ucmf698VBdGVUt7Nmyo3q/jeHJWyKce0hArUGhwij5/uoYg9TZx9AgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmVoKJ8hSEjXCfeGUVdRxrQYGPCAR8GEmvS9TcgAQThG4L", - "privKey": "CAASqQkwggSlAgEAAoIBAQCubDylrliDnatd6ZVnl3T73E9gxl+KMV9Y4Gr9GVQcGYyYNlJMr6SezMxfk8hzkxyIlJjnzI4lXWPqAxloRdmps/7f4tCSnOTSUo+ZDsEjazVNYGntxjaOoEnSzkM+MbvOlq3McvW3XSmMDbRVOU79tfNP6PMDZHOxXnRMDaPRh7U23xVE08/gfjnOXQSEg05+EhnKzizkWeqJvRbfRKbY7ZnCL8fNl9Wt75PoxJnSdhdcpL6Qba4q6UkaEx6MJ0EidT2LClSCRg+/z+CtuFi4RgYTBJejdn4COK/Ho7h+MMZi5ZBx7uhZZ/6wefh5hWc+vUCozAQnfLsg66c6C34VAgMBAAECggEAZL+yXEUTbZrCJHHK0dZjRSOhWhXbk7gnCfA+/EkIE18Snc0qxo7h+LP1DPQQ4elEnwOuOp4mMSD7mG0H3PoT2vlULEAYF8e2SGJV/aPPHcVMOZCKP0SxuLqPScvIfYE+qPrSEvkIQ0z1taco1d1Pai8SBsNYs0nvpbEYXeG3EUxsrVcB7Dh68ydbHYlhUKMZXe0cNHyndrK9Jox9Z41kKxYcKpbzORi1pfnd+vIYC1+3geoWGaJTW0xnzpXpUl4EmG52NMD8uX6dpbJXpIpdMcKdCspUH2riSPOGTDwthvKW3Ews7fxGmEblDR7pYClrWPCn6UYiKTZxiZesNh6FlQKBgQDblX3MrWlbhG8ih18SFdJSlagy2OB5ecgf/zkJRsVNmyTO2+wv0dTsTLcwFafe7S0srqLxHc1ZEzryy+TxOHfplB5orxLYookAjZ8WaoZC50avQfkHMEIeJmo3OAem9yzbPJ6dVarR1TX8ZpID4g0d2ge4CWo+VwzY94VeEkDgLwKBgQDLWWn/s73pRJjl+Ll56ItwEdajSZf6VXLJFBIMeQn8GJ8o9pFn7YGUVbhkxBQPd1UjO0Eidn+YL/CywfH9ePcRIHPaOjI22Cl74hnCz5ManDrTH/S78Tu1CQeWKacbUaFTxLgNqo6z/NZww2CCgV051IJZJHvqBp2DQSYHRm9Q+wKBgQCQtaYgGzBRxadQBBKdYpAnKMWeLNtScvV2UMaP3HnuuQ263ah7ozdFOxGGuN7WxUt+JODxMgjAaTHyDHkml2Y/IwQfTTGIXyUWnj53kWBF+xDUMxAgsqcAI6TgGya/3ClNmleVrH1Up8RaQGZ99J1cTPHFUT8ZMlkfK5BS/IiQtQKBgQCpYGTGM7Tv489nXnE/dc8PHgymHdqVDS97BVizQu5qKSgJOreK1W2lXHEmnZwH9eHYYrayOfm1jdjzTFCATI2emmVlVCwXOp3zLjU+6x8gfxkQWgHDuf99n3PORAuI2cmCuMyFtZb/nI4RhuuQSKiaTsPz9Euydqgkd9NxI938mQKBgQDRcf0BavVuHEZcSegoI0wVLynCUapeCbnUFv32Cer9/V/YV8/ZcjTS1P/YlhZB5L16LguXDVfgXa03zeTeItjsW6feR3ZJo3WzwS9CEEuLg4OFu36hXsO/Tncv8MGO8weVYLGjeHhrbaPWmoWGhmsm7VP6430O1MAFSoZj6zyBzw==", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCubDylrliDnatd6ZVnl3T73E9gxl+KMV9Y4Gr9GVQcGYyYNlJMr6SezMxfk8hzkxyIlJjnzI4lXWPqAxloRdmps/7f4tCSnOTSUo+ZDsEjazVNYGntxjaOoEnSzkM+MbvOlq3McvW3XSmMDbRVOU79tfNP6PMDZHOxXnRMDaPRh7U23xVE08/gfjnOXQSEg05+EhnKzizkWeqJvRbfRKbY7ZnCL8fNl9Wt75PoxJnSdhdcpL6Qba4q6UkaEx6MJ0EidT2LClSCRg+/z+CtuFi4RgYTBJejdn4COK/Ho7h+MMZi5ZBx7uhZZ/6wefh5hWc+vUCozAQnfLsg66c6C34VAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "Qmc5QckxBKLCgQVhMBisisvainagMKYDEeHQkXzPU5ppgx", - "privKey": "CAASqAkwggSkAgEAAoIBAQDBXIuC7gUixPiZ5TkXpTbZMzAt2fdfNV8gci9Rs7mhdtcAIWoMTTqcEEvuY0qA4VeoyiHOZrwWijcN1qhL/iWIwm+35ceqARa4WKzxqdFNUUqrcQ41tm5D3hk4Mc7+26KhRf5Rb6w4ZQxnadRHD26GcYOtGP+LLTKGFreWJOFcbwGCnsDkrW2f/jhsVeDwXAohfuQw2x9YkhhW8GBKeXnVrzdKneX98j2TmdQ7XxbqpmtYbSRscvvYjK3sliHJ4iFpqpXpHXIRuflgmcWOjNMQXBs1tNZpo5aWrjFngotGIqLWoMMCi3WgoMfhby246KpsgmtFDgQB0fdol1HmPeaFAgMBAAECggEAZQW03fL9O+0s9TqNWY032sKjqVD3rQZ1bL47erQrh/BO5AKRJVw0AtWA1kuJ4UvaQJValDuYiS4tFU3RH+LoOUtckve6GVf4RtgNgzT15S9Tk769bdKiSVMAWhuryft2PEwVUvbFQ7GHiYABKB8n35Xu9cDZwh0bCHNV91vNYjyygNg+VcUZK9TP6djcZB4YvdWv/WtpKgcAOOcPtgZbp3GzFHL/BsSNBHGg6Lynt9StYaZ6mqPYeYd6klTa2bVe/xkVrJ1cCPkWcFBk700JoPw/La2bARHjew46BSLf69wDWTQfl2QdUMNHVjwIZfIQN2dfldJFx2bYGAEhi7NIAQKBgQD8GKDmhw7ot1o5ha1Wl+Uvx7tfgtOmnouybrX1Ce0B8peDfD4MAUhy8nVwEjyoGVlzbY89odT3hSgp9KGD5h+38spAo9Rr2Hb5V2QxehlHZCewr+bESuL02bvxZnAxSFjJV0B6/A9anyBWKzw4hSviEYjvE+W/VYJLccZaP8V+yQKBgQDEWxPezoVJNVYIAKvX4ssX3nDfc+zdh+aAOcI/+CEmrWZ1E3kRD8bVFt1LFND2zIrlU/1FhuuhiL8KtsN4slonCdYhEi6kUnbsG5SkWfB8+TM391qvrX1eLBD0u/NzPfBBaeg7BQWFUSsXFWVzRfZG2VzXm3222AABJYVpcD5b3QKBgQCnkweBtc1nTFohWoa6xQWIGVCoUKK4YzOhTI6PcCWn4cZtlKz59fBe2GTQNo8zfoZDgFRzN5wFXPIx0Xd74gC7mhxvk3ekqKONY1YqvWsIVb88Z/ESEmWDNSkFcn6pg9nhHKq0FdFu/8/S97J0L7HX+Kf5pFRYN1MBK4QagcGaYQKBgQCdEV3rtLfZv9h5vk+3+asMBNu1Yz3uV2+C0rEYCpw6HCsBK/qEM2KRwiBylswxH51bpLvMigiixohLQbdLLSAAalXnTmwQ9gY7CDT24xsEXTMjabIZJWZLlmRZ4J71aG5vZRBnZbTs1+joJi1o8GX4dpdVwQPm5xHZ2PHHTgoT4QKBgHU4FGuQxOvbPXe2kEoxtonjTXZFd3ALLn7WHVCrlBEYkj58j0aIx7RsF/1dlVPM4j9SzAldk5Eze3cZdsu+Ae8wfLerxMwmQEWlwNGmttp45t6sfVq5Y0Sas5bOSRTfUQFXzrAxFo+5ZETYKN6OkDnEv3xbTQTnKaPV2pcqFx52", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDBXIuC7gUixPiZ5TkXpTbZMzAt2fdfNV8gci9Rs7mhdtcAIWoMTTqcEEvuY0qA4VeoyiHOZrwWijcN1qhL/iWIwm+35ceqARa4WKzxqdFNUUqrcQ41tm5D3hk4Mc7+26KhRf5Rb6w4ZQxnadRHD26GcYOtGP+LLTKGFreWJOFcbwGCnsDkrW2f/jhsVeDwXAohfuQw2x9YkhhW8GBKeXnVrzdKneX98j2TmdQ7XxbqpmtYbSRscvvYjK3sliHJ4iFpqpXpHXIRuflgmcWOjNMQXBs1tNZpo5aWrjFngotGIqLWoMMCi3WgoMfhby246KpsgmtFDgQB0fdol1HmPeaFAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmSBxDrxA5Cs9XCPHZkTQML8frDa7uFoCRHQDtGrkXCcC6", - "privKey": "CAASpwkwggSjAgEAAoIBAQDB1rGs8/efBrlhYevYWLmiREq1AmubenyJHL58MAWwgk/wFQZbTEHGQQQZT0AoR4vtcn1seODzUKxNCH4tpYOK+F7/ey0gtoJwL3YI0ZwL3kPIq2MJW2ZnqUZ/v/VkX5QtTUPfyfZfQnRYcZ54pWYHdFzqUbOwN5wD+VBWq+V6Fu2aIFuP7/RUPeRZ83CXHxsD54hTEDlBmc9lut3rVtFB0s6wvO+pRmt9uLUg93GtG/oA+qeEUXNj0l6XtoZq0CdJ2kj8bhKrz/oqx63g3kg7Pyv1L8Aa/MC7GjJ+o4R3bIG+sk7lDsm2bMI5HkfrEVgE+FlEvCS2FaDTk/jVHaDTAgMBAAECggEAOqCjELqhlJnGDCw/1ynOy8N4DRN0VIxRim8FNi6YKfDgGK9jQs3nvvz/LmCH+SbarbDJOru83hryYkJFV60OAkRpB0DMP260ORZBzx0G45gQTGt6AuSALq5GQnFe2UMHYERUWSWOvPUul2mWEsuD9pE9YSng/VV0fMc1g2FugORTm2SYenOA/tZfm6kPo/H9Y0aiVEKv5O9Js8GhGa35zCrF0Rhf/awthNLQWb0+vi92iBafrEilZfuSpD9mgbttRaCTaN00YwC4749jxlQyIwuUEodWwAy3NFQ1SuoiWNfUJbmHtuHf+2citsLBeUWcev7QWf4t+aFHQFUh7sIo0QKBgQDl3uRHHPV2DwfRbcyThpeGAg/SkgnsnE+CuSKCtTdK0l55Ck0EIeElsgB15FCinntJ3ZADU9l9gtf4toqM7OeqowwQ+8kEaqzMPILOicDs1SHUAKtxzUJjD3WuGnEgYKd2JjV7JDvuQRHwyht2Qhaken0Va+wJIal+5tLZ+z47hQKBgQDX30pkLCc2QJHrUyca42jaZVAvh8W/no8nXMN7LAJ/xPhGGi2tnTGfkUnIg/+8IZWLzS5198swPwm7nH1UN7QQq0atC4Ld2Lf8dn3cmyAJjcRll4lt9AB6VAJp5rkAAud9VJXCn2e0J5elXoM3YLgW2VlpFUy2JfUmEAh1kDr+dwKBgAZYVbLE0N22Yn/caQY1c99GFUu5rj5yvhscoyA6glE1Z1gt+ZxAlydkN3EJoVQrzblnPT9qRBmbz/xUhZSIQYjLQV0CpjTSAP0OOooa8VFYPLvOXO0iPk/fsF7i6fZ71IOFYHqKsIDOGQGtgn6MKnXVz7gUp4pE/Jm9I1rS/Y/FAoGAXxJhEfL8JgGUAj7x5v6mjCC4iuZR6g1r4JsTIKkGRL071qvq2B5131++TggMVg+4bASmZKAIJaxtnenSrIeHzxuPmeCK9ydeCFsrHUBYgLyl9VQi24DtwPJEyd0qNt4Qk3rwJfHMW2Rgfh08zuPSz4VTwlr2GPZonCXNg/FMegsCgYEAt3cw21h3X3VHmAxgWEU11XjNjDKyCrWUW4aYEUjGso0M5dksFLDSNezCls/k7Urp8agl1wRE9Okr1RILcle5bbhPVqQ1oXobANSjIEVhCwmCjnOuYMBJA/tQdKHqyOFe3Z/Sq42n7ydMdimSmBb+kf5uwLL8hWjPNDZJrxBl+YI=", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDB1rGs8/efBrlhYevYWLmiREq1AmubenyJHL58MAWwgk/wFQZbTEHGQQQZT0AoR4vtcn1seODzUKxNCH4tpYOK+F7/ey0gtoJwL3YI0ZwL3kPIq2MJW2ZnqUZ/v/VkX5QtTUPfyfZfQnRYcZ54pWYHdFzqUbOwN5wD+VBWq+V6Fu2aIFuP7/RUPeRZ83CXHxsD54hTEDlBmc9lut3rVtFB0s6wvO+pRmt9uLUg93GtG/oA+qeEUXNj0l6XtoZq0CdJ2kj8bhKrz/oqx63g3kg7Pyv1L8Aa/MC7GjJ+o4R3bIG+sk7lDsm2bMI5HkfrEVgE+FlEvCS2FaDTk/jVHaDTAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmXmdPtQ6bT9pNyHKjfDUB7of3cmXQtJDBYKKeUGTEn27d", - "privKey": "CAASqAkwggSkAgEAAoIBAQD6xCvltFpdm2M8dlK6d6laqnIjuZRUtxFn+sdK2LqCsJVgg/a/eT6t8R8vKphjRN5piOzVJzo8NZC2X9UW4TeL6MBNRKrWzCFsTKwdEQM14Npjcu3wufi00p8ZoaG/JeEucXmmRzK10cXTYTG1hFcBWxWi4IHwsHM8+mNCNoK46S4Ly7ehQTi+1QLWG/lvd2FLN26FFNEhadikTd7+Psq7pvYC4XIAieU3LturRbfvGKzqEUwFTrayFsmxTo+KfiLDOyjCPjkwLL7+VsDmZUpHKkAetZpVx2NSBLD0p42qUyG4AjplfCQFi+TaJZqIEqrXAdumNWYgjBNR/hkAt7MlAgMBAAECggEBAJJ06D5sKyroifjSElcddCejzK3YwS0JDn1wFd083xFdGKEZ8Y66vUTRwqjFc+LmYg+5DLkhA/4OOsqJBecq+koYUdfO9wgkiJC75vnC6eEZxfK3OQiTVRImwQ0zPUhqUy3Q0H+wrYlLTwK5jVK6TCZakDRkcv+jzmoawsX1GDvtrCV0MzQkZL24tz1SDDSBDCijaEtyfqBqWOLLhDjocvYuEKsse+pu2PfSMH36/0dLaEwf2h5tib4OUyuTfA5knjbcEfsSFZHQvf84Y55baOuJbf3kHqEOKJ2WG+IYpAsVnpCN5Cwthj9k4TCpDaWCBsNFmXKBNZ8ZNJqXf+J30bECgYEA/aP8hDGYnbtMM9PZUzsS9RyzVx1k/IyJ9bOOD+RbHzomf7YTWeOv3bvbFzoyF1acmN+cY192F2w7t9Owckk2qSk1VuKHXjw/E3v6xLRnJTvhi1/+i7887A9jghm3MvfPtsyArRagcWxD2mnHum7QeQQbBG+Om64XVE/N2Z9JZlcCgYEA/RlXIPlprSo8gKKwmBa2+mPB6VgLXZCB3hcp+MC3hzzmbPtOup42Ap8g4YVpk5QNbizOfWEsYoT3O4jEFE6SKlzSvhvvWnw/G4ZGq7QnIp3ZKY2pn5vHenjri/RZzO9JATQ+nGjgqFHGvUXXUZ8bNq8oP0Fjc9hrdv2c7b5tLOMCgYEAkB85Ighodt/xWdW7vG5pxDttsEd0lYhp7+H6DA+us1zAeXsFHeOhj7XptRYNVnORgdA1tcWNfZuzhy3TKe1uEMrokxke4C4NjU26XUFBBsgyzZZbNh8RR/Uqjsd78IsdTPqA91lPC4QAPkAzDD1hWhI6I9gbyVwvx2mdR1YaR/sCgYB7N/UFJqfeGCvwbEQRJy3Z5Oso0SZnXMz89MYIRrqS6oE8GXUQwamFyTbW1H67zF5lfwbgX4ieRiGfKExdnormeN5Yk30Jzmdi3RJW0ZQj9DkfU8p62/pXk7sJHeMCNJSUM30v5JdLGtTonLHhGNbE3q13bjwe0AQxn/Lgg87fBQKBgFIJuMQ+RsrwK7XJ+Zm288rKo6kbRx8SVm0mjhCHaZCpcJbhlFB+8yvE++n+TKtgDp+pFrjkQf8yYO9p6+B/tCbQ6JHNCfdl4INMGAaCEAxqLnZPzB5ToxRYYHJ6RDVcBtX2inpZAKr1uKra/cLCWWg/cJLH3ozITCL5tHOMWevE", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQD6xCvltFpdm2M8dlK6d6laqnIjuZRUtxFn+sdK2LqCsJVgg/a/eT6t8R8vKphjRN5piOzVJzo8NZC2X9UW4TeL6MBNRKrWzCFsTKwdEQM14Npjcu3wufi00p8ZoaG/JeEucXmmRzK10cXTYTG1hFcBWxWi4IHwsHM8+mNCNoK46S4Ly7ehQTi+1QLWG/lvd2FLN26FFNEhadikTd7+Psq7pvYC4XIAieU3LturRbfvGKzqEUwFTrayFsmxTo+KfiLDOyjCPjkwLL7+VsDmZUpHKkAetZpVx2NSBLD0p42qUyG4AjplfCQFi+TaJZqIEqrXAdumNWYgjBNR/hkAt7MlAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmcndsRHcVLGUgUMVjXczHf4qU84WtjFb8cKjgQHxPBkjA", - "privKey": "CAASqAkwggSkAgEAAoIBAQC4olOupPQAcn9YS66bRt+6p671m14N78z8Hv0pM+kF8QmA8G9DR9z+GJxFwZanQkIiKcwpw+o26lPYI8XGBh/ohTWTRgqIV3Ra/SGyIgMEIKjUJcoy0+/rfS6AgRsDuGWYWPTGs3i4OCc7CB22in+I61FX6zzHtxIiD4ADpjLoFxIdVEoOjp2Q3IErn9dyUx9rMqP80+WzxYLxtlaEX4Kt1vRATFk3yeg/6l6g5e6XctqD1gyIPaXfYyXDQAKQtDnGuc6Yf3SNvZjjtRImeEb4gfhBL9KTIvL0fq7cnNrQXuneupMQ5ho0naObLj0LBdJ/dVcIrET+WZvkdD8aTe9TAgMBAAECggEAfziq/L4Au4YppUeQ6sGtS8pbTjVeW7AOyPL5cjioqkVqTQRfRjbwWc3PcGlyS5HmS/ANFAJBEtHoMBiGIGr79ZZEUlSC0Wuha0jcvQeemGuAqZ3Yc6mBufwp3LYZTTj0GEPDdl8YIaffsFdpUeyg8FFlXXVkDiFhR0Ly7JDKpwyBG+p4aeqMmPYIYqx64yy8ddqooW6XBBCXkkMUDWwE4Dd7Xpokx//DKVmOmu85NOkCtqycptaidkdFKVobRw9zDF+7xprs+kSJOOF+nIAHaOStzuc4x8Am4WJqXNNTX2yGty2gEoMQKjrAu5FtDdNqGvrwbfQ/Yt1etHZFjh5yIQKBgQDzIVSw64Ca/6roWTrVBlRd/OTvWzjMb2vfkRpQIq6KpMpxP8qbQYV+nX83RoadpcHAk4kBoimC8L05NGTZjLuHMZ9fAs+/wVXxyhmdHZbCG7yUMF7f/j/IBc5yxeFRYZs2gWuVhdPrFqz8HAnTY0rHC+JTbBLxxIpA2X44RgZAqwKBgQDCaE/7hpAH6+TXtaXdeS7uxJ/efZQFccuB6giHCsM+dylEZKtPfs6sLpjeYQo/IaOLEhpvBwflOc8eOeS8uzYr42vo4FqvZ/nWB/vmm26QV697YQD6HabqBm8iL5NpIGWg9At/7doxfZfiRVcioIdDvgNx22hNr8QTqPBS1jUb+QKBgCX8HB4z/Pi6XvpEDpP/lCjG/QGEUABonALmyaShdoGEs3g0DjRpbTDV7G03YIq6veWXZz1RF4k0kWuhiuwON7Ish4ixiMGdtA69k3jfiZE0Aido0znNoCtg9NsrnUM4q6Y9XBCVQwGknkwZGVPkXGdyrN55sRACs9Lj5/tkvU9XAoGBAKMkUIJ+QN406lzO9fsul+ENJi/a6F3NSf+iuzdAI+qGqx3W8SAMBTne/LAZdTTXcNvi/EXR+6E0awgtgzOSU3pvJf5OUCvEsJcZKh4yr4z32K5MEDrUqV7YuWhRzn25DzALvJ7FpoZDpDLhB6dqWTjS+ycP/a674mqxKcQKOJVZAoGBAKqFFYjIOGNpv46dgXLcwN08GUTzUkOuqs96b5mmxLGE3CGAlSUeeoaKLRlh9QvjpMQF63BBiPcYetbB/NRWL+225lhAyQfxWSJKRNlKos7g/VuF0CNtTGT9FYhZkekBuWbHiIe03SYlzAEIF9FtR6W0AWBK/zv/gsQHwdIoZbbw", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC4olOupPQAcn9YS66bRt+6p671m14N78z8Hv0pM+kF8QmA8G9DR9z+GJxFwZanQkIiKcwpw+o26lPYI8XGBh/ohTWTRgqIV3Ra/SGyIgMEIKjUJcoy0+/rfS6AgRsDuGWYWPTGs3i4OCc7CB22in+I61FX6zzHtxIiD4ADpjLoFxIdVEoOjp2Q3IErn9dyUx9rMqP80+WzxYLxtlaEX4Kt1vRATFk3yeg/6l6g5e6XctqD1gyIPaXfYyXDQAKQtDnGuc6Yf3SNvZjjtRImeEb4gfhBL9KTIvL0fq7cnNrQXuneupMQ5ho0naObLj0LBdJ/dVcIrET+WZvkdD8aTe9TAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmTSPkZzHvNmHtcqwE9nGZjebbmFsrPAvRYPXxrbe64o3v", - "privKey": "CAASqAkwggSkAgEAAoIBAQDTvih1SAj4ioy/YtpD6Ewu0SvPlH2gF5GsUZ0eEJNdBwV7PWfXv+F36MdVxL+f4wFekW/BDp0kBFjDtnjoktrcusd6vpeSXndY4v4y/3DPLzGPpyldZIKEjuBRCm0PdUcl2mLXoH2hEFo49Xlk8h1eyXK3yscPJmRU9ANlHCmgIo/lIDdISosCn3BQwCx60FhVkfEiO6Rt/vzMH8j9QswLoqpAiz+XQ8iDqHNmsDpwliIOELmWgiwV61DVzOXPzvyHl+MhCLvvr8UokL2jxOmldAiIScXGl8dnMIKrhG714UIBcq+4757XF8fuL78nViSaTe1GB4AoSCSspQWvbq4dAgMBAAECggEAOcp5wmDRyfwOpCG3zrb1LAX8/h/aFbq5EJ4J0u3VOpuy/ErrL7B4OkD3Psp/PoU3l3b8WGXDr9Pb4jbIUznZsEruLOsd9V4BFuqFVKfxQyrvTPTjzlCjasiQIq5Ey+ZHb+Zl+dIc17vd1BPzeQC30WoL/GvE3rasxZ7/2jXQiprE7e73HABDtje4t3BKVtdc5+2Za5K0mVJU9AF9PWdp+SRNuvbhrr9wj1DPuzJVmOFkgGHWY8LBtUlfixTvH1cOBb+jXSOf7+yOjPNi7s9MpHVWdejM1Sk/TYKGfkv9/j3esLdwdCI8P7pupzzNtuFCjRMdZ+4Oul13YlfKULEsAQKBgQD4aEw4V88SRbM2WARFJhlWq/WqsHV6UBcSq6N4Uj+EmxUqOIMG6JIOcOjDazBQk5mjAZMMZM0i2jzBrSEE/Mtpeywj8c77xs7Kx415cgzdIKM9+oFcHbDYEBGxkekiFm6u5Gg+dsbh86gSTxcbUP+R0ORGg1bLq+YmzJPLLL6DAQKBgQDaNvjvSE9lAlDXW/H2qEI5PxfBNf2iuWhVNHkmDFb/0Gk4ietx5J7Q8oqTHWdts/QpQ/WsFWVTT9Fastj8qEaMubO0z7inl6IDMBv8KKRKsaQq14d7iT4eDvJzB4JdUGZ/Ch5OHTW2onMnOJnZnLuCn6ek+Io5igT3glXGIdTXHQKBgQDx1ri98d8Lbwg21CH0IE9y7h9SelElL2wHJUsVDR4Bv+ovHK2TwEDSBmLWPjjfeZON+y5qVojQcZ/M/vyymlp+6wfiRry4qqkRCo5Vug+ECQ5kfMoMIGvXLm3Lbr6GDUjcxEoo5gJiYJE0ogNg+M6X68MSUzPhPg3noCwTFhC0AQKBgQCMrxxWyHvHV3LfJXwd1eS8G50pB7H6Eybcp/PjP9lnG+p6dRDCYO6zL2t/5VklNPuZDyN4SmMFD1Sd8OhMHAFAAQmG7NTT18Kv43hnXZxuO5DnvgSu9JCDuIc++fxmRMuP4+od2l8i3CD5jFhEH/QUBvKCPWqAJieFmxXJo04hUQKBgFCkYPBQZhgaoGbRkvjRoh5Nz7pYZ8nfIzCt4B1NJyIJNUGyRBIuamC3Rm3mBF0fLg3bM2fYC4YdfInTeii1u/UXLlYgw1u5/O/xN5/CliLRX5ALDtm+noWFqxImlCGmkV4ZEOxHO+kuaxG0V1mzcMkYvCj2u2YbPKMrHWZ5PN8D", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDTvih1SAj4ioy/YtpD6Ewu0SvPlH2gF5GsUZ0eEJNdBwV7PWfXv+F36MdVxL+f4wFekW/BDp0kBFjDtnjoktrcusd6vpeSXndY4v4y/3DPLzGPpyldZIKEjuBRCm0PdUcl2mLXoH2hEFo49Xlk8h1eyXK3yscPJmRU9ANlHCmgIo/lIDdISosCn3BQwCx60FhVkfEiO6Rt/vzMH8j9QswLoqpAiz+XQ8iDqHNmsDpwliIOELmWgiwV61DVzOXPzvyHl+MhCLvvr8UokL2jxOmldAiIScXGl8dnMIKrhG714UIBcq+4757XF8fuL78nViSaTe1GB4AoSCSspQWvbq4dAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmX7K6pwskrZB8hvqwTgukFTR6o3DHiVui9PokxuJULnvr", - "privKey": "CAASqAkwggSkAgEAAoIBAQDPYkOtFBtPpJqDmAF4bp8dieu7d1/WlaMw2VynFmL9AhcydjDFp72N+YRztF8e0jBz46VkBGQ9PwZHkVgWNv/X7pUOELfEaUpg/KCx+X8uxLv7hjUb8ne2MVP1tO+Axl6cBtSbtQSqTxCTxK2I/4JRmnLNWcZ65nRdD/1CrBkGI2fwFnRYkSexGyFlDrhJtfpiZAB/lPk9GPcxeU6i2MKrx9S/vpj/DwV3QENlF/YSwdUMxbaaFkQHgeYVNHAIUP2HsErj9xDCNbzh51B4uLxIahOWs/exHvHBXab+xu8BPjIsCxaKKQ264jWssa8dDBRJ5O+eRDPwXj4TFdoMO9JXAgMBAAECggEBAI7qfRENxjSAjysY2gqQ0X6dyaKLhbRvsuK7KKrNNrJ9elcANGRCUNNCnRDPwK2Q1GtI+nWOwTWj9UPk2fuVM4Mvm/DxfHMSzHtCHcwI0Kj+Uz3nIzp7QhyAqgeuBU+NZS3JV0Nm4CwuCJKM+7ppuvlZorv5nlqb7p0jo7kKuMQM6znKsw3YzrOJLBvrCxWAo4ISpYuFHW132bpleO5ZY0Ipx7Auiz43C0jnq/v2ly7nrrPlp8xqbRJ37RM/RQyn+f/qTAWAJvR9LrHZQvP3+qln57+QQkAFqhPeYhXpXaJQQ4IPuEfAiLcAm0IWgrULRI2LHboQ6HW146GItKyTBYECgYEA/kCAKZnrryYGaKIYQJpzWqFLv+z6KQVtqXoHizkEsaNv9oHXl8zOauQLm3pKMs6Zgd3Pt633UOm8glfnqcOwft858t8h1hgd+p+xvz+LWx6sVJTcJ3PvyhuF1I2oGQ8Jl0oKlWP+2LYqkFK2Wp4BWTWhvopb9sX3QaXh75nOhbECgYEA0M9F3caJjrFagtEGWB7tDPogqoLPLrQdFLRAhYRu6ds33btclvgmv4g852F7X8qo5Nv/p/uMbcWRVczfzoV9BO1/6YMewT9Nc/OrpLFCVkt8k90XFVJiRTowfBoJb2Aojiw8zRs7XiwjWicdydTHnCBCIVYHd8hxIsrc3KeS8ocCgYBG1A8gB7oJa+1jHqzk6mHyQHbKu6ig3ttC2DTbywGMvvwEzv0RU8O5MVgucu3So41OCU3BXJxGFScnpHdr6pDzdxo8l35klwla9TveDES1GKFnWqTN9NU7F1m78c5/VJoWZFD4dwfatTy8Qd589gFoKbGqU/70iweraRu81LscsQKBgGLg5BrC+zyg61VrGe/8pRAyGenki6t4CxVUzgDr14HSF0BeitfKpr6oCv8egEe6NgQ50XSAf90zY0EYBRtMxwjgVmQDfTrReSHhT3RrpBgtIs76MQYdvv89MNxzj+g3xrycYiZWMOTFTfBQ+aArrGJYPDiA/oRQXJK3MaMjj0hdAoGBANyYagDRItp4Pv5j8b5nXmZq7l+9BxerzPwSzK147Wh24OBd6PRHxi9wJRrl9MeExJlf/CF764OnLodGhLb233v4iRG6LFb5j8XExFFNrVtnNbjdxOMKUFzG8FTd7UNcVO/8o2nk/qsuAAingYXMbkzhEl3soB591zs7GetvYDVc", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDPYkOtFBtPpJqDmAF4bp8dieu7d1/WlaMw2VynFmL9AhcydjDFp72N+YRztF8e0jBz46VkBGQ9PwZHkVgWNv/X7pUOELfEaUpg/KCx+X8uxLv7hjUb8ne2MVP1tO+Axl6cBtSbtQSqTxCTxK2I/4JRmnLNWcZ65nRdD/1CrBkGI2fwFnRYkSexGyFlDrhJtfpiZAB/lPk9GPcxeU6i2MKrx9S/vpj/DwV3QENlF/YSwdUMxbaaFkQHgeYVNHAIUP2HsErj9xDCNbzh51B4uLxIahOWs/exHvHBXab+xu8BPjIsCxaKKQ264jWssa8dDBRJ5O+eRDPwXj4TFdoMO9JXAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmTcEGm5kj2vjQUbXTXo315ATwkUaggyJtHWsRVcUKN7PB", - "privKey": "CAASqQkwggSlAgEAAoIBAQD1Zl0OXDb1tHPDquEh/+xKF19vIbjim4xWDc9sK2h9OATAvGjuTcczW/YJ+swqrPA6zin+J1XrCThWDYBuQj9GNKR8illHZriqlwppVe2+7bLWca2dviLXmGh0lUlwJ6u7qQduPZuP4gZADS8YarIbJveUZGIksFF/php+/60OZk7nonapFlb3Sn3IR3ke9CS526pH9wA91A4UrvgKOtpS8K4NjScTOZHc62sttvg6/ssiWBsvXnwu9hhAbXCwfK1//A0wuLznpHdbg/WijnnEAijSBnapdgcpYIQnrLfxCYF+goYifIDlgAIz6pRvoZbtdUcZ5QgoyPo+w7Po8TSLAgMBAAECggEBAI5CUR/KBXJaseF0Zh63pdstwX1DJ1L2qVwZlW03nNM6bkbs8kdzf08euHsAkOsMZhcw/NcBJqWiKq54FUPV06h3TAOGkEr8GYGLHdYColhUo5/9NpCDcN9a0vMCuBf0Z3Hagxw9SrkWZlkrS2n0MFvdMxkrOFncfOJrAGEvBruZIO9h4ESHQku74zvnkaRTVPtvL5OKFKkWb8v5/5oJ/p5gAOCV5lo1OzFNvjPmHJTJWck2NwFMvQCrRNnh7/E7/1/GmBECK5/HpaZQRc0PnGER5U9/LEbfJU+4khHR3VzVr9d5Pn3lJhBmTmLaWitnWH4Bt4XDkbO52rjoD/TEuQECgYEA/wi/OsalFb/ThCsX0UJh+GukMjY646RMGCGvMVEPLkYZi0An0pL0Vbx6io7C/mcxBdz+8qSbv30UJMLbjC0IiGWVsWSamsQxr/O/z4R5F0ByTqyW5KrMVNJMxmjhKRoKTeWV1pTpY5uIbnd1xX/twxmQO6iFz2Nn7TTTeYHo2xECgYEA9lRGsWkaXbZ6zzD6TiK5I2taDnLqk9GSkwPp3cSWgm6iou7hNqLobRVUeOUAepbnYraa3lEOUyoJlrzBiIdBJUsXObs641MiGU1rjbBm712tdR3ppGPSUEromrCy+3gn0LLmZHe32gfZSkEdOyQWxdR5CJbeqMroT89S3tZe/dsCgYAmb5YKcKeuqHNjRu9W/U8wlmBvpNapOjixpln178Z+7depseiOhtFGHprFSRDAMKMlxBG0VfSXHm2rwKY/8QWJMO4nhwb57jmiz/SHfOqXA4J2svIm0krrOaqSeHn+rMsCxGgZp+WoumcMZvqb4lTeA3tGUnagM9YU3NJGTLrgUQKBgQD1pDEi9davISvytbrGdGX/ZixWQE6gvdrW9I4g8svMohtZM7Iu0+HH9f9Y17TUiuuPSt3BWT9Zu4/4W577UTWrxOgSUB13WA2nAceBcioUBWzWX9AAePLf0vOGXzL9BmNeASkzgxc6O516KNjHg0OaYDmaUSkVVdK409ymD0yHBQKBgQCeBJSjjHUnpCOtXTSuPw7L/f/R4jZebP3LCqnMYnMAWe6w5VIBU0y0Qty7SsnDkWkSHTBjCo3uO+FzHh4Vh94VcOrtZETowPsXK0+Hnrmptus+7zoLwZQd6CYSNpxE0BpfUjv7YoPJMQLuYCKU4DNs4bgSt53SUJ7+qLjGbbbPpA==", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQD1Zl0OXDb1tHPDquEh/+xKF19vIbjim4xWDc9sK2h9OATAvGjuTcczW/YJ+swqrPA6zin+J1XrCThWDYBuQj9GNKR8illHZriqlwppVe2+7bLWca2dviLXmGh0lUlwJ6u7qQduPZuP4gZADS8YarIbJveUZGIksFF/php+/60OZk7nonapFlb3Sn3IR3ke9CS526pH9wA91A4UrvgKOtpS8K4NjScTOZHc62sttvg6/ssiWBsvXnwu9hhAbXCwfK1//A0wuLznpHdbg/WijnnEAijSBnapdgcpYIQnrLfxCYF+goYifIDlgAIz6pRvoZbtdUcZ5QgoyPo+w7Po8TSLAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmWfHjXRK9udKSkHtKhguehBAzW75LeHyPxRLP1ZykgdPG", - "privKey": "CAASqAkwggSkAgEAAoIBAQDaCLdVLSfGmXFz2qwciYslW7GQv8oMy3pkBotMPzdXIhSYXB4p8/HjRyAeX0DeCF9vC1SdaHXc9p70katM/K9AcwMod74QIUVI9dCS0oQKKW4oI+/Az5nYXEC/C1Lu70JnOVWxt4R9rTU087UJkPDHs3DsVN5bQ4R6wvQe9crotxRvRiXvOd3TnR+u/B6bzQgRBqglj8GUYSmLe+iE6e+WXnJhRcA4unyixljSXD3rmaZf3GI4GSGhLnauJbwWpmmyTfH56J23rnJLQnQJuygaMtreDQrfk2u9C8rSxY7pFNQCDahkMolicUsfgegN0hZfVTokHypbB7Teb49e1iaDAgMBAAECggEBAIQfuAkzneDpZyjPoHCCoQF4eTfAIQ5z16z5kjwYKs7wZg6V8+l0XGZf2YTOMB7ccAh4k0P340SNZnHKPEYg8Ypap9VECrb3kmbOHyB51W3bAVftvwHWS+IitVGP6SfFcTXgNp/FF9KYvZ9i95febyp1AL8WBtDDL2q87PY9+EHgYQTqv5yT1OGXhGN7dRMxyDGfe8OEZVjpsQ+hhA4/c3rPh+uZ3ugZZ4CnaistyNMywPZGYVoPqQg8/HRspSL97GctmuCKph7qQyKGVvFg0ExJgEQXVNSjVw0z7LXmDipYsz+3qR7dLGAQvpfXTc2szAhbOE0cqKUUlz0qt9M0MKECgYEA+flgc2Y93ls8/MtZbvzwbbi/VX3pfXKikqdizTAshwJzcbHMEohzHRnKb6hNk/P/V37c7NJXZWa/k3LPmz0Xk7wtSXSHpmk/+GEMpwjjQ7urRSg8HMHjN96U5amqmA6pZAYsv0tIouCoR2+XPi0YY2meIRpwtbeR4npxBbhnGQkCgYEA30o7oh1jy93VnCWHRg4g7CmV0H0ufgJfAcjQg07Wo+fquiu7QfmBHqyWGJCkAcT9pC4Ia8o7Xa17m5Syq9RyIPlTJMmr9KCrFQCyj9Tr1p59MK1FbkpLTCrRimaYmR/dQJBTfaaISSZipXEKyw5VhAz28tLBLTevwdn72YfT4isCgYBFvVk3WNLx8ip1rJXq7Q52zhAzXcmCgjTxDVn3PPVvRTPICH6SvRbAi616sU3TdUNLuc0RFS3k0GGqVWGuQcEOKnXIBIbD2qFKPmk1QLmG8Bi8VplOvJkTwTlxSYCao5yGl2JsjChbqKnKJEvhwNsJATJoseO4DtrYgKh/nA7HYQKBgQCd5diVo0LW/1/2s3MdTxBo8F9It70QzoxwrpkEwdN2xKFwVUxuMwnjrxfU9zODLNJQL101HCUu8Wbfdh+C8xBh0O3CrfozWwqgJ4Ydv+umMR1GNsFKZK8qhXz36eUvIyFKbsUbrY/iaoqHg5CmVtSSNLjMrcx9NUvMQWGfSjXDUQKBgHQUaT18/o+WEETXKLdyA6L4BUWSNzqTtt8qvDLffV+9/6YwO9fAjN/6NyRp4iBFtje5uuw5e7SfraWY34GA9tHv6ClGDYPQB2p8MwNq+8TyGkAxiPb6kqYIMGxxeXnwwac1ITK2FlAmFrazn2WmXUChB0MIiapyLGOjHLB+sH9l", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaCLdVLSfGmXFz2qwciYslW7GQv8oMy3pkBotMPzdXIhSYXB4p8/HjRyAeX0DeCF9vC1SdaHXc9p70katM/K9AcwMod74QIUVI9dCS0oQKKW4oI+/Az5nYXEC/C1Lu70JnOVWxt4R9rTU087UJkPDHs3DsVN5bQ4R6wvQe9crotxRvRiXvOd3TnR+u/B6bzQgRBqglj8GUYSmLe+iE6e+WXnJhRcA4unyixljSXD3rmaZf3GI4GSGhLnauJbwWpmmyTfH56J23rnJLQnQJuygaMtreDQrfk2u9C8rSxY7pFNQCDahkMolicUsfgegN0hZfVTokHypbB7Teb49e1iaDAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmYnNic1ZXYYFnE1GLFE6s5PJat7SkDRuYigxycHdvoeSb", - "privKey": "CAASpwkwggSjAgEAAoIBAQDqtF5hmd8vOpqbbwV21tg14xX++zqKXLH6GEsERFfmdzIvwEqXuaYwubrGbTONETICm5KDzaY/lQClV+R8Um6AmR1dL9j9lOyzeZE4f66l+LVu6n0jxE0T6Ik6nsHtIzOqO1aKpLNO+219KBswnBWc9lv7HD2e/OKlpOBDqX/YxpM1sz8J8A8TzNCrkufP6OSCdZ/s5XwPEh1o4tYXdJ9BSAfdKzUBgRtbs16Q7vWrVfxD01pmvvVnkIHVR+eL3d+3n19ZmMlGAm8HpP52fYTa7ABixDxdBelQN92/cdaIwePvAdGpqLv8fCEVCLwrQc4BQi2BePp5n5WYHTwEFW4hAgMBAAECggEAEeyWbKPArK2wEwDGjQ3ZUzw1eNSc4uYzXWMvj3Lct6gQuB7aU34FGCGHBxJd5n8Sr6pL5S72bFKnyvjMZUYyVDXdTTmTO8J81TQKiCMQJnK5AHB+ABZEwKl4mXZ4XvDaSDzh3hK38uc2tGE0umChMeyKl8HPXu33LSlLSz+NmPNj5wAallohy0JLSvM9hImvxE7kFTtSZCQUctz56WTnQdS7VAejmSPAqUheLlgq+eDbqzqJbMJOj+iXrT3DUxx54CIMlyd3vZg/LGI6PQxLNdrRBG5Tn+z0cn3T+407pClLdr6+ShJGBgzw1Su9NelWIJaXlsGquVXipj2fzAH58QKBgQD4yXq/k89BENdGI94h4ewICRXdc2ElgGK2xURUDKncb9HC7foNv/XR9ma87gwYq+uM/pgF6DCNi2VzVy9UHsollAOjdsn5y7C2y9HobWgArtv4YUe7oOdzI1UrP2htvFNNxjdDcYhGmeXlDOZcxW5AzQGJu/dclwIbH1vY0g3k7QKBgQDxgl4qHzB//VQJNpuimN2X/G9XDknKvm8guLjPXDr/2jWBthyPM6ow16qroAEGUtkXwBSt4y4GW/sz1vhf0sVrPIWMqkkPxMrnNQ1qQ3gECWHgQCBHVTqgJU0qOF2+c+WG3SGpB91YrIzHJqQRDjtHixEC1uJDdDRrYFfqqAqbhQKBgAzbhM9/2Rc4wpdqZSGFJoinx4yBWQTyJKfjfAuH+ANfeAzF9cVeJVsri9W5y8A+qlbIFZ1AibnW+XBDkjubt8DHbIS3L+sL/t8Dm56SgOyAHPgyNt3Yi/2kVtN8XG5HbFq5osOGi49yhrIWv5UN0wvgTHMM1tTfLQmvzjRfbr5lAoGAVvyC2B8Vw/PFse/WTNFMdzK4E54U3A6NTjbace2hXogE36xtSvLr6N21Hk3qMJHkmZZYnG0IJcg5iWlzWmg7LS3GWGz5FdHm1zIXm9+jOaj7dN8EAU1kaUwmJ//XXAK4eEPrnMs1YXv81LpJO89pcJJZVTF6m5seSlKQN/fAolUCgYEAw3j6b3Lgi1Z8jioss9xDmMPvf913W97N9lt877SXWSJwvcDik6cuRS6FCs2NBgUUcbQ2Sct/WqVbDNBCDU0PjpOFmlHjWUl9CZ0M4A5uiPyzoJbICE6Rz7AxAiQbGxRCjMe8l10rCkb5twQcxwnWFmWuCfp4C0IrXmD21Xlvwug=", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDqtF5hmd8vOpqbbwV21tg14xX++zqKXLH6GEsERFfmdzIvwEqXuaYwubrGbTONETICm5KDzaY/lQClV+R8Um6AmR1dL9j9lOyzeZE4f66l+LVu6n0jxE0T6Ik6nsHtIzOqO1aKpLNO+219KBswnBWc9lv7HD2e/OKlpOBDqX/YxpM1sz8J8A8TzNCrkufP6OSCdZ/s5XwPEh1o4tYXdJ9BSAfdKzUBgRtbs16Q7vWrVfxD01pmvvVnkIHVR+eL3d+3n19ZmMlGAm8HpP52fYTa7ABixDxdBelQN92/cdaIwePvAdGpqLv8fCEVCLwrQc4BQi2BePp5n5WYHTwEFW4hAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmfQa5Q8TzoTugCs1GTfwB5ckb5MwEuqHdQu8LdBSmcqXk", - "privKey": "CAASqQkwggSlAgEAAoIBAQDx0kP+8QIqEa1V8vmxNxn/bx2r+I42wn8MZytziUKLzXPJIfIRejdcEO/U5jqDQNIr1ltIQkO/P+K8U4/ThlLfJ/pqkOYHNFtU1kGPVnMMDHkr2RvOG9MPUC61aCyhCYAUpUsHwSraBVxfQkDUTUOCMqwn0FUqpErM2v+naPGBYyn2owsN1SMLE/JxyZyB1VhoqyMpbfUZY0nYL+OqF7jveVM+30cfdfGHSkojgJCyb9Y8EXRCPRLCX2uQ8g5zgln8CyELd4p+wgSJ35YS2pUGlZ9Ev9UK+YZNCF97NIhtepFNLHmrYemVKrDokDP7fl2XEwmtjFdNq4SiorQdTHJBAgMBAAECggEAf/a8dJQkiQ6BoxHIf7ag00KBeRc2alPR10Zg/+qKhGBb/PsxlX4O/XEY+Jg8LmiGzxvHgh1OrE2qNe4iFdTm1Z/aK7oxf259Rg968dbVWnLfTAy/YfnnXhsYHHbb5vuYA1TUt23It0ZO8zmkBLQ+HQ+jeg4Mg1wdGPpqfrRR2B0SKbR/aG6c9EDhYXIDNnkX1Wfd4y+GqlHN0YrURbFVIzJUu93yrhP7DaM9CYBlmdjul/5NdfX+zU9XjqHJVw7/zxkJjWwfm8UgNVgjsFJlHhfcTKsacRUAiYuwljo84OOZUhshWV321LIby4BKMNFPEr6uqh14ZzTFPiczx8yoAQKBgQD6xYh4fpelVf21JMnjtWuiEkFLbTSKqFMPp6djCIfiBuJOFF3paJCHtnCGsK++/aEQ3mlxlJ6kYaPRcQUAle1RJtd8C+xhRqB3mjjwO0Cl1vTJgYMNMW1E0mTzhMVT1gRdt0+3Uii7k4Z/MWRacm/zFuE5GF9mxG51jabjULX/0QKBgQD23PYgucYaTEgZQrLMhN3TSQKmlGG8dE7S4QqdchmXkaqCMnU2LW2GYthirsZQa4hByUamG9j6T9tldpuPP0Fc8KfbwLohiwIatQHwIQUT2uhkG+HX/+/tZYZZ79vIf9Yl9fhAwBnSChRGhS5n90jhZ/CbyEk1PZxmDBe75r7XcQKBgQDzwQRZU3vmC0L0S9EuVM9Nl37+aSU0Tk+GnQlYexdR/i0FhkiOs8QhFpYkZiQ+etyPwBEwhSz7Tall0Pzyx8kJI787ZX+cQoGCIFeOM5owWVRRdmFDdrLmvbfA+WKxjgtqaN/EqsjLI6gNhJ4uSKRG3wuHawh4pSFVhJ4ewPpXsQKBgQDx0cVwjUqXnD3MMOABI+4/+HcWQqfy+WP1gujpDkovhUunulHDPoDZcZ5SHK67PHr/JnGEaicEHJHoNGVxzx7yMfPcelBaZ1cqXkGFvnLA3mFjH0T+WAHpZNhU5XdAUqmuCeKjWwpwC9uMsQ2iXkQQOccicvHzq2S3OgVN1V0AoQKBgQDBGXOTN/at01B5ooKS3/O/3zBoERfgnmAsPhnkYCl+yn6eSY6tU31do+oUXxXLVTt79goSSC9sQDzA9TSBBo1NTGzFElP1zSBEwhgU+l+KTdjKH0qHI9/8ks1G4SoSNFxAj+MrRVsy9glqdmJIgKmPqdwlnq/7hB9l/FUC65LsfQ==", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDx0kP+8QIqEa1V8vmxNxn/bx2r+I42wn8MZytziUKLzXPJIfIRejdcEO/U5jqDQNIr1ltIQkO/P+K8U4/ThlLfJ/pqkOYHNFtU1kGPVnMMDHkr2RvOG9MPUC61aCyhCYAUpUsHwSraBVxfQkDUTUOCMqwn0FUqpErM2v+naPGBYyn2owsN1SMLE/JxyZyB1VhoqyMpbfUZY0nYL+OqF7jveVM+30cfdfGHSkojgJCyb9Y8EXRCPRLCX2uQ8g5zgln8CyELd4p+wgSJ35YS2pUGlZ9Ev9UK+YZNCF97NIhtepFNLHmrYemVKrDokDP7fl2XEwmtjFdNq4SiorQdTHJBAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmNcR5oXnGP4VSX5AjaRcPepFMz27ZGupEJXyq2BMpRp5g", - "privKey": "CAASpwkwggSjAgEAAoIBAQDQ+zxtRDJm934HN6L/8q8EFnXghF2cvpECCpBc+/+yR4l81QkKblB3/LWDeAcx4YSDipJZ8D7Wb18R4R3BfcJ2AoMnvoavX64tnTfFOYiUMri4txnDDCVfYzo2CC5QGTqwzwAdC4olqHpb7oVTIAzuQHOQSdH7nEsmy19SmXo2kQlZdU4b5G1x42qyFIRY2F5i+d9fx9WtwuYmCOL/QaPSZOF8j1uxuPzeHzc8zyVHXqJfvbhh7MOowCuawecp2N7OvYUsNJdtej5U0xgEdyICTivIPNOIvRKoozvMcmA0RZto6X+RFQX4c1Avc7cvA0CwKW2fLvsW+SB4FP5cur9dAgMBAAECggEABtl1LY+ip0VNWCc2rHTjz5p82pL8DnsgfZSjDqkjxFAb7X9+AF2FPamGuXxhn/zoPvd3vILnTFfyIb/jHchla0DB07em6nCUYOJaRZiRJWpUK5m0unPXdbzm14aFHhL1nX3rXwhVys4u1HyI2iSex+BM6VnCDCEfRXI8+ZQWMVuYaEyCTuhvl01jJ2LuPsDdQGffMXuiubwZSmvjwOlauUvXVCy6l5riwj/UyoEyIDp0bWY3LbaewblqHGvVGKBBX3pdNE4fadFtgeJBETMCr17EwKRWrNEo28w9Tm8A11s5GntOcGnpk6h/OIdBDPqXqBoQ3CnkJOsI+TmL5nTa8QKBgQDzbFZr1YKAEvKM41xJ6lqAKh7jXw+1SK2LgvatFMDnvsirsRJs/s2D/PQaTnQu+fG82LQbIj8a0wGLd/UKTle6jldssA0YU6KooW08/+1RaaaViSXik91pYel4gxyaN55ie3zlJ9lDiRTximQ+RTHr+w1qk38jcn93YOlYcZTNCwKBgQDbx1mvz9QK9Xf2gT3+KfMkJiEcQit38s9kWQnY3BGzqAq8Oo631gVpAy8KiYey6jKohj7wNPnhDBISVJ/eta6rMiDjujVa4IkBhakyzMCm1nSQHordv/oocnQHna2TplvYbSMUAjgvatS7AbnzZzz+k4iXCmfSEE/1z9aoTUNWNwKBgCcqymkFbL8QzWgv+RyHkdJHdLrfA9cGf64P/4Lv8O4Y+47sqetRwF25aMmG0Bjy7JuXPruS8hZt1zTKs2naGzGQT67UUPcWFfkOKFaFU3kjB8PN0oO3iQu4zmkup36E7n4oInt4wvOj7fPDcce3OIYg2hLI8s8QUEQ0Gre5ZtjrAoGAZhqsWRiVq221COmsUltM4VtxgH5hUX2VyknvYDeFZdDJA/+0dEXTB6F6Bkw0pfNWC6MqtE/4UwxXjPqRt1byyggk7YeB6DFulS1ymO41Bo2VY6s82p6o6oeZzjv7+x+LhfXWGSKa1bStFiBMMn+g/6itCXbFGvuHGm0vjcsvYGsCgYEAvDl+6IL0r1HNoyXZZgJGM8GVW1PpIJlHVE5X1X/LDtfiNNAhxXUt05+8uuryeAWpA5hr/Bt7/ds2k9wJFUHAU7VRJkhZ3J64vtl7MJro+cSNEZ8H5O1Y+53SBuAQk9Z5wOwZjjHf30l+wYoTwoKJ15DIPOIKGYyhE0Bx4HOBBdU=", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDQ+zxtRDJm934HN6L/8q8EFnXghF2cvpECCpBc+/+yR4l81QkKblB3/LWDeAcx4YSDipJZ8D7Wb18R4R3BfcJ2AoMnvoavX64tnTfFOYiUMri4txnDDCVfYzo2CC5QGTqwzwAdC4olqHpb7oVTIAzuQHOQSdH7nEsmy19SmXo2kQlZdU4b5G1x42qyFIRY2F5i+d9fx9WtwuYmCOL/QaPSZOF8j1uxuPzeHzc8zyVHXqJfvbhh7MOowCuawecp2N7OvYUsNJdtej5U0xgEdyICTivIPNOIvRKoozvMcmA0RZto6X+RFQX4c1Avc7cvA0CwKW2fLvsW+SB4FP5cur9dAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmPpTw7SQru7aWTmsZMhBPmprCWqnqXdqFbra14bnCYWYg", - "privKey": "CAASpgkwggSiAgEAAoIBAQDSzlp3EXYtalGQfbDDu8eE0y4Wn+YyVB8uOk7CVmDonlwp3brJ0/fNaP3mDG/Y24YdvmgpCuzNaxvEIYz19Ji5MKUE9wY9D3gUoJN8jRIAfuKIUnxOLxd+Rva3gp7Yn/5Y9aOurFjlWWNs1jimmqfDTnA5wmEKvIDuoWjMjq4fg150SIUjtG/SKg25lUCL2n6WYK21HIsObVWLbh88Hy3lQCbBU/4CcYakF6TEzrcgSPZ9hz1oirrznu5jpO2NoqT3vSN5NtiUrGcEdyk4L0efwqYKDIEjGjXS9SWiJYc+ri2vxsOy6rmoFafugGcX8cnnPLmqGhzqXngDLkQG1DqBAgMBAAECggEAPAp+Ba+5gxHnDUpfUEBpgVFMrTD5tZf0EYyV5hAIJfkEsv/uNZHj4GNo/V7JdHCB8HLM4/OyoodBL0mHBn6WCRjrx1A8PKPtRaK+nxjm6bE6AC3OLc6H2HWJy5aue3CGVvwPlK6N2zTsdpFFLV6bLatnl2vfi9lIt67NVIXG3j5dxo2OlPqXKLkLnMEwjo5hGj86Z+Q0O3Z0fxe3CApr373sVxkHDgChagVoraguFubOPsEOHBnjO5THZmNUhlEFDRydX5aCTYerzo8vs1G7vd6I9MjKkPAwRlGJ+XtmwmXbaX9pFjwiHjkiVFUAXxw5dmhuzYcfN0kZSsvWDv94AQKBgQD4UbSG18nyOFS6/njfebrtwJSkhQIyk54O/1KAguQUqOx4CjYhuhGeQlu5yTLQoWUO8E/7K5zdjOsCYCHcmI1W8TRolyOo0o78ATvSrcwy1gkaYgk2duac9WiBV6FVDtxlcsX1pou4vt4S8LXDHVb2ToMcretZpdx9WJAQkP1GYQKBgQDZU5qWmwG4uJQ6bloa6NQ4sgY7gXpf8fRHUk+Y6W6cyRkeiBVo+3BmoY0kE5kbJUQAfbpOJ2ynQzV1T19CFqnKJHbd4WQM5Q9+Lsa7QUSCmAvLoaAPe8AoLYlYoFodpRKEvjZk6myMhcSP85/XPkPsAA25GdXFZspUlPzuU48oIQKBgFzJ4yRT9BE/vWGWf0I6cYAv6xtC3Fxbzr8Z5xFAV8vkh2AfqLSXm8fAUhgtN4DAHkwjvi9Dz7z10Ec19tFAa+gl/4hpmZiW/XjrWRhTey8vzXz/TyP78BaMmT1jqlRnVjHOXmx5jFI/eCopqjG7f+hP1CxeTMhV8vsfoc2e8BVhAoGAWkAt4n1cqal9ZQaOxL4L47+Kdwu+FjoUh8nW5FmMZe/dTqCUw5QniXdtdZ3t5ygCpXGQ/QPCS3PNr3nWxUtEF34tHteLBQ/a7zvdq8Xe/ZzGyTnFjqiFlCnU78kno0f5+MZFMINpsLGcf2tc5bYl3svm5wejjuaw/48fuplYygECgYBbaRw9VV4XUBu+/nlcjSNdrj2N6gHxilYwUX4hTAT4ZDVxxJXZZHlxp20jHd3L135M6VAEHjA5b1+tUbe90GxOdM6xi9iKn36VHtjd7K/p0NWzGd86Ny8sqOZN0Q3surcqXvT2nFyuO6q7jDtAN2b0BSo9K9PnSiN50lEVcmBX0A==", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDSzlp3EXYtalGQfbDDu8eE0y4Wn+YyVB8uOk7CVmDonlwp3brJ0/fNaP3mDG/Y24YdvmgpCuzNaxvEIYz19Ji5MKUE9wY9D3gUoJN8jRIAfuKIUnxOLxd+Rva3gp7Yn/5Y9aOurFjlWWNs1jimmqfDTnA5wmEKvIDuoWjMjq4fg150SIUjtG/SKg25lUCL2n6WYK21HIsObVWLbh88Hy3lQCbBU/4CcYakF6TEzrcgSPZ9hz1oirrznu5jpO2NoqT3vSN5NtiUrGcEdyk4L0efwqYKDIEjGjXS9SWiJYc+ri2vxsOy6rmoFafugGcX8cnnPLmqGhzqXngDLkQG1DqBAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmSPUjpXC8ogoXp3AouY35AiLYTJWBbXR8uCmQY6QqS1rV", - "privKey": "CAASqAkwggSkAgEAAoIBAQDLlhiibulDWV/h9BweAmYkNDnyeU9Q+cn6xI5xCrrmEraPCvaQWcNBPbIoldERlE93d/ynP59y8Kixqhh9bXFHzP/EwXavvbO0OMxABtJZOx+lhWGl82HIjrvz44TFNEmXZBUhv/3BXEm0yLOPznW6ccdVNicOZLQoi46mHpH7BYe3zjQuX2cEp4wRG5pKB5X3QKKBmWn1SlskzthP5v3So7aQh2MfhGtetU9sJ8vNliFHST77QIKCyF4f6secNuyojekiHu7CUX55q7o1IVdZnrrEXZX2aMormAfqPhKQFPJYyO6aeWyHwMtxiiYnUQbokFpA1TQ1C6tOvc6ZXNsjAgMBAAECggEAcilS2xMyvs+JUt1ePv29ZSPcMroP3iqUNoiuD3mi4I0xzfip1rxfH2CHXPbV6/OstCOWi/rDYOLO1gG6BeuvEEJGUoDiGx5XfQI0ltq8bckXr+uhnDtkY+CWSOcWdrchZUF8EBbnJtyngDbjagquPcS7sG7Ta+DQncPUVBbkaUvqSpp4M5rqwECtEA9kUZQIMp/pJB9ZQ/2Ob2cyoI5SmUvk0by0XP8Hb7GBtrf6S4+BbUaOkj9jDfAH6Y0pN1ubJ3tkd38FAFtorWsJAdkvrQliFUjfjk+ANmWR6iIEGJMXsabUmrmXDIhzpv+XMMNpyI0L37l0DlkJ9hUEwql0wQKBgQDusySod6rD02pmSX4gTzf2RNaAOghTwiW6W4YyXOokRQPyrbvXUqhnlYJGBpB8Iy74h6+nJtCBPOTOhFSDjL0isTcG5KV4MWzoRT2l0PhOA03KccTBAgxHQSc2ACJIJr1yiuj62/ToEtmc7e4Eb5HeYzgUqig5Z9LHjxXu+UZeswKBgQDaV3RnUqb+3tlir2mB8Srr7ZjLMMjYw657YtvFCHpljuqUPbt+6k1K6uTsnJ/fTrfRg9Fn168YA8Kc5tqcW5ECjN4Q5/MdGWVBVuLByE1OAZ3Hm9g6++tIWLMpb+z2tgduKUA/hfdm64X6HqoBsxporYhj0Mpn0hrO8L7FlrXJ0QKBgQCDWHQNd3uxsb3UdxA9+xlSG+LkQAqg/C4Cc6ZORC5astdPTCYWf9dG2FAM9EPA6yNHgnI3SfZlhvpoYQyYLnNMibM7ycj7cEb7ME6R1YEsfEjr4tpfUh8rfkBzSHOUvCx2wNUeZLZIlUbFQW89ZZ8gffw38sGbhPPI94UcMHJ2XQKBgE06s9y8Gn96ObAzVYF12XW8A9iTN+ecR4IzNIMb/Zcglw66SzCYFaDTNwgOWmo1QMWl95LgcnlvEw5GhbralI8vXnjiYla/ndYfsnNSsy1NWw64rCIo608auLyGb23Qcw5fHu+ZJipMUoZnBEE3pbay8tRDjORuJ7dc5k2jgkeRAoGBAID3nD/tbyg617LB/tO1rnKlfkUGmLocoyzzX+reQHVRaabjAS5Ip5a/7g5gtLyFERlImbEzmCbZm03PomZgHLHMPE6Jlo1uTsggf8Ax/ldR3Q3ZficWwQ05gZGflezsUVayewoqDbrW+qxXI56v8l/dnoVs/5FjlY3aFZOFO2pV", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDLlhiibulDWV/h9BweAmYkNDnyeU9Q+cn6xI5xCrrmEraPCvaQWcNBPbIoldERlE93d/ynP59y8Kixqhh9bXFHzP/EwXavvbO0OMxABtJZOx+lhWGl82HIjrvz44TFNEmXZBUhv/3BXEm0yLOPznW6ccdVNicOZLQoi46mHpH7BYe3zjQuX2cEp4wRG5pKB5X3QKKBmWn1SlskzthP5v3So7aQh2MfhGtetU9sJ8vNliFHST77QIKCyF4f6secNuyojekiHu7CUX55q7o1IVdZnrrEXZX2aMormAfqPhKQFPJYyO6aeWyHwMtxiiYnUQbokFpA1TQ1C6tOvc6ZXNsjAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmeQD4Y7ahTYxRrDERmgTD4QTUxEgXoMKcoM5TJ9LXDKfB", - "privKey": "CAASqQkwggSlAgEAAoIBAQCrq+NH4sQ+lgXdZgQ8G/xD01fkuFuuHpaRX3a1QRsVP30KxhTNHNAuRq1+2RFSIfT42jGvEV29xFKAAZpY70P/6uPRurxn/uqCAroPJYV3FrCYS/b2N3lJOBrG6e/TzM2Gp0Aj5TbJwzA2k2vWHxjnyacVyzxSQtO5wPfiv+seP6RVSnBYQnJkeyjxDyhUG9D1n4q8TX8Bdjfgnubh73rg+jGwfmY7R6YQVCRNkwGnjesJHNvE0fgKt9HQYnDbgwQ4b1GeKPgYPpc+gOLOI5ctY/ScgXcHrI4N86o4uJsMBWo/kmTD1T5PJjGtdHRByzLrUElsJnKb+0rXiDS2WroXAgMBAAECggEBAKI4bjAauAjQKUCaSzwl0c6h4nduQqwZmXxLslf66sW8VcOdhECCjrJ79SxdoIF1NxEE1lgxV9yfrLnrSdfqWN53Lsqb47d96knqm7j+Ys0y8rMnbXoi14h57Mu0ef0xlbE9UF3bFle4C1I3InqWrikxo6Lzhs/Q+FOaZmOtqVbNjhdP88oIZANK/ceCYdba4mHENqxl8yupcuYplKdZJc7udOd97zGwebqlhsF9AJ0/OINUo5yeH4lBhjZmLyzvgUnNbWRqQbIrFHUJERb8LiYadNDuGURgKdSb9ARe9+/9L+mYTI1tNs7F0hACMnJxLRb/q3rxW2zPR3C2v2LB6+kCgYEA3NMofdiNfkeCav4OnpHpTV9G+uj3xEKtTvqdNVxrJEXbeRMYzZqVY9TP2v3UdSCDD1zjXays/+INljFFForSQFech9UIuuIDR4IVpmHiPwzmmYHf2A2tVZvjOFIe9sJMXS7nL193ojDuNvy6XCN5V4Vi0MYnM2XdaP+gKIa252sCgYEAxwRXiGjyEjRD8XMiwpe+o7IY+5DDG0he2f5jxI7ObEqVupQeAknQbvjDfXAv55bWlV8kQOWgcs5d4mpJoZ43adO1R9VFu15VkyhCJ3XYL1AlNj/543Rd9y6/Kxm1yYu42EHUrpluiWE2Rt46+INd+Ztj/yzYZoCM8F2GiBoW3wUCgYEAvZKxYkg0QEKXnc55MnxE811mDCVP/zbWncTcjWDHwh4OqkRQuMGKmmeqAXCDogHFQb0Wm+aPpiSkUVn+27lVglM0WA/1LKq28f6lI29I0aP7m7E5P7uOIL5xNHqbhm+LKzwG0E5+38ht2NriChOSKiaijGRwZtl+WJOLJP9xqf0CgYAGoQ9lXNGLZ7BHr6UdxD42Z61LW+QT2ZJHQqECIBuiIc3g/CQPwXOu7pxcZktCNJULPrMPclao3FTmQNIZDxMbdFDahrEe76J8F2A0vkkoMkw7BWCGgg7LOARoJCAZCY1rrq2t7zBuZQ2QyMBAHOgZc2KeUlkW+Ps42nSrveq7HQKBgQCwrPtKocpxsVTd+VuFtki3AtUvM2NfQBhtc5FT8WKVvO5PKBIPyG6hBmfGeOrxiK8lISO6MVDaU4FRZ7hc7bQUD7IpzGvNpZo8Vnsav11bgi3VO7ElKnHJN+MrqFYywasCDOnIVBGBBr0m+qnhr7pEgJeHuVnlcmynkZZJob5zWw==", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrq+NH4sQ+lgXdZgQ8G/xD01fkuFuuHpaRX3a1QRsVP30KxhTNHNAuRq1+2RFSIfT42jGvEV29xFKAAZpY70P/6uPRurxn/uqCAroPJYV3FrCYS/b2N3lJOBrG6e/TzM2Gp0Aj5TbJwzA2k2vWHxjnyacVyzxSQtO5wPfiv+seP6RVSnBYQnJkeyjxDyhUG9D1n4q8TX8Bdjfgnubh73rg+jGwfmY7R6YQVCRNkwGnjesJHNvE0fgKt9HQYnDbgwQ4b1GeKPgYPpc+gOLOI5ctY/ScgXcHrI4N86o4uJsMBWo/kmTD1T5PJjGtdHRByzLrUElsJnKb+0rXiDS2WroXAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmRu9rXt1KoHES8dwvoN4Ca5PkHVEHsyFY3F7CwY1ZUTPA", - "privKey": "CAASqAkwggSkAgEAAoIBAQDfElzt5+t4UjWuHtDpwPvjIwQfrgn4+OvnKhBeK4yJuLlC3Crl5kS4cMkiwnbpsIGAF00BO0GQi7j0EY9g6bEsSnmKvpAovoekcVACQZzbsbe5zp1KYqbSawkzPrQO0OUR6UaIPEx4LZ6fpP8AJZLfOsJ1s+ETJ9qOQZa+IkrHgpHU629WdQoZt9tJ5jKLprWgp24CvBnErLnUPDXhkT60TrF5mSMukJJ/hsnbgzjs83J1AfhRp3wCE3X3606cUnT/0IpI+flLA7KvxBN8eVIA4zWFFr4hGZxtzNnxHImcwJ1l4Fj2vu20Ks8vozjBYOauVoUD02gVKBbsYdw82Ec7AgMBAAECggEAKBbiwJbHiK4tm4dKQFhAbIekfBUJEceajcbPfj0RWsbp9Iwg4YRKoWMTor2UJVdlTqHhYvFFTTbvHF3ziJU3FCCmSzsIKWpkcjczC9TC3fDIdgod1np4RKSb2KvSLD96i4eC94TusUJxmXtLoLkf9iJXRFP5hTnKW3qKHs2G5ufIQCp3QEgJaCJ+uGd2npZ+5x1pMZAF7HsShONyiDWT+63sF0TXrh4OJh1e5+SLCjlJY4luXDWYkelihWbWuWEe8lHAmBorBdRAPJG0ssl/rzSeEdQQHNY/WBYgXq1OhrrwffEKXBStrH1lIteYdAtPoTV6ilHB+S3Sj1LgWDaRgQKBgQD+q4X2PwGJ2PECog657PzWM1w8QQgbwnydl2N+iLrg+lOBdDE6QpJtOCmuTUAjlTbBONUhunsBSugHZtSUkSeJ2NBEuHf4qvqB9o78PGT+fILHMei8uJi7uJY6hNWbzfKR//RRdxZ/ZIJGK0eEZ5HO3K2s9tnS9jRg4OwWbo64mwKBgQDgPJhNmr+rMtMhRqMF5iYfpFIvvF+KGq8RJ0kk/PACf0wtxzjlryOnuy45Tkjuga3rr3xOJKwi6j+5MGxntH12cfkfZnR1Y1KnmTbNVs91R1ZGR08s0Omjfl1YauSdeQxmDyoOQXjV65vOb1L7bvwBa5DafUwVeFUikQ7tg7cF4QKBgQDFy8iHIhZ6zwEZj26qn1McttVbgxLeJKcO6yb+fwnOdP5onCsj2dLKe4V7+EnpmRnm5tI6mRCyR1CBdy+CmF7CJKBVz4R2oa1hRXN2mx3BvkkAl1XxRdpyaoJbvxH9Ke7N0KMcpsbVeOXpw/GO97X6mdFWdn9l54109RzIq2O0IwKBgCRvlCvf/k76JjZc/PZjbERt9fDNwhR1u4alBIyfEPzG5ID3wzYHHFsP3jXvk4g1yCXo0OD9sn7F427bAHJlcJGDeYBxrHC6n96d1brN5U3gNpOa2LGmjKBFUzOfwuAXoD0hL6s7VkAkVZ/YlPpIEWjFqrbl7yv57pN8UJmlcmLhAoGBAJXAhN56TY7mc/w6fKm8tD46zao2iynMREOUO3Wpa9wWtRWyAQwg44SW9mpuBuFVtuZ7KgumKX8BWcHsz5lu78KD+zZ7ko8UhtEDXbk22KVxsWYEgUNmaTkTA3ziDWq8Y2QJ0gW1HJ3G1PTu/0FH3b43LxxBnmSaR/wQmnKEvvoo", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDfElzt5+t4UjWuHtDpwPvjIwQfrgn4+OvnKhBeK4yJuLlC3Crl5kS4cMkiwnbpsIGAF00BO0GQi7j0EY9g6bEsSnmKvpAovoekcVACQZzbsbe5zp1KYqbSawkzPrQO0OUR6UaIPEx4LZ6fpP8AJZLfOsJ1s+ETJ9qOQZa+IkrHgpHU629WdQoZt9tJ5jKLprWgp24CvBnErLnUPDXhkT60TrF5mSMukJJ/hsnbgzjs83J1AfhRp3wCE3X3606cUnT/0IpI+flLA7KvxBN8eVIA4zWFFr4hGZxtzNnxHImcwJ1l4Fj2vu20Ks8vozjBYOauVoUD02gVKBbsYdw82Ec7AgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmYqz4o7nrf6tNUo7ymPzLUPM5SpF7QvXK71kGNnLYw8Ra", - "privKey": "CAASpwkwggSjAgEAAoIBAQCgNnacFOUn73TenCv+AqNqSUFVf6rTHKOXq1JdxpiOvnCTVs/jGzFwZv7ytKMtfJf2o479TQL5anFnjOos6m2hxFcmF/W9XdcUEK7UiAD0n45RaGTOisE1ifZ4hUcdl5W1kmKHmbwvwcRRxXuoBvGYXq2ZJe0fWqaAxHHSzyVved4Sg80NzwX5mIwRK6Hg9GYCxj6vpRd5GAaEgxIk6aRnhV4zWOIYPnGu7ifUFz2mEyzB0a4ihZ2P/cLxr85Jr+GmJ4bwb5nxNtx5EOHVpAo/JFUenqc9bTowTvfWbOC5zsW/1FtmL8cFHUdVjVGDLfn8go8ZJ6tbMoTsbvDqNUNVAgMBAAECggEAO5Kk3frDDutySIhHr2bpvs7IdXNIYMGobvAsa2Q6O/HCSHciS+9Dnekeab8TYgmPNA2zUKq/LWEQFBIIzXTKGTm5shd8r9Jh9DsT10FPIabms4ye11Iu76qCNGRSgkVoTKDG9GcM27EwP7uv9FXIpgCmimjY2CzL9tuU+289G0rdzCDhYXpyTIPj8ZBu4sBp6pPBcLsjMuzzdyJ1OjczrSvIPh7hW3LQLS3i6Da4bq1yImqwyqN7tK94W4vRAxRBFjls6sdMDFZfOJST6l+qvHwcv6ZjwFQS+eJOPLcfqAxypBxOqw+Kmv90xJDJiJkq+jgsTnrJkYCW7KeMRaCgbQKBgQDO8ar/BlXMDoAzdGysm+cIFeZyNKQY4QLsJlH+VmpDXxdZnnkIJeQ6dfnbyatCaqHaqCKkx/2W06ZZOXiDjL6a2L1ZElfXszylYfhapvGDKIVqOIg1a1KJCRCE/MVzpGqmmj2td1O7PInGKTvA9zwTkLnWanNm41m+9q1gPFIeSwKBgQDGMOxUGkeSHAauoloKd5mPxbbm6rWm/a/Jh6gF/DuEU4lpNh3eZb07DpNq6g8xb0Fd3hcA+kJfoOZwrIA73SYCvAirO966ZgolSNRNakQV/QLEPgkL3FaOwocyDeG/LfN3uxvjFdIqCFzhWPhdRwjGNUrSeeownEGGsgZCVHqg3wKBgQCSE6sFi75CZTX/nD4d9Yq2fWcG1LvEyAhdE4urQeqOlfAQlbmPk9evoJl3mLpoDocjpq2VrYoGzm3M67FzAoWFHltCJZ2WJ/I2N5qsus0eLRtH6JHVS2WeT6S2iwsB31xdL+E7slCLiWcjVvXT93ETyoQzoz7EsNUn5E5r8QhyUwKBgHpYrzuH8ZC/3lwl+yGlDVYUvsE0OSk6SC9HoDD5saARla0ubCfjdHqll9mTXgetX5PbyyWeWCUChd8ejhbmgVWE0HEsh2VYIoE7wVt880UDqJaOmTUKMyDz81OyAB7t9fN+vUtlKBUsjnHKY5/pfwAk2+isvCZ//29wLK77yavPAoGAYR3tpLA7LB91/YWuS8D3xrda7cvLm3nVsik4tgWl0VgrewyGJjKWCvsV/SupVBIn62PlJzMSLDUzQDcWw/9mZ9isAKUBZPAI7lYzuuw3D6j3/05I9vKvHRUtAH9mNuYNu0lfqwyCuMoV1LD1wTfgaHSF1N8y4EoqQrcUBM1qrXI=", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCgNnacFOUn73TenCv+AqNqSUFVf6rTHKOXq1JdxpiOvnCTVs/jGzFwZv7ytKMtfJf2o479TQL5anFnjOos6m2hxFcmF/W9XdcUEK7UiAD0n45RaGTOisE1ifZ4hUcdl5W1kmKHmbwvwcRRxXuoBvGYXq2ZJe0fWqaAxHHSzyVved4Sg80NzwX5mIwRK6Hg9GYCxj6vpRd5GAaEgxIk6aRnhV4zWOIYPnGu7ifUFz2mEyzB0a4ihZ2P/cLxr85Jr+GmJ4bwb5nxNtx5EOHVpAo/JFUenqc9bTowTvfWbOC5zsW/1FtmL8cFHUdVjVGDLfn8go8ZJ6tbMoTsbvDqNUNVAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "Qmf4BuSEF3q31HTpBuse7aPwpaYzJ2e7FHiGSHsmhA3uZz", - "privKey": "CAASpwkwggSjAgEAAoIBAQC40RyUzcdSIEiXOd5cb6Aw1rH2WhagwIX4p158NPDx57pDbjKVMgZXNJk6Mmnk4h0WFVxugFJtG9l730y5ErUa4kNFOMzG85urHM9pvWZAtqwggI44uITxST8mYGXwOT3jIJaPqnVmSQ/8TlAbQ+4tTEH2TfJaQwZ6uhn3h3RIQGyUm2iYrEa2eFV5x0aUXln4Q5xJb0Agns62QXJ+m73QaJhp3/x9Ul5umMpDRwWgfMZWhiHi0Uo/FwcZpmvNSeEZlnAyYHMulNkpXMH6UYj0vzHxH03BRkBAVpZl8aD1asKtOI2H7XHirqCcRoSLmPOTKgrGktDqsHzl6izy+KTfAgMBAAECggEAV+aNE3DztenI3LQXQBuPMutJ5QNf88DddzATTjvXxRYTjvKgeDk8rslDf1xu5P9uGgy604uQqHgwbiv8T6HIJSssF4Y2TwGaLj4boA0GzwySvTqnae7IvAG8WUJL+X8gIiBju5y1DZr+UV/l1bHvW/gC/2R/OdLbCA/vPb1c3ueF7k21ACoDN6CE2u+IxX+FfFmtvwlzo0zYobgNYMIvWaeM7qjMRHH/p4KTrutKcgGhZhq/0hXymRaX+Uc/aKyZkVVOvKPzHOQ1cucpv1Hzocl6dAnBgEOC0tm5/ZG98t27xRSef3zeElarzkmwh4vNDDNh7g7HLkPY6LWeF77ZAQKBgQDndF/9iwKyDw+7zPs9QH1PReW6joZaUY7UFFsHpatUYSVMt0TDOo2WrGcr+l3B82oYqQ88SOlH/JPURPQbsv3KB0r9OtfcF7RTxwFg83KGl3SyZzaTxchFIPF1hjp/KpcVQBdGJmFOybBg4ZomCkTr27ctOtP/uFoR+nx8m1SNwQKBgQDMapiG2Oj5UNQ8emHcYKPDuIiKF1y2VNUK/PXht1GN++D/VMykXWjXxytEnn/UrLHNbmuH2dJrXJIjNfH1TNcRpsM6LrgtsWLTex9WIeT7Afgvbs3sR19FkAHssbEXVx/W/nK0y0mADyXZ2NG7UZMzKdpELjF1+WV8tFj8RM8anwKBgFLpsH1OL/ADTzqSaqn9kSY1vt7+sYhnUQgOJrHtmhuHFWqO+HYLYq9IIUlyzeVtwmMFJO0OnWrpQze2X9AQZbPauvVOAAfbAgFE9+x4KV2noelK6hUzs9N3wqe8JvZpFmhJZkz98Lvdqm56QtM/uILZWZw9R7aCntlz5uZoanjBAoGAbcrTIZpfh4lidRlGdpdxXi4/J+xkX4ow4zX62sEbjKc8sedaAu4o4byYAMMg5Znb5froxo639fJCi6btzlL3MQPg199ADUq5Sd1Xd2u9ERR9uPxKnh23jiVK41aNR3wEHfWMpo6Ja763Fcre2z11UoWoNfaZmkPZvqEfKl/K3QECgYEAzNCm3YWypjjJ6ny/VHCWmjLZbgJN6kNbhfj/djr2As016K0DJqII0E66QWT1BjnMe4uBkabNvBCTybb4o1n5C2vRgvYtVJ/TJsmPKLxrEvARiYD0N1+v24P1G3BTpnVAqVIihMuTdHQIkBmqlGHOUmOfZ8CJb+lda4wMoAIxJSw=", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC40RyUzcdSIEiXOd5cb6Aw1rH2WhagwIX4p158NPDx57pDbjKVMgZXNJk6Mmnk4h0WFVxugFJtG9l730y5ErUa4kNFOMzG85urHM9pvWZAtqwggI44uITxST8mYGXwOT3jIJaPqnVmSQ/8TlAbQ+4tTEH2TfJaQwZ6uhn3h3RIQGyUm2iYrEa2eFV5x0aUXln4Q5xJb0Agns62QXJ+m73QaJhp3/x9Ul5umMpDRwWgfMZWhiHi0Uo/FwcZpmvNSeEZlnAyYHMulNkpXMH6UYj0vzHxH03BRkBAVpZl8aD1asKtOI2H7XHirqCcRoSLmPOTKgrGktDqsHzl6izy+KTfAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmQnr4zPrD7UJRyLx8p3hkd4EXghTjwBHjnSVKwLtP3gFC", - "privKey": "CAASpgkwggSiAgEAAoIBAQDaDKzUGU5PCmN9cfxfi2K6LSf6Ro/LEJmBI4+aAZWRW5c8WJdqNIME7kQ0vLwr4ZjxVizntHjKr8so1CEVlet5YxQg2ezhsvnniTIYgoAo/tuLt8rZqEL6Kg0I9SYFN/zWHbfsJbGqFA20HoUSjPnO6MiSYrXHLzlJwXX/P0twRJGW1a0YjI+A01kI0XrTYfXNBgSPqd1gd/nWqDDMuulF+Y8dcXPRoUHEAQI4q7nMUyTSFX7Qhv9U7fKIQ1cGCEOKjWyB5jt8xK+cfKWJ9Mb1eLvPu/ZBnfjqW2g8LnE1TbZ1LvFmnbQZgPWZ12AfAsFprswMcIHNxoCnz3yxmUoHAgMBAAECggEABS4p6PwU7THM/Uz49vgjx1KNUZfdkLB7RSMoJTuGZyaq6CceqcpHlpVmj24wdkZs0McAWBzkhcQ5amXnx1CBgKfG8aTbyNzsrQCIbSakjtTHOIGMUzF5LeJT3vOcDKGw4xFfrj+TAfxp+u6CsNcilDTZlwi7UtkfXk43VHIXg7pCE/CxBstrlu48/Rsxvymzk7QHAkUneG0oVWJnkcPQNlnntC8XpQMqapxcDdIWjhXctouPn2hdVDC2KpyC2MrQKvbDb7bCVi6qntnryEe+DmX45TaR6TPfvnh1oc0ov02Tf64TJPieql+tjerlG23YoGWJH0wdezY+R/jIc5r1WQKBgQDzneebNpmQqduM0/4HI2LuI17J2OebKt7voQDm7q9ER7hsIPTUxXt9dx1RyxVZpXxNKFWJ3ITUtx/cLC4fxXb7CHxzWNx6nhAsnjwJeHbGiNw3x0c6fY0Tn6CV83WS2+XngItH4frT78nhnYFbnD4HByUXCfmlV6fR0UpTfkNUswKBgQDlIhKTh3IHKtmO0k8edbfhH0cXergbebp01Agjh6mnmMnsjI0XYn+iI1QK1ClN32mzlqHXMtGKtc2zKX6yhDMT7kPglVugKQWaQWkd7RHCQDaxN7tpwiDaYjVL6TqrTa24Dy5Lyrem8j/i5Bey5+TH08u31QuyUQGqCNJePnLnXQKBgB1cy9yGUS4BewfXSUfc+QCQ3MzhStEF8sbZFf2/iPpm1pCZzEiU4NR3dd405wbeDkRSdzTdklj9FWb5IDoOF9Ab7rwMWs6gnHx0OfI+RbqaJkjGyQwAs+9Ijxdjt6kSvfwQHzlzwEKpJSD/VecPxt4b+1lyh1dpYD3GxvmXP1BHAoGAWCd5uiS8LCHCPf6PzgpASm58LX5bYsa8g8Int3O0Q/S2izmv9rVAoaKx7NCfa4Ru6FclwOOeVp2HnEx0oD3YYOykVL1h2QavTx+nT4or8O4/nILyqce0WBC8rI34sntaQJwmlaZSbfp5tdNHgt9Q18iWcg2XSG1+FGr8dKHWF0kCgYAM6YV4r4GzAI+TgqI63fiRH3WhtE+RYhW9geUcGpI3PcGATee/R5xGZ9pYtNRHrm4DQ5EqTBiIZNKmBLmpanilhmoeju0+19fojk5tqlft/YrMOxfSNZkLNbm2Yww4vNHTCDvw8XpDlAjS4yyxRSDkucuCdsP6yJF9IyTqQsbrWA==", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDKzUGU5PCmN9cfxfi2K6LSf6Ro/LEJmBI4+aAZWRW5c8WJdqNIME7kQ0vLwr4ZjxVizntHjKr8so1CEVlet5YxQg2ezhsvnniTIYgoAo/tuLt8rZqEL6Kg0I9SYFN/zWHbfsJbGqFA20HoUSjPnO6MiSYrXHLzlJwXX/P0twRJGW1a0YjI+A01kI0XrTYfXNBgSPqd1gd/nWqDDMuulF+Y8dcXPRoUHEAQI4q7nMUyTSFX7Qhv9U7fKIQ1cGCEOKjWyB5jt8xK+cfKWJ9Mb1eLvPu/ZBnfjqW2g8LnE1TbZ1LvFmnbQZgPWZ12AfAsFprswMcIHNxoCnz3yxmUoHAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmbF4cqzgjRthALpoDKwj6W1gDLVQVorkrmHVgGdgWkPzJ", - "privKey": "CAASqQkwggSlAgEAAoIBAQCyGullIgkT2WDF2RH2SCfhwqyRU1EN1OtIAUSo1AxlcYgGAPBVWDhO9Scce+nnGvlJY5OAUd7j1/QX/6/1/DPsRyKNjTqtUVrDZ+MUGYH8js/sSlDKeIcgjNXJ71EFPsqiGg0QS2PojpDkNf7jNDtCxEVTgGYC4wWb2r4cx/wcHVdO6uarCRGU9Pz29lNtNuqgLouLf+cLhi7G8kUisR42NWEP6qC/r4nb8GOttCcjrTS7tWgJUD/QBBlmYwJvmsVbJKQ4gtdYTrcn6yxoj0KrN05HMIw2cRfL5bgMaXO7Lm4IhnQDfYjrJVyfj/ZTJ9SICUG1SSgy0x9gwqK5HibNAgMBAAECggEAZ3RiZjBi/XijUclJObmoEOc3viKbTmGDWYwDCd5CZRqRXItnDuvzqUmVsmH3+Boe+5Yvs7XatpZWXypSV5xrvK+FTpvenZZIFoFd0esPKlj6RdLVIwbn1ux3spikg1t58LcZJ4HjQs6tMyJ6MBfC5IGFk39dwgeE1oc1LxqrQth/KzSYEYZgGU0bcgpNdAu3hHH1PBHsMkJ+3/mDhpOQbj3QWIm9wqRTbWWGehw3TnggwYVPC+xLQauFqbw5LkisVfZBlfN0D+fsbeivtBJzq+MZeZWi7BqTvAbPq4gJonxe13CApp6sXyUAup+Z7VOuDRs6wGt/OqUl+W/pmrqBMQKBgQDp1l5LmR+bj+F30FqnwWGB8znsPhf2CZgWy15IwavuzKwCCT8Z7uTISldE79kQWdYUF8gAuLbSNycUmM+b3XIYPo/8jXqWPooQeJ/M7umrJ3H8PINbNO7Q2XcxBjOFkZAj2P8drSqpL6Amqsay30+sd9ETaKf1WtN2JrWYFuQ81wKBgQDC/E2jt58nh7DZRTwMFtS2YutAAvtESU1xnwrGtuODmD7Xyazo8QDWKXb8XepAKxW6K4oWeW/SDXPtjbsxIdeVlYCQc+G/ekF8+oYp8MvDeq/mR13/JmCuW2b/aNNBj2Q7eRo7vWEVYl33M+qdZbYiFiDw3STmTQ/E/wxZPg2A+wKBgQCtrU50D9LuE7t+5f2vQ25Mun52/NeHIjEYHQx2NYKh5tqK2JtJg6nhKXYP+aTbBB6A5fjisE75a4VXQvhP5/XqE+2Vwu8d0G1zNmRaLcjYGoAKvFdD0tjdvedNPjHeLvND7NPvEsLwzjLBBW53RG1Ex+k95Sl6jm8o/i86OyZiGQKBgQCYlgTT96AOuTsF7A4/j6ZKTEK4xxyGpa57GfC+7ORCWOPkzigH6oGzFqPMfloQeSb5l5TqXYHKKUjtP5qbqlYg8uu3H1gsFaol+Y8ARzXN9batSHAgeZHzIAgMG6YmieXwPKbw1RSiPWY3S2NwZOYQ6qxAkW6M4wVSLh0lwU+j/QKBgQCIA0BuYWxcxPoeEiKc9JyFAPKt8ISCV0BVNYm5pYgw5vjEfNO9d1iZFZfB/ECdvRuuWxeiKgy8+l78XPjIqFyDZ7z1gI1XTz75HULbWUgjpv88BikhAY5/qjhri84Gzjfa4tZmEZKxu3K7ii07ZKp4o7k+6rosm4AZpOKi5WpBfA==", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCyGullIgkT2WDF2RH2SCfhwqyRU1EN1OtIAUSo1AxlcYgGAPBVWDhO9Scce+nnGvlJY5OAUd7j1/QX/6/1/DPsRyKNjTqtUVrDZ+MUGYH8js/sSlDKeIcgjNXJ71EFPsqiGg0QS2PojpDkNf7jNDtCxEVTgGYC4wWb2r4cx/wcHVdO6uarCRGU9Pz29lNtNuqgLouLf+cLhi7G8kUisR42NWEP6qC/r4nb8GOttCcjrTS7tWgJUD/QBBlmYwJvmsVbJKQ4gtdYTrcn6yxoj0KrN05HMIw2cRfL5bgMaXO7Lm4IhnQDfYjrJVyfj/ZTJ9SICUG1SSgy0x9gwqK5HibNAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmdycNxu2hJK68SQovw5Yju9FnuLB3NqY6mk15ATr4nkYa", - "privKey": "CAASpwkwggSjAgEAAoIBAQCp4KItjrRxtJU1AjHPhNvcH77MSh+XhLBMZaOOTfsmJ/9FtkUwMvWCVcfgUhi+CMyNqZRbTovcqr1U9hn1szLUjxhOPJ6DoFVZxZGARypXw3pTzZkLLYTJmOmNnQYG52Go74DmPS5E9x/iPxiD/xZTSsfAOKTgD+TmSRWxm4PXL4953NDtY8TUIdXSXnSgRpnJr99OugJDxwgW4HCSmcYYGo/j+xfjd8hwW2x3Zk663i5FlYpGDms+V39k0TDRFbCf3gcSZ1nOlR8CVMiQNeqZFgOGPfxOqKuOAfFg502ghaRXFQR9ZTO8HMOiN/b2m9alg0DGVZQ+yhktLuQ94JwPAgMBAAECggEAek1mlWQLV12KmppU4DGn1GfqhsvKyNxXzPjT8u0Dpune6AKc92GIzegSOdcBRzewhUEUtVPsb9dg7h0sfW8hZlULS7Bq8xrot/P8mB0kSAFNPa5kw95mnnl/lFv7bdcBwY2FAL4FZNOCWfHRJZ7uJNNO0n41fbcTthPiEXeESNQhWdVczNMycqod21CJLWuyBGv9LVTamWR4fFB2/ixjJNKVAVh0WTBk9LMURwgRaNRzwq+3NTZW4B+brZTLAAVav+xxIaJZWVv6irGwtXCvZTxpTL0vuTS41YH4oAhvWVvl/XG/CokEAh8T3CEzR23FN1A9Bgm31PwbSOeiWjbZuQKBgQDavlxkOb5/SioGpPpmMBdTTt5UO3oiCvVrV0yZuOpAX7+1fSBegksdfa7XOkixochmiIgbtuTRS5YEOmevE+YPEEHstzpQ//7lJOijzCaxLHTLVPYVrLxXktnGb9IWygna24BGkhAVvIt8dfwg8/3nsBoQ3omIVrDV5u5ycbQJIwKBgQDGz53Anqi4jEKPv60NBbj81LYR3RtAvPM4OlbaWLAeERaHR9xC4nWEgwvEDm0BWrQ77cGZATZ7QK6nstrKue8khkxbIKbibWJJWJ9F57yTZhvwq5sNayjJRbIsuKkubdvXaJp5ZDBz8L0XK693Rj4MGXp17ulZ8L9fwPcARt0uJQKBgGmKsb92EREPsqlUDrEhgQ+kHSfdLregO/vXulDtZLE8wZ4KyoRvL1kCXErih1KVscCvHaTpoQvPAYn2uDJEUptwB670VUHh0pWzMkBd70lLHutAih+5IYLLiyHwsBho0Up04DasoPAr8c1SjB1GPHr+gAUlqoxK77W1X9V+QRSrAoGBAIP47c8fgwB+mvCxXD54vgOXcAULsTuYMhvxHhZzKPXMghfrK9t6WGhOVVEgAlwTyfC+MvVOSMwoc8f+gh5wrr6gJ6+WTTGhSs1FdvUAj72I2qM4RwTxTXHOQihNrICVjInBdkl+qGtOMzdeWGvkxOtjPldq8Jwzo9X8UfptEAXBAoGAKaDO1B5bpFsiBlQKZmzeg/6vBa8D/F27YVP+bxoqeY9HlSBDtDxI540vyGwE1tJSql3gPvCZzKPxpXYPeXHweKp100YIYnRokLlXJpofA0dyJLKY3DPGAPTv4cOkgspw7Ckr6vsJL0/MK/kW0WfmSptAggdDwjgeM6mtcVlOsI0=", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCp4KItjrRxtJU1AjHPhNvcH77MSh+XhLBMZaOOTfsmJ/9FtkUwMvWCVcfgUhi+CMyNqZRbTovcqr1U9hn1szLUjxhOPJ6DoFVZxZGARypXw3pTzZkLLYTJmOmNnQYG52Go74DmPS5E9x/iPxiD/xZTSsfAOKTgD+TmSRWxm4PXL4953NDtY8TUIdXSXnSgRpnJr99OugJDxwgW4HCSmcYYGo/j+xfjd8hwW2x3Zk663i5FlYpGDms+V39k0TDRFbCf3gcSZ1nOlR8CVMiQNeqZFgOGPfxOqKuOAfFg502ghaRXFQR9ZTO8HMOiN/b2m9alg0DGVZQ+yhktLuQ94JwPAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmSSEcBgyeaT8U6HykT9ErKHUKumaSYPBo7mdqTwcvq55U", - "privKey": "CAASpwkwggSjAgEAAoIBAQCv+kI/GSApNKExW7zcz7lmyTvX22eEiLhvwQv/LuGSknIqlmC8IqoQg+8l/MulZ3rKeD4uuS+VDDkNPcDDONHIjtlu1qKIYwec5SUiNhPTVpNiCeRkyG90XikwPEXak3OOyqPO17TtvK8n4/Pk63ehBix5c2GNoK0m4b7ThQzgUoOzwQ+AYRq4U41ma3pircE8JKYbywGmJFJdswvVjg9eGUgADfS9JhM941ZgQVvqCnX/yrxjZTMw5HVS+MBh0ZFHa6BK3rTF4yrQp7n2u5Tj2efs1Ci07DJ5hAuHZOn3JnX+UNLn0HMm/Bg/hvWOrc8zYzcZv9eVLfPCGVaUDUP1AgMBAAECggEAJrQ2Ccau6iEnKsHwgeg18MNlpA4fcGjZl8qvpspa1m/bKD62u+or2UILQSGecJyXxxw3IPOd4Xw0uBLS6J0AlsnETLpsOO7+56UGS8X1ClBKTg+66eeji8aB7Jf1DSPNEKTE7mNG6drL80wRglG/l+zRr0yPMiUasCiKXd8ve86Mu4iBLUdStBSHQAIMWBqTOP2oo10oW8Z54dGMQ9kSXcbtUFsD/5FJATQ1AlMPluHqyDf/fwqxAN2DSx2kZ8zIVFVcpK3250wWSY27Mplvq8VTdp4CjVpqWXxb710Oh969FPgYvNMHVHfuRfBuBiRsBlJKaVWtFWrNtWO9vngSAQKBgQDbE4OzCWppyqQ5DzUOWnI9COdjIHfqb1ynom2X2knr3Op7+YNpI0yUDygOr6OunKycU6uR3oWrJRvO6hdKWYYlFWu5BhSqHCSPCjJl/2OERRAkqmUxgu3dOM/WzJjcNLXB0aZ4LNxyS5x8ltSlpLR8PA1ZIliSFhaErHqTyqAywQKBgQDNoyp8po2Mmd5xNbF154BVJHuARdnu87ypV0otBW/ce6dxSDa1nKsht9I5C7nh1rwzN+srGrunhokHcSP3BOYmYG5bPn2/+bhmUo2KPIcXBzPFZEtaT2xYzn3DNZx1G/dY2ukgEZfS1KM6FcTj635P07zdcLTFWks0HbqNwA1CNQKBgHCU67ZDHXN2VsSX4w0YP+LLw5U2Z0mLpxLiru09mYVjRwEk7XpHUKA51b0OV9Bw5WeEvAO/VfPoowzHUea8cOp3wp8X1+C/i64ScGnoP60GjNA63Lv/69smyfA5vkhTsiADbEgPzc3Su31vSaJCLRo3BikLNHcGcNYHiQqQM5lBAoGBAJlNnS0Ulc5OH9FScBwwHDJdYlz8tj44I1wzoS7zMLO0093WMkMuqz4V5nl0zn0ZM3ETrRSTd3arC5kqtd9AHbxag6suaV0ndFuEC9UUzrlSOzxbSvnm4CVMu+E+JIgB82KgwM+Rjhg1QgLZm9E3DRHCDrkffwTqDcqqpxtqI/hJAoGAeS3nCY38KdaHbGTT7TmSe6HSt/kfb1NIygKTYEWNDc3kR9jubfk81/iwtAAvLfE4FY82TwC+78wyfpRa8Kt8m0GZLmyf0/8ACQd1RSig5P5ORKN9Er3nUXtvnr6tfkMELuPBWC9Rs0Vky8JIPJCfXEsQEOOGcuZDLsWF9Oe7Vwo=", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCv+kI/GSApNKExW7zcz7lmyTvX22eEiLhvwQv/LuGSknIqlmC8IqoQg+8l/MulZ3rKeD4uuS+VDDkNPcDDONHIjtlu1qKIYwec5SUiNhPTVpNiCeRkyG90XikwPEXak3OOyqPO17TtvK8n4/Pk63ehBix5c2GNoK0m4b7ThQzgUoOzwQ+AYRq4U41ma3pircE8JKYbywGmJFJdswvVjg9eGUgADfS9JhM941ZgQVvqCnX/yrxjZTMw5HVS+MBh0ZFHa6BK3rTF4yrQp7n2u5Tj2efs1Ci07DJ5hAuHZOn3JnX+UNLn0HMm/Bg/hvWOrc8zYzcZv9eVLfPCGVaUDUP1AgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmU8D5FgAjyxrSaobQT2FhMhx8Gxx117K8U8cPNYX3FRNX", - "privKey": "CAASqAkwggSkAgEAAoIBAQDIgr95M7CC0YOSJKhNn0g3V7GXwdBkd8HoGr+cFWKps4ghZeR+VYUjK5Fu93TuBEdeNac5KOAGfHLwN8uSdT4eN+F1zcb75/E+1RC2X2z47PaRMg8vXQ5AmTkXr6EX3E/IKxR/QaURuK1obTBiMAVzhJ4FVuYmk3VVpqKWO6U18I6pT2rfARtlEJYG6GHvqnSyyju/9TKL79VjD2wJrmNCS9l+yXSKAL46AsBHmyTzlLn1/cbRTFmG08Nm7PSokCpOV3ZTdVSPrJLpha39yMW/Ex5ICC6aSCB08lhCSUA4DoMOcYulOWO0q4rB33UokBzjAZAsZMlLhWSoBd99sSLvAgMBAAECggEAKrReH2w43cPNp+SSy+VutgrBUjb/MUaoT8zSnmWXm9kW1zYiUh3Yu0LeOKoPh1n18USwFuZzwC3lNPBNNSYvUrRIGpT3GlOt99ndM1pjlSiy4v2sakQBcxSvKjJHtxM/ErzKIshSZdHVbPZEZcUghBfsp+p4HiMtzE4vNpwBddkjQBxxAzLuua81W4fXdVcuqpH/1z1vCuzxgX8MpEjepGR0RIBPA1UsV8yb4I0w2xD3XO4nS++p0tZD0Z8SyBYcVRx0boUQAhghtLXhs4qv4VDkk1uvfU4A63NyFd4OQMwd3eGOnqxYQq+M9vRNIP+CenVfO7WiQEY1SOl5PtZCwQKBgQD4PCycyQ/5b82WnoAF6JOClgkAZ9wJXVx3Uz7QPQyoPoF33D1SowkMjEL44s8IsxSLDcU8Dnrqji9qjcbDgepK/6AN8vgzWwnitoX3+X56z5uGaY9EZphWk4VEnYQZwQZmQCrnLFaIxnqzLTxa+gW6VFvahvaKu8to4th6aVo0XwKBgQDOyGe1eqIyDMaXecXORNATt5M5x+FKMEzIy/sL6P7ZCKYqJ7DJP+qtlteqGcOccvDlxCKaM8eIrQDPHIRFyAexsKQ7UG577zHle2EE90LbYxKvOwPP4hRm2cDBc/suCtnrGjeYCHCTRcoj8TKsCkmdd97wNOmkPoFa+YtoFOYbcQKBgQDhNaa77+ZgRUDeP5qiwajitsAf8Bo/HMbBM3MvddO/6EWJuvSfvm59RduU9iEjIWWn6qxgmjqGBs2Z/FqyEXHA7T4GqcLoxNWpLDNLEL3hKe1N+wMR6YqYMWqdH9Mzkl398oV6Ck3P9VJosMerOl5r+BEFp6CRqWMYG4aPOHmwPQKBgGo9LoNf8UszoyiaCNXUJu+qZnrOReJ+9ERKAL56w8ywE+ceo0aSjzkGgeFEAWs05q212m1NYxvGft7p8M+FWOajMY3D4i/Mkd8sR4lsnC3pNeVPtcKtjfvVrqH1u7xJGPMgciWrWGNh/NwAhR883duIhcL1/IBFGOKryUL9UcgRAoGBAOG2J2RTW0NyhldTOciRpRuMTNTpr4LTGE9S//5S40i7YNv4ZFzUCabPXCyRRYyXUbhegeWmlNgfqRtNF+QyH98Y0ZezGJzQ1S0uQSnUeoqGcA1ohczWuOXESe1MgVS0Gsoo6Ytb4muYzlFZ97Y9gBM0KhaQ3GzMzkvNnX+3hqZv", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIgr95M7CC0YOSJKhNn0g3V7GXwdBkd8HoGr+cFWKps4ghZeR+VYUjK5Fu93TuBEdeNac5KOAGfHLwN8uSdT4eN+F1zcb75/E+1RC2X2z47PaRMg8vXQ5AmTkXr6EX3E/IKxR/QaURuK1obTBiMAVzhJ4FVuYmk3VVpqKWO6U18I6pT2rfARtlEJYG6GHvqnSyyju/9TKL79VjD2wJrmNCS9l+yXSKAL46AsBHmyTzlLn1/cbRTFmG08Nm7PSokCpOV3ZTdVSPrJLpha39yMW/Ex5ICC6aSCB08lhCSUA4DoMOcYulOWO0q4rB33UokBzjAZAsZMlLhWSoBd99sSLvAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmbdrCRXAWsGxXTzaTkyL7o84PXkLQ8iyjWAJqXQrXoBBB", - "privKey": "CAASqAkwggSkAgEAAoIBAQCgS5u787XHG8wm+wPq1JFbNR+VBdQz2VTygxOZ5DMJn2ZWLbXEES94yzO4UaVA2/umXtTiiM97VPFBgYC3sV8rGg302qiNApY8z+OlGasXuoZBW6aWlFU9F9j/8jIEKEGtgqHIYNLDPjjFVa8308TCDC6otJmzCRuCJnDvYRkRaVlha1u3OSwxcKBVG4EGBpK1uOw4IpfP4zBkZAw4Ig5YzVA/QciJJ8TNRB2BwIv3f3NHWN1wV4v0MJIdj1JW5kLaU0WeQxxAgI6DJ9KG5h1Lxi3tqgudLAGF1mpU2VXBu6Mrnpqm/MPKHuoHrA259MSSIQZJ+75cDo9YpzTOvTQRAgMBAAECggEBAJSH2uObHQpFeLN6DxQvKg2AuSYGQ65TqQIacTQ9HwnAmTwrmOz4G6vrZp5ZkS37aUCtSMgsi0011WOkk1gjVBMFTn9fiaU4C2yIGeGnWkFfhf3T5hZLlnxIt7vaeXwerVUQ4cZh6YofAs3f6r9pTD2eujF7P5yFSOcdpbI6n9bf9plHJlVNwlOWvFrF0uO5lD4fPs1D6XWlKFuz2G6LOAvexZfbGmac3fpcrgY60rlXXOAueC35puyuha56RQtwKXHMC3/DcKExlRNXZhQByV9GrZ5WVWgSkouE1vbmFYNiLBaPWH4fFXgbkCvxlxiZKiVkx+vIRg70c8ZjAxFJDMECgYEA1TQr89ewSszz57am3oVaia+cB2vuy1QSDzxzyeGjbIpf+hbuxaZ7uHKngHV7QhJoC4OZ/+crfM8HvjSXObK6ZVWiNm+a59J81CGhV8XqzxO6WRNiGKSuNfxYjFv6BOqMi8lT/nIG5Mg+IjuZ6HVYcK5lLKS3vRlfR7uofE0hXVkCgYEAwHimE/oTXTLbWDCA/w207RJSyCvyroZ8+UXeSq7fKHkSFYAaPjq+NN520sR+E7g8uZqP4ZF6Pv/FAV3hVpuB5+wtk/2uPFO702rre7Rw3B2Hq0QM48njXfcTAiFfC94LRhKdcSnr7aOKnwAyl8EP6FSqXNLoMKnvV0iFDX/tHXkCgYBbwl6ATf4z003OFlBvSNmUlJ4Em7FklURIhm4XHyOk3VE9Y41UR7jLw5zPrsBjyWQ6QGORPb77smbUt/G2BXQvlNGBuDrlNzQ+YFL+YdITWZxEJhF8JbRMy9SYZCWQ5BmlN/sMcasB4CTNuvUclRSBOq2UrzfdDQRy7RMwnEmV0QKBgQC+CuLBOt0/2uVlkI7uR9RrePowF+TJmpVvdCNnTn+d8N2ASTqgU1RX04kz1zw9sF6VTR3gNcqkxdr53H6RC38bRsJCK+uMOYlt2VamkKYXUTkSTGEF0eQkdb9ZDSZSC27KQ7sdb606uY44LPPHj6NrXZ3RhZYp5sEiR8LIb5Xq0QKBgGU22UnE2iIgRKsWZ1BYQebnp2wmI7QZiU5ZmVWNfuM3kyjaA7IjLPK3zI/wi9NV8GnnpVWVOGoFeQIg4mAMfHSkCHEPG+8Ib0cAu2yHEv+i8yn1xwZqSuUx1aEjlczGCFIUCy4X30DIdwtpsVlUlAdE1p1br68firIz6W3OKyn+", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCgS5u787XHG8wm+wPq1JFbNR+VBdQz2VTygxOZ5DMJn2ZWLbXEES94yzO4UaVA2/umXtTiiM97VPFBgYC3sV8rGg302qiNApY8z+OlGasXuoZBW6aWlFU9F9j/8jIEKEGtgqHIYNLDPjjFVa8308TCDC6otJmzCRuCJnDvYRkRaVlha1u3OSwxcKBVG4EGBpK1uOw4IpfP4zBkZAw4Ig5YzVA/QciJJ8TNRB2BwIv3f3NHWN1wV4v0MJIdj1JW5kLaU0WeQxxAgI6DJ9KG5h1Lxi3tqgudLAGF1mpU2VXBu6Mrnpqm/MPKHuoHrA259MSSIQZJ+75cDo9YpzTOvTQRAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmWWYbfs1jFnZuKP6ky3Zf9A46W8R8tYA9eTqRdPobWFHh", - "privKey": "CAASpwkwggSjAgEAAoIBAQC7z4AoGK8K+kzoHhz5McSmP5IbsItENCi14CECnItsPXZlrUCywWMcXpIjh/FyMSq1P8d+EQryuYxnhdP3Kc5cYVshK7aaYsf+Pfy1sVzCfp59snGLKcKzetWLIDZsMYpnYp9yv2+ArxqGCYG7OikYgc2729ub4PkZpsBcQmsYy0lGyrCEId+dre0yGIHA4tYBzGuGfIGGfhLulyNbmDHdVkiAPyP6+VQvklvuzYim4KdDPIiklxbGIgQLM2WdrOHtS7PA9j5BzThzyjzUhe4JEsyS22oddbIzFai0rmYX06SDQcSY0PYWmpzOrCwmptNN/4XNK4OiR83Ttl6a7RtzAgMBAAECggEBALp71TL7H4P0+TxZ+kbtxeeVo8xexkoYyHufauee7Umy1ccr+twD7heTR+SD7ZiHfXKvO7TP02EkIGgCmHAJUOClwsjzEMPHZfHrNuxqikKNW25QKzIVa0CvrS4R9DgGEPmLEevsbhkGxX1mHyz7GSc+bDwmmK70+iMgUkzJnnHkZMsublJRd/Jc06VpogkI6SzR5pIHwc/t6B6U+WuZ+ohiJsEWiXrTEvD6RcNt/zHZAwmkatj/xkWcRRNhvS/Irb+ZkxgivXsk5XeNNK1BitK9Vnii+tVfoTTervKgMx18RN0hL9Fx4I01MwZNEnCpKxO+Xwg5oOwHY/TihgTesJECgYEA4y2YC8Eg0LIl+pnrNWUvja6RtJDSFIKGWOIAB1v+zBzszlDdFISLAeyZfZgwLzdOZZjTNuJp2V+pUsU/qTLqccIeI5u66cgRB2oI8O+g7XH472P7Ay/n0dFVj42fS6yZam8o+HRcK8av+f9F7YG0Fvs/ilrKg2Fcd4/IgenxXJ0CgYEA06NOqdNk2L3s3rgRkuBLoQ2gBmca0gKLcJOBfv2shiJ9y7dIBefSZxyMT6938U3NGvvzA8QB6/QZvz2BrgM9wS8bVIv25VtQ6tSatARXiTVfVVfAuEgErDEQWCdyqFerjDz4gu2yRUS6sg9rr7lJ1cXEGhsc2h5wzSQwGVMDc08CgYAIkorfPq1nUqGeQDqg7C2MMh8rah+TSI2bQwPvQyhtOVYyPtjo0kuQigYMuDZxQawCp26o7ohB/JseFXVehB5WppWOkGzQL4188yJdPR2ceCWFmwc4ypD72ONapGRzbZLockNghLuJp1iynVBdMvzBtT9jkCN+K6lalaFiTZqe/QKBgETOdFW8V64r2WXznCsPZyc+YceTH+IlV6ZLHq/l04BsmE9yECVzYDGL04ZYuvsl20gpn7GauTE4VGKboZysixhSs2UCeEvLK3ydkIp0Wu1N/+ekNxDywSombXTrplha4Hggnn8avnnMxZH8d3tTF1E8EeyW4gN8IBph6I1jMtz7AoGANXAUXM+PBA9xe7QUs4ZegR8reObEW7sUJ4rOmqSoTB5HIFHU6L37QJiAaHAF9S1Ncvgo7v+/GHRvA+O/FYy731kW/SJfHkkmPtfUl3u+gtdIdiyYswV1gxK/8PRB2dlbkrqITIBfHGqnSVVnIVqhy/wc7YIXu4H2mrpvrd389nM=", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC7z4AoGK8K+kzoHhz5McSmP5IbsItENCi14CECnItsPXZlrUCywWMcXpIjh/FyMSq1P8d+EQryuYxnhdP3Kc5cYVshK7aaYsf+Pfy1sVzCfp59snGLKcKzetWLIDZsMYpnYp9yv2+ArxqGCYG7OikYgc2729ub4PkZpsBcQmsYy0lGyrCEId+dre0yGIHA4tYBzGuGfIGGfhLulyNbmDHdVkiAPyP6+VQvklvuzYim4KdDPIiklxbGIgQLM2WdrOHtS7PA9j5BzThzyjzUhe4JEsyS22oddbIzFai0rmYX06SDQcSY0PYWmpzOrCwmptNN/4XNK4OiR83Ttl6a7RtzAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmeQvuGyVN9VWyVmKZt1U6eiy578rF6kiU3W24nVXi7Z1d", - "privKey": "CAASqAkwggSkAgEAAoIBAQChA9xZ7KGxG0ti+VUNB1KH0Z8F37F6zlRqdwtrD/k5RRz91T+DEwifasCLgs8pdpl/I9CwlASXwPcbgVNj4GHlptxzdF6zeSuP3QDlhB/jyTpRa2lw2S2zYhBaTGsVeMlmGRV8LFpjhnCdW2kO0+oGEofF6Tcj3g9J+ul96aNi89FBHYYSERS+MCHRYvCuiHOHXtDI0yYfXR19rAcvzFOIHX0EoeNfKd1zwDv6p5kJnPR3n0DtGsNw8A0tW8w6l8fQd6Y97EmqPRzBQQ1Gag4zGfW/eBu59lNonghfjWJpBRH7TXlvy0w4WILbnEaUOFoeN1+fYQVMMfZTVA/+ePgbAgMBAAECggEAP8XMr50miYQa/q9sPUXKLVscFfJ8U/yGuMg/sH7aIhG6otqkViDiyGk6q8b6kByWPSINVPK7QvO9q5o0UhmcDJ5jMCNGIuV6GHfbFAyZqNmZjIfzcivCiwrrGSitPQrjEdobhVv3zPWBgwGiganzRcZvGjb9jOo1ugJ0GlfAS79PLIbzEflQj1P2sFULk47IXT+jwv+RqyUH8SN91wWblCamC7O+fIz/4jT5ff8iwtbkflGw2Gbf0iBY1CRqEHAx2zTFch8WeJb8KkVbkONy2jP0zCVPIpH16gw4FcKvQZZAMdZLI98DZ4MCH5J4JyJA0M7HyW5KXh5xNCaHjL8GIQKBgQDMTUam/4YhaUx17p3C8Q+zOO9TzmUzQQuoMvaG3Xs9Ekp5ygAbcHCNQUZ/thIHBS7kKDSRq62Tm1PXcnboAoQ98dSxpy1CSda4v3gAC4TF66+Tx94KFjLXZelgBIHXOzcqB7YuDXXHTmUz53V+8lVZ/rIhPVq5ZbZeWYWIUWEEkQKBgQDJwnTuY7yVQc9nZd/loTPLKibFqw2YgGGVG0GjIYhXWQiS31PL7wtnde+p9B69bBkEMnBiK20WBjsBgOP7asHJIiidb3BjuNUKQYqBatZBJt8OPmS/6SAZGY5DAlgntpoAhyX8HixNHeHQ5VUMHwmStVXWvHZvSGYy47oyUFTX6wKBgQC/IMArNTvHgBoe7ie7GwgkE+yaC6nTZFPCfELz8rn7bWQtQdQN14gELgAFNFDzLl8q5Y4ghWqyf4rVMOmardgHl3jy5kJKFIgDeGSMLjp9artsVnwcFZ5kspu8zxqlP2mhMWu287KuzWGSSEQ8iftdYRBGVn7MmSIebEOnPvKzcQKBgQCuOyolT6XkMv+7p+Mw9wO2N8Fhw/SqtHsQe4g0KtoFrFJWG1vO6bCseNEtsC33oGj+EdyxOhUrBthf1QGL9UZBvijaxAiHZW88Oxsz5aH+g2Xuc/0nKVfZtRMAVP7x1KOrPwqTbS8OrXZ7of/OxuLKeaQWG4wfT6NJ4RTDLFIIXwKBgFYydorZxccoBmtQzWhQA/I/Xgm/JO+k1DG7XzHLvOztwCwazRoBiVQ+ecIc1c/tsDQD3DuZZle+oFWYlfR3yrpBO9v2JaOeD8wMb5BBg067VAZ7CcYdbTccl3ncObaawWttJC4Njx5GElrUZa+aJwzL5LnGq2offNDw4BNGyUDo", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQChA9xZ7KGxG0ti+VUNB1KH0Z8F37F6zlRqdwtrD/k5RRz91T+DEwifasCLgs8pdpl/I9CwlASXwPcbgVNj4GHlptxzdF6zeSuP3QDlhB/jyTpRa2lw2S2zYhBaTGsVeMlmGRV8LFpjhnCdW2kO0+oGEofF6Tcj3g9J+ul96aNi89FBHYYSERS+MCHRYvCuiHOHXtDI0yYfXR19rAcvzFOIHX0EoeNfKd1zwDv6p5kJnPR3n0DtGsNw8A0tW8w6l8fQd6Y97EmqPRzBQQ1Gag4zGfW/eBu59lNonghfjWJpBRH7TXlvy0w4WILbnEaUOFoeN1+fYQVMMfZTVA/+ePgbAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmV7Hr7Yi74neZktqNghgrGYNV9X8zys1YTm31npSdVCrv", - "privKey": "CAASpwkwggSjAgEAAoIBAQDCF5axKAn+m/82xRDrewOR/LWE9THASE9CDihLm8FnCRAQw091KwzDOsMC3I/+lCHwxNNDLLFpgpSFJ1JEftdpMj4h7JnUn+Rx09PW9JUtii8PZmbpJD0w56eu1kachHrVMuZvYNA6ge+ukMMQpL3Za9udHk7a+CnCITG9N0F3WGk9p7GPpPlSazR/DgJiaqh4W11k+UAYejcs5Bj3QU1ndkGWhfd0bBxepK+otk9qXWSwaaOKxFCVrSBnv+hxTkaenWJy6oAE0GoQNQdsRxS2yHlgZEMLjmpD2hnUn4GfX6BLwn9/LY5fBaT56SIgu2JxThgro5mxkB4T79x//S+3AgMBAAECggEBAJgQhx3RMtNqQPAWQYVc4ZU1GrpKqGnvvTkRgnyKUWJ6dT3M56nyypMCrNrHF4HraRQMAUD1+SGjDt2rywajIf3nQUqu5m7xvrd3sNcO1PnS87/rCOHMZKy2MmgGtVfXa60xrdzBSyMrvi9Ud5/Ikn2PxYY5wqpIF99ixmdqrT3khjhzb4BRL6zW0xvKYOuuv7LULNC4261IlrBn6QUpaSKopCVGDiajZYpKYLJAgYyRXlvBEQnKACMRwt6uJ/kH6b6A8mlMV20OWrjHSce2F1MU02ZGSj8IblRK0FJvtVWhdOgOaM8WodtwIx7fgag45xTC4d6H3PnwRHJfG9GinnECgYEA8oT0TWcV6OdWmJOsqV4IdbcWatM1U2uQnZ2A5t+3MBsAZDS+725VgtSrWzAB8saVmDCOanMNq2Gtq5wdS9aVlR/SATQwRmVeGFG2ae7qtS2CpBwwIu2bD3bcR8AEVtR9uCJQyAeBQj+Hy75+wj/KR5mKquMTMXVkovAwn/27B70CgYEAzOGEHcU3+4SLnrVbXHoZfRrk9oiaZBWQELchk16D1LSQjf0fnJOjDGIO5L83qx7f9xzAdyGmYc9YbD9j1mTGnqgsSfhdRDxPcynExbhQOQAQNB7U7KCQlFzs4sbRWpHPrRpNAcxLZiDlyhSLEZLFnMOXUct2UGTeGYjmmmhHwoMCgYAIyeask2rI2PFbcCaWsLCvy2XFk0fgcQp5m8abF0plNOVLvFmbBa2Voy1ejZvUd3veWwweMXMyXcTUbkDlia48DD4pCwIg2vWQ/g0VQ7I/xJlyZw8bhO7UnaMX+o5tsx+nN58j0JnPk8vRB2NCmNs0wwyyaq48YZu3B+tLMP/BJQKBgALHeFxTBYxi4uX3PdMGUPwydjKl7bo31Kl1Yn42RQGIpYFXkqs0EX0kg2E0+tNWauFWQYIcMb6X6nIldfw9h7g1PcyPEuzPCKDeSy4Hbwcm6hFa7bZ8AxoQHKKC4eohmjiV57+Dfu5WuedA2hYV8JpMyOuyH9u9Uon0InSrv3VzAoGAT7iFhk5+t0Tcn4vmFjNKBTSBBdwGet3mHVbjVpit3IMT6dShxhpOxharIGypdWKLdptRwyIfNWoZ8P12DqujOsJ7mKn+uoAnD3Pcor6Ph5ZVbnVhaUU7nnxZ22gZZPbpZyWQQgbDHxq1DTGGpVW59q0CZ13+CE8QFKeRscOHW/E=", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDCF5axKAn+m/82xRDrewOR/LWE9THASE9CDihLm8FnCRAQw091KwzDOsMC3I/+lCHwxNNDLLFpgpSFJ1JEftdpMj4h7JnUn+Rx09PW9JUtii8PZmbpJD0w56eu1kachHrVMuZvYNA6ge+ukMMQpL3Za9udHk7a+CnCITG9N0F3WGk9p7GPpPlSazR/DgJiaqh4W11k+UAYejcs5Bj3QU1ndkGWhfd0bBxepK+otk9qXWSwaaOKxFCVrSBnv+hxTkaenWJy6oAE0GoQNQdsRxS2yHlgZEMLjmpD2hnUn4GfX6BLwn9/LY5fBaT56SIgu2JxThgro5mxkB4T79x//S+3AgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmX3mkJEm2E5qnMS3zfSUGLLvJrTrWP3cB2x47CzV7x1H2", - "privKey": "CAASpwkwggSjAgEAAoIBAQDOd2RrB4TRtSdUarzog++Ma3tnWkq/kCiMGbZ175EnEkVImcCtKuVjoS+2qXDYoMR85yj1Q7tR/Me5iFINqK/ZZRBcmvokwWPLlNUCsE6Ig7YJCmUvuRaRGKYmJ6mJLVd98yhqeU7Pu7A0kXp07orpVmje+KbA7WCeZqsOQXHgTweqirqoBMZf6kV5p/Ya3mngpqmzCPhZTQSI7Fx1f/XkAh24CXeU2lnNlzpXLHVzGT68HQ9v0QCRh+bneB7tBEkwKLE5g2VjALjaDj65mElGkn+8ZyttztzNb8/ujrWkNBtKR7u0wWIgcgfs7fBf7c8i9HFs+9nZraZEdoLPuYGfAgMBAAECggEAMhwkER34DHWtH/3v73bmEuybPNBbR/cTAD3VXPZSAmuayS4X52970Rxz2h9xtgH+7lmkRTK1Kgbx6oO9dnc0hszSlcc/YuBU+jobINXtmZBuA++z80s2wOx8ltIVgaexjm4PpxfeGujwsTGFyQ+EQ3GnbkZnInf6dTdx2Lnli4zy1nom/7MrLxqXhGYf5iRdNMiPwx5dxSUsAWBhRvMMhJAyX+Mq0ft2FLxTk1eotHNXWtTwjPHEsHDjEEZT758uMsxqXYWKM/rrabrBhB1MM2nVww++G0Q6zdiE82kG5XaPR9unYqyjVMFO0srY6DgYXxyNVANS4rDEAjhPzI8qoQKBgQD5Tx0M0cJdtXYmGtqLaVKly9Gpe63lMVjwCOFTudLCi0QnfQ1bB+oEb+SKOegrGD3yRRF77a8iRtkqQc4ujoUTjyBjzJ7XH5+zSjqPrLFa/z5caLV2Agycj36RWCJV2L9XsMg9xk+HXxYiKEA4ft1NkrQqXMgXRSBl/KgdTyk+1wKBgQDUAe1AwBxQnb4VR3iX+rUjsl6JQBvwC60d4/vtSV4nFr4sDJuQo169uE3FI57OMyJwlBM3B+e1YEGPNafPojRv5EP+mGG+XIhmFiY6k4D7l+37p8/ymKAAJvhgrU9mKQniXZx5a29Hf/j8t63yzN2jRzs3gcE/uzdsddTdvyfieQKBgEkcZUWEIf6/H1XPXDWz/lO2sNaF+ZoT3aQOxp16Cg+ZLbRy3L7MVFlWwuuyTZ6NrmTk0lrIeiqQIlFdGOzYSLhSqcn6kL4/fOLkKsZFe4FXBt+sqUJhGXe0MQbIlNEeDgbWRfKvvFTTkrcTnLm0oouEMSeXK+p/ECA4dsiZlVvjAoGAMBIvxZrJ0M2zqAeIpI1IPUvYe655pzg+jKSBHxCftKVHgZ1qOKWSedosaCLng0G88WHh6Xx1YX7t3pb/8eiJk0Vi1XufzhYVJ3CmQmnnuSR95a3rTMqmnOI5N1KUyklL4HPxYualWMT/o+3SF1e0ea1RFAjr1JOSwZkGJzGMzaECgYEAhmHEv1HWQus7MFSiDPCEGekDbAbrqQOVHznLM7oU9PoVT8PPYm4qajoe9+kaGdR1wDgUcOIP2FstUAGrUCil/5EJbg+r3T1sH1TdIVEOalFNmoddYGYK00xdIyuHkZ5ADQP4oahM5XbpWm/2n3VNcTgY7qHoKqU7psVwLQklSqM=", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDOd2RrB4TRtSdUarzog++Ma3tnWkq/kCiMGbZ175EnEkVImcCtKuVjoS+2qXDYoMR85yj1Q7tR/Me5iFINqK/ZZRBcmvokwWPLlNUCsE6Ig7YJCmUvuRaRGKYmJ6mJLVd98yhqeU7Pu7A0kXp07orpVmje+KbA7WCeZqsOQXHgTweqirqoBMZf6kV5p/Ya3mngpqmzCPhZTQSI7Fx1f/XkAh24CXeU2lnNlzpXLHVzGT68HQ9v0QCRh+bneB7tBEkwKLE5g2VjALjaDj65mElGkn+8ZyttztzNb8/ujrWkNBtKR7u0wWIgcgfs7fBf7c8i9HFs+9nZraZEdoLPuYGfAgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmRVSpRP7xqAD6DygdDs41Tou7kC4ZMcbXZusRsBmJPVVH", - "privKey": "CAASqAkwggSkAgEAAoIBAQC1pz29v/97ld9dVd/jUrCM0ZIGABwqG1UvpPXffF1zWlHBHyoheUXi1QD+QDNFV06ajEMP1j0jGeo5bYn2ggZf5duxeT5x5cRO1yWF/j54uprOlbg1/8+TRgRZrKFDmXY2p3ovxwg/dqBFZ+H8EDTWA3Wvq5VFPMzHlm0HCernyxnERxtC6vqC601wIsU2TTEGqHzEPi4yvIgfdu3Hse92j99nBUo6jftjMxv50IMLZM091DPfNlQCBRKwrxmArm2o591B/+Z7xsaNreQKIw5rDBnwT/QXzZuKf8hkcnXYek1PIiY4FngtHuWj0zjNDaNtClI/6kR4pCYgBFoIaoz1AgMBAAECggEBAIVQTMaSPsyTTE8yc9JgYEOoljMjJ4hbcOQ7e1rd6bN7qJ5D4eaZGwoC6uytbzNHhN91as4Xm9zD6xrkYijwef8tMVOJOKPcTXrS+K3izjRKNszAImY27D8YVp79S4jR+mjX9ptTxaDVzX/CYp5bwnsCJP+cvDsJCPy9UBynUad0MQVQnYCS6/gl8xSlxh7oaZ3oZzEMFZWzXmgc+w5agaNL+XEzWhLi5PD+DxZoCMnRUg6+0AiHJcOFnDTyRRi4bymerDJNVScOG7Jx6QLJQ9LwP8iNMa5zWoSPSR2E9m+Z1JioxXtAjIKfz4jxhbMIBibXy7euBOu+WP5k5nkwLu0CgYEA4a30MyabKClXN4jaflef7Q6csqSQFJt7oaxC6DzFT21bxvbJB5gyuwJXUX6MnIne7zl56j43usMDyij4Gsmm2Ukug7fdUnbNfzfXZHVIqCDRI12LKM0gdhpbnnchtKg5Is8A+vdz5a9NOtVFhY3KpZbFvLgf6V1jf9Klhtfl+B8CgYEAzg8LZLzcGcM3UTA+t/4ExW2jPNVi1dn+Wff6aL+X8sQMn+cD3xcQlPs394eBqvqh2/57ifdlHCKnrIGnuscTCM4VTbfhOwIGDd1PagxE3SRcG7yShsVO79GhcWkHFyUmditCVUue5UXJWj2Er4YQZ6EVwicC7SYKO6T8ZS6ZKGsCgYEAkcaI0CWm4Zlaoh+/aw702e6vX2GXRAhvIq6gBV2D4lt0hh/RGRvB4TSQ7K4+67rPC13oF1wbKYNgxkwSf1M0eHSiHCk/SE4/TWbnthdgWGHiVeLNygw+ZKt/9OtlFUn4pjhqnLIM5heHXnJ21t8RQEcU8WNKEbbmV6HclC6PeOcCgYATbYGyfsf1ud0mT3kqWc3TW3Hvk2LdLM95ZhL6+011OxzBmsNXrlIG6eSt9t235CeMmWLGcEfdLjtG3XaV+p0F0IBbsoGO0bMGbZ5GLl/zxbDVgKMEB+hYXhhtm+xqNzt4Gr4HUrjpfvnsAy7Wabp0OtDVXF4/Q73lP7n4RDt2fwKBgEHE+gsQoYBS8CY5UJHmHbft4oQ2YbGEe9DwKwXlB0y65154okg8Qk/fldwG4W/4PujOvO+rqHZ+zw8i25TV9uvYToDeobFj84y+MInWS65Gx7Ky1NQP6gGO4+2CxzWsvF0GSbdwqW4iqtwYgj8StFsxjGCY5Rkrx8pjbpXbImmC", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC1pz29v/97ld9dVd/jUrCM0ZIGABwqG1UvpPXffF1zWlHBHyoheUXi1QD+QDNFV06ajEMP1j0jGeo5bYn2ggZf5duxeT5x5cRO1yWF/j54uprOlbg1/8+TRgRZrKFDmXY2p3ovxwg/dqBFZ+H8EDTWA3Wvq5VFPMzHlm0HCernyxnERxtC6vqC601wIsU2TTEGqHzEPi4yvIgfdu3Hse92j99nBUo6jftjMxv50IMLZM091DPfNlQCBRKwrxmArm2o591B/+Z7xsaNreQKIw5rDBnwT/QXzZuKf8hkcnXYek1PIiY4FngtHuWj0zjNDaNtClI/6kR4pCYgBFoIaoz1AgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - }, - { - "id": { - "id": "QmVG3c9ejpUUoAVg5T3TjcS1tXGWwmG6dmbgkSLxvp3jgt", - "privKey": "CAASpwkwggSjAgEAAoIBAQDN3x153zheqZd1EMVM/ty8j24QeAIdqvntIFMTrhVgqtGV6NiCLTytiXo/qOFa4eN0V0AXpK6xXS46NAFkOej4rDG3b1d9eUKADndja98ywtbMdKjddtuVWvIa/0M5TgBvtv0JSAi3qyOUM2GmBkRn9L4GsBvj440I82gASkNSsxKUa7LCxAz3mK9pCZ5QVWZGRIMjQzNpB3uR12vwXKouBf/Rnx9GIg7G2a7f6GTWWlNr+BLynURMaWnOu6mtVnCmPGAXysLZgodzw1aLdF6im57a/pd9zw0jcI5d/ffSHii51/337+ffdizSwTnAJnCGN4Y7Vpq5l4SJXFerT6V/AgMBAAECggEBAIeRtqJryX4k5fUULykt6ARP22YC8TnCPsTVdX/PMoqu0keKxxCqY3vPvW4wcv5bJGKXlkA7lUJ9HxT67DOpIu6mzjKCorWg5ZbYb+xLu/Z8ceC/rffw7lbjRe1bTVRuNkFa2jSDeCIjE9HjKBmhpOhkNcLHtAYU8eoEB+ew/7ZzwXiCTJUNFeTqb6dzuvhRpipD0ARCGGWUbbt18osWl1IPDNDkQfcCAcDSO1Mb2fC07uS2D3Hj9h0wUFN/lfzg64mIs1Lxh6+ctdYO3ln/n2hpYAT/QgoqrkpPCZy7PrWkTCSTW/Su9/NWqaTV3F9MzJnKkzexyF2442qhA15fpwkCgYEA7KWSUQg1hRoRPXrKpaSmF/iXW1d/+VOLRXWTR9Fpvi/PDQpgLyVMGLL5N+ENjSlcZCbk7hbffjSgoU6mSZqD92vKHN3kbFPxHf8Fk2g2V+GY2850pYlXX44IlOg1aeAX0Zx+SiWzWtCA9CFxwTh7gSpx/HOrFubiF2oO0OzXHuMCgYEA3rU8Ea67DKfBTPzlM9Wc/EpazVvdKC7/wPgjJfKbkTnqk6fjx0q+jLq3LzHTdobf7w7ydWs5U+8W4/c/oXmiGNVXwpEfTDAKYIkyKoMwlp/V2XaRlEVin4+SvlCvxMeo2x3N3kTnul6vGfT0O2tWFjDP8Uz4VnQVJz+Iqq1AJbUCgYAifqwKVcj/YuJadNivNoXjfqAJd4K3BD+L22yhjlv8lhl3TCjjFmu2Ofhr9ck052+JRcYfEoR3cBJuEPnaRsSvvy2R8aJHTCEcfzz/1LP/MWpHuBt2ucNbsWd81TBcA4dVTZt3EXHIbhYt/+YGBUazeE1vQCkTSIpyYUpRmARvgwKBgAO87wEs+Z7AwhHUvNQd5cCmTtfbjt65yzkl8REV/V52pmVMEBqsOn6KM8DrCS2YHfIZQiCOaCvse2ngIIVJUVsxWYO+g9P3inUMWHc2NH6SuDgqMU9Xysv60O+40vpuj3r+CRKN/YW3SSEaZ28H4i4FK7hVHmX1FNXPzy9uMQFxAoGAcRL/1MJiXG9XPdUUuRnlHgMgObeKyYjvy+1JyRgYhIMudpk1HWvo/+v7SIZwDm0NVA3fEgWhB1BNUxwbijrxw5TGH29fnXa+NFATFcmvyfwSVvbEB8Ml5E4X1S6r2EE5lHYIoa1fTV03LMJVuSrN12kqOyV4Bj6XTaJ0ZQ/+vi0=", - "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDN3x153zheqZd1EMVM/ty8j24QeAIdqvntIFMTrhVgqtGV6NiCLTytiXo/qOFa4eN0V0AXpK6xXS46NAFkOej4rDG3b1d9eUKADndja98ywtbMdKjddtuVWvIa/0M5TgBvtv0JSAi3qyOUM2GmBkRn9L4GsBvj440I82gASkNSsxKUa7LCxAz3mK9pCZ5QVWZGRIMjQzNpB3uR12vwXKouBf/Rnx9GIg7G2a7f6GTWWlNr+BLynURMaWnOu6mtVnCmPGAXysLZgodzw1aLdF6im57a/pd9zw0jcI5d/ffSHii51/337+ffdizSwTnAJnCGN4Y7Vpq5l4SJXFerT6V/AgMBAAE=" - }, - "multiaddrs": [], - "multiaddr": {} - } - ] -} \ No newline at end of file diff --git a/test/switch/transport-manager.spec.js b/test/switch/transport-manager.spec.js deleted file mode 100644 index f3389c95..00000000 --- a/test/switch/transport-manager.spec.js +++ /dev/null @@ -1,295 +0,0 @@ -/* eslint-env mocha */ -'use strict' - -const chai = require('chai') -const dirtyChai = require('dirty-chai') -const expect = chai.expect -chai.use(dirtyChai) -const Multiaddr = require('multiaddr') -const PeerInfo = require('peer-info') -const sinon = require('sinon') - -const TransportManager = require('../../src/switch/transport') - -describe('Transport Manager', () => { - afterEach(() => { - sinon.restore() - }) - - describe('dialables', () => { - let peerInfo - const dialAllTransport = { filter: addrs => addrs } - - before(function (done) { - this.timeout(10e3) - PeerInfo.create((err, info) => { - if (err) return done(err) - peerInfo = info - done() - }) - }) - - afterEach(() => { - peerInfo.multiaddrs.clear() - }) - - it('should return all transport addresses when peer info has 0 addrs', () => { - const queryAddrs = [ - '/ip4/127.0.0.1/tcp/4002', - '/ip4/192.168.0.3/tcp/4002', - '/ip6/::1/tcp/4001' - ].map(a => Multiaddr(a)) - - const dialableAddrs = TransportManager.dialables(dialAllTransport, queryAddrs, peerInfo) - - expect(dialableAddrs).to.have.length(queryAddrs.length) - - queryAddrs.forEach(qa => { - expect(dialableAddrs.some(da => da.equals(qa))).to.be.true() - }) - }) - - it('should return all transport addresses when we pass no peer info', () => { - const queryAddrs = [ - '/ip4/127.0.0.1/tcp/4002', - '/ip4/192.168.0.3/tcp/4002', - '/ip6/::1/tcp/4001' - ].map(a => Multiaddr(a)) - - const dialableAddrs = TransportManager.dialables(dialAllTransport, queryAddrs) - - expect(dialableAddrs).to.have.length(queryAddrs.length) - - queryAddrs.forEach(qa => { - expect(dialableAddrs.some(da => da.equals(qa))).to.be.true() - }) - }) - - it('should filter our addresses', () => { - const queryAddrs = [ - '/ip4/127.0.0.1/tcp/4002', - '/ip4/192.168.0.3/tcp/4002', - '/ip6/::1/tcp/4001' - ].map(a => Multiaddr(a)) - - const ourAddrs = [ - '/ip4/127.0.0.1/tcp/4002', - '/ip4/192.168.0.3/tcp/4002' - ] - - ourAddrs.forEach(a => peerInfo.multiaddrs.add(a)) - - const dialableAddrs = TransportManager.dialables(dialAllTransport, queryAddrs, peerInfo) - - expect(dialableAddrs).to.have.length(1) - expect(dialableAddrs[0].toString()).to.equal('/ip6/::1/tcp/4001') - }) - - it('should filter our addresses with peer ID suffix', () => { - const queryAddrs = [ - '/ip4/127.0.0.1/tcp/4002/ipfs/QmebzNV1kSzLfaYpSZdShuiABNUxoKT1vJmCdxM2iWsM2j', - '/ip4/192.168.0.3/tcp/4002', - '/ip6/::1/tcp/4001' - ].map(a => Multiaddr(a)) - - const ourAddrs = [ - '/ip4/127.0.0.1/tcp/4002', - `/ip4/192.168.0.3/tcp/4002/ipfs/${peerInfo.id.toB58String()}` - ] - - ourAddrs.forEach(a => peerInfo.multiaddrs.add(a)) - - const dialableAddrs = TransportManager.dialables(dialAllTransport, queryAddrs, peerInfo) - - expect(dialableAddrs).to.have.length(1) - expect(dialableAddrs[0].toString()).to.equal('/ip6/::1/tcp/4001') - }) - - it('should filter out our addrs that start with /ipfs/', () => { - const queryAddrs = [ - '/ip4/127.0.0.1/tcp/4002/ipfs/QmebzNV1kSzLfaYpSZdShuiABNUxoKT1vJmCdxM2iWsM2j' - ].map(a => Multiaddr(a)) - - const ourAddrs = [ - '/ipfs/QmSoLnSGccFuZQJzRadHn95W2CrSFmZuTdDWP8HXaHca9z' - ] - - ourAddrs.forEach(a => peerInfo.multiaddrs.add(a)) - - const dialableAddrs = TransportManager.dialables(dialAllTransport, queryAddrs, peerInfo) - - expect(dialableAddrs).to.have.length(1) - expect(dialableAddrs[0]).to.eql(queryAddrs[0]) - }) - - it('should filter our addresses over relay/rendezvous', () => { - const peerId = peerInfo.id.toB58String() - const queryAddrs = [ - `/p2p-circuit/ipfs/${peerId}`, - '/p2p-circuit/ip4/127.0.0.1/tcp/4002', - '/p2p-circuit/ip4/192.168.0.3/tcp/4002', - `/p2p-circuit/ip4/127.0.0.1/tcp/4002/ipfs/${peerId}`, - `/p2p-circuit/ip4/192.168.0.3/tcp/4002/ipfs/${peerId}`, - '/p2p-circuit/ip4/127.0.0.1/tcp/4002/ipfs/QmebzNV1kSzLfaYpSZdShuiABNUxoKT1vJmCdxM2iWsM2j', - '/p2p-circuit/ip4/192.168.0.3/tcp/4002/ipfs/QmebzNV1kSzLfaYpSZdShuiABNUxoKT1vJmCdxM2iWsM2j', - `/p2p-webrtc-star/ipfs/${peerId}`, - `/p2p-websocket-star/ipfs/${peerId}`, - `/p2p-stardust/ipfs/${peerId}`, - '/ip6/::1/tcp/4001' - ].map(a => Multiaddr(a)) - - const ourAddrs = [ - '/ip4/127.0.0.1/tcp/4002', - `/ip4/192.168.0.3/tcp/4002/ipfs/${peerInfo.id.toB58String()}` - ] - - ourAddrs.forEach(a => peerInfo.multiaddrs.add(a)) - - const dialableAddrs = TransportManager.dialables(dialAllTransport, queryAddrs, peerInfo) - - expect(dialableAddrs).to.have.length(1) - expect(dialableAddrs[0].toString()).to.equal('/ip6/::1/tcp/4001') - }) - }) - - describe('listen', () => { - const listener = { - once: function () {}, - listen: function () {}, - removeListener: function () {}, - getAddrs: function () {} - } - - it('should allow for multiple addresses with port 0', (done) => { - const mockListener = sinon.stub(listener) - mockListener.listen.callsArg(1) - mockListener.getAddrs.callsArgWith(0, null, []) - const mockSwitch = { - _peerInfo: { - multiaddrs: { - toArray: () => [ - Multiaddr('/ip4/127.0.0.1/tcp/0'), - Multiaddr('/ip4/0.0.0.0/tcp/0') - ], - replace: () => {} - } - }, - _options: {}, - _connectionHandler: () => {}, - transports: { - TCP: { - filter: (addrs) => addrs, - createListener: () => { - return mockListener - } - } - } - } - const transportManager = new TransportManager(mockSwitch) - transportManager.listen('TCP', null, null, (err) => { - expect(err).to.not.exist() - expect(mockListener.listen.callCount).to.eql(2) - done() - }) - }) - - it('should filter out equal addresses', (done) => { - const mockListener = sinon.stub(listener) - mockListener.listen.callsArg(1) - mockListener.getAddrs.callsArgWith(0, null, []) - const mockSwitch = { - _peerInfo: { - multiaddrs: { - toArray: () => [ - Multiaddr('/ip4/127.0.0.1/tcp/0'), - Multiaddr('/ip4/127.0.0.1/tcp/0') - ], - replace: () => {} - } - }, - _options: {}, - _connectionHandler: () => {}, - transports: { - TCP: { - filter: (addrs) => addrs, - createListener: () => { - return mockListener - } - } - } - } - const transportManager = new TransportManager(mockSwitch) - transportManager.listen('TCP', null, null, (err) => { - expect(err).to.not.exist() - expect(mockListener.listen.callCount).to.eql(1) - done() - }) - }) - - it('should account for addresses with no port', (done) => { - const mockListener = sinon.stub(listener) - mockListener.listen.callsArg(1) - mockListener.getAddrs.callsArgWith(0, null, []) - const mockSwitch = { - _peerInfo: { - multiaddrs: { - toArray: () => [ - Multiaddr('/p2p-circuit'), - Multiaddr('/p2p-websocket-star') - ], - replace: () => {} - } - }, - _options: {}, - _connectionHandler: () => {}, - transports: { - TCP: { - filter: (addrs) => addrs, - createListener: () => { - return mockListener - } - } - } - } - const transportManager = new TransportManager(mockSwitch) - transportManager.listen('TCP', null, null, (err) => { - expect(err).to.not.exist() - expect(mockListener.listen.callCount).to.eql(2) - done() - }) - }) - - it('should filter out addresses with the same, non 0, port', (done) => { - const mockListener = sinon.stub(listener) - mockListener.listen.callsArg(1) - mockListener.getAddrs.callsArgWith(0, null, []) - const mockSwitch = { - _peerInfo: { - multiaddrs: { - toArray: () => [ - Multiaddr('/ip4/127.0.0.1/tcp/8000'), - Multiaddr('/dnsaddr/libp2p.io/tcp/8000') - ], - replace: () => {} - } - }, - _options: {}, - _connectionHandler: () => {}, - transports: { - TCP: { - filter: (addrs) => addrs, - createListener: () => { - return mockListener - } - } - } - } - const transportManager = new TransportManager(mockSwitch) - transportManager.listen('TCP', null, null, (err) => { - expect(err).to.not.exist() - expect(mockListener.listen.callCount).to.eql(1) - done() - }) - }) - }) -}) diff --git a/test/switch/transports.browser.js b/test/switch/transports.browser.js deleted file mode 100644 index 735921e7..00000000 --- a/test/switch/transports.browser.js +++ /dev/null @@ -1,52 +0,0 @@ -/* eslint-env mocha */ -'use strict' - -const chai = require('chai') -const dirtyChai = require('dirty-chai') -const expect = chai.expect -chai.use(dirtyChai) - -const PeerId = require('peer-id') -const PeerInfo = require('peer-info') -const PeerBook = require('peer-book') -const WebSockets = require('libp2p-websockets') - -const tryEcho = require('./utils').tryEcho -const Switch = require('../../src/switch') - -describe('Transports', () => { - describe('WebSockets', () => { - let sw - let peer - - before((done) => { - const b58IdSrc = 'QmYzgdesgjdvD3okTPGZT9NPmh1BuH5FfTVNKjsvaAprhb' - // use a pre generated Id to save time - const idSrc = PeerId.createFromB58String(b58IdSrc) - const peerSrc = new PeerInfo(idSrc) - sw = new Switch(peerSrc, new PeerBook()) - - PeerInfo.create((err, p) => { - expect(err).to.not.exist() - peer = p - done() - }) - }) - - it('.transport.add', () => { - sw.transport.add('ws', new WebSockets()) - expect(Object.keys(sw.transports).length).to.equal(1) - }) - - it('.transport.dial', (done) => { - peer.multiaddrs.clear() - peer.multiaddrs.add('/ip4/127.0.0.1/tcp/15337/ws') - - const conn = sw.transport.dial('ws', peer, (err, conn) => { - expect(err).to.not.exist() - }) - - tryEcho(conn, done) - }) - }) -}) diff --git a/test/switch/transports.node.js b/test/switch/transports.node.js deleted file mode 100644 index f9e464c0..00000000 --- a/test/switch/transports.node.js +++ /dev/null @@ -1,237 +0,0 @@ -/* eslint-env mocha */ -/* eslint no-warning-comments: off */ -'use strict' - -const chai = require('chai') -const dirtyChai = require('dirty-chai') -const expect = chai.expect -chai.use(dirtyChai) -const parallel = require('async/parallel') -const TCP = require('libp2p-tcp') -const WS = require('libp2p-websockets') -const pull = require('pull-stream') -const PeerBook = require('peer-book') - -const utils = require('./utils') -const createInfos = utils.createInfos -const tryEcho = utils.tryEcho -const Switch = require('../../src/switch') - -describe('transports', () => { - [ - { n: 'TCP', C: TCP, maGen: (port) => { return `/ip4/127.0.0.1/tcp/${port}` } }, - { n: 'WS', C: WS, maGen: (port) => { return `/ip4/127.0.0.1/tcp/${port}/ws` } } - // { n: 'UTP', C: UTP, maGen: (port) => { return `/ip4/127.0.0.1/udp/${port}/utp` } } - ].forEach((t) => describe(t.n, () => { - let switchA - let switchB - let morePeerInfo - - before(function (done) { - this.timeout(10 * 1000) - - createInfos(9, (err, peerInfos) => { - expect(err).to.not.exist() - - const peerA = peerInfos[0] - const peerB = peerInfos[1] - morePeerInfo = peerInfos.slice(2) - - peerA.multiaddrs.add(t.maGen(9888)) - peerB.multiaddrs.add(t.maGen(9999)) - switchA = new Switch(peerA, new PeerBook()) - switchB = new Switch(peerB, new PeerBook()) - done() - }) - }) - - after(function (done) { - parallel([ - (next) => switchA.stop(next), - (next) => switchB.stop(next) - ], done) - }) - - it('.transport.remove', () => { - switchA.transport.add('test', new t.C()) - expect(switchA.transports).to.have.any.keys(['test']) - switchA.transport.remove('test') - expect(switchA.transports).to.not.have.any.keys(['test']) - // verify remove fails silently - switchA.transport.remove('test') - }) - - it('.transport.removeAll', (done) => { - switchA.transport.add('test', new t.C()) - switchA.transport.add('test2', new t.C()) - expect(switchA.transports).to.have.any.keys(['test', 'test2']) - switchA.transport.removeAll(() => { - expect(switchA.transports).to.not.have.any.keys(['test', 'test2']) - done() - }) - }) - - it('.transport.add', () => { - switchA.transport.add(t.n, new t.C()) - expect(Object.keys(switchA.transports).length).to.equal(1) - - switchB.transport.add(t.n, new t.C()) - expect(Object.keys(switchB.transports).length).to.equal(1) - }) - - it('.transport.listen', (done) => { - let count = 0 - - switchA.transport.listen(t.n, {}, (conn) => pull(conn, conn), ready) - switchB.transport.listen(t.n, {}, (conn) => pull(conn, conn), ready) - - function ready () { - if (++count === 2) { - expect(switchA._peerInfo.multiaddrs.size).to.equal(1) - expect(switchB._peerInfo.multiaddrs.size).to.equal(1) - done() - } - } - }) - - it('.transport.dial to a multiaddr', (done) => { - const peer = morePeerInfo[0] - peer.multiaddrs.add(t.maGen(9999)) - - switchA.transport.dial(t.n, peer, (err, conn) => { - expect(err).to.not.exist() - tryEcho(conn, done) - }) - }) - - it('.transport.dial to set of multiaddr, only one is available', (done) => { - const peer = morePeerInfo[1] - peer.multiaddrs.add(t.maGen(9359)) - peer.multiaddrs.add(t.maGen(9329)) - peer.multiaddrs.add(t.maGen(9910)) - peer.multiaddrs.add(switchB._peerInfo.multiaddrs.toArray()[0]) // the valid address - peer.multiaddrs.add(t.maGen(9309)) - // addr not supported added on purpose - peer.multiaddrs.add('/ip4/1.2.3.4/tcp/3456/ws/p2p-webrtc-star') - - switchA.transport.dial(t.n, peer, (err, conn) => { - expect(err).to.not.exist() - tryEcho(conn, done) - }) - }) - - it('.transport.dial to set of multiaddr, none is available', (done) => { - const peer = morePeerInfo[2] - peer.multiaddrs.add(t.maGen(9359)) - peer.multiaddrs.add(t.maGen(9329)) - // addr not supported added on purpose - peer.multiaddrs.add('/ip4/1.2.3.4/tcp/3456/ws/p2p-webrtc-star') - - switchA.transport.dial(t.n, peer, (err, conn) => { - expect(err).to.exist() - expect(conn).to.not.exist() - done() - }) - }) - - it('.close', function (done) { - this.timeout(2500) - - parallel([ - (cb) => switchA.transport.close(t.n, cb), - (cb) => switchB.transport.close(t.n, cb) - ], done) - }) - - it('support port 0', (done) => { - const ma = t.maGen(0) - const peer = morePeerInfo[3] - peer.multiaddrs.add(ma) - - const sw = new Switch(peer, new PeerBook()) - sw.transport.add(t.n, new t.C()) - sw.transport.listen(t.n, {}, (conn) => pull(conn, conn), ready) - - function ready () { - expect(peer.multiaddrs.size).to.equal(1) - // should not have /tcp/0 anymore - expect(peer.multiaddrs.has(ma)).to.equal(false) - sw.stop(done) - } - }) - - it('support addr 0.0.0.0', (done) => { - const ma = t.maGen(9050).replace('127.0.0.1', '0.0.0.0') - const peer = morePeerInfo[4] - peer.multiaddrs.add(ma) - - const sw = new Switch(peer, new PeerBook()) - sw.transport.add(t.n, new t.C()) - sw.transport.listen(t.n, {}, (conn) => pull(conn, conn), ready) - - function ready () { - expect(peer.multiaddrs.size >= 1).to.equal(true) - expect(peer.multiaddrs.has(ma)).to.equal(false) - sw.stop(done) - } - }) - - it('support addr 0.0.0.0:0', (done) => { - const ma = t.maGen(9050).replace('127.0.0.1', '0.0.0.0') - const peer = morePeerInfo[5] - peer.multiaddrs.add(ma) - - const sw = new Switch(peer, new PeerBook()) - sw.transport.add(t.n, new t.C()) - sw.transport.listen(t.n, {}, (conn) => pull(conn, conn), ready) - - function ready () { - expect(peer.multiaddrs.size >= 1).to.equal(true) - expect(peer.multiaddrs.has(ma)).to.equal(false) - sw.stop(done) - } - }) - - it('listen in several addrs', function (done) { - this.timeout(12000) - const peer = morePeerInfo[6] - - peer.multiaddrs.add(t.maGen(9001)) - peer.multiaddrs.add(t.maGen(9002)) - peer.multiaddrs.add(t.maGen(9003)) - - const sw = new Switch(peer, new PeerBook()) - sw.transport.add(t.n, new t.C()) - sw.transport.listen(t.n, {}, (conn) => pull(conn, conn), ready) - - function ready () { - expect(peer.multiaddrs.size).to.equal(3) - sw.stop(done) - } - }) - - it('handles EADDRINUSE error when trying to listen', (done) => { - // TODO: fix libp2p-websockets to not throw Uncaught Error in this test - if (t.n === 'WS') { return done() } - - const switch1 = new Switch(switchA._peerInfo, new PeerBook()) - let switch2 - - switch1.transport.add(t.n, new t.C()) - switch1.transport.listen(t.n, {}, (conn) => pull(conn, conn), () => { - // Add in-use (peerA) address to peerB - switchB._peerInfo.multiaddrs.add(t.maGen(9888)) - - switch2 = new Switch(switchB._peerInfo, new PeerBook()) - switch2.transport.add(t.n, new t.C()) - switch2.transport.listen(t.n, {}, (conn) => pull(conn, conn), ready) - }) - - function ready (err) { - expect(err).to.exist() - expect(err.code).to.equal('EADDRINUSE') - switch1.stop(() => switch2.stop(done)) - } - }) - })) -}) diff --git a/test/switch/utils.js b/test/switch/utils.js deleted file mode 100644 index 0348bcb5..00000000 --- a/test/switch/utils.js +++ /dev/null @@ -1,76 +0,0 @@ -'use strict' - -const PeerInfo = require('peer-info') -const PeerId = require('peer-id') -const parallel = require('async/parallel') -const pull = require('pull-stream') -const chai = require('chai') -const dirtyChai = require('dirty-chai') -const expect = chai.expect -chai.use(dirtyChai) - -const fixtures = require('./test-data/ids.json').infos - -exports.createInfos = (num, callback) => { - const tasks = [] - - for (let i = 0; i < num; i++) { - tasks.push((cb) => { - if (fixtures[i]) { - PeerId.createFromJSON(fixtures[i].id, (err, id) => { - if (err) { - return cb(err) - } - - cb(null, new PeerInfo(id)) - }) - return - } - - PeerInfo.create(cb) - }) - } - - parallel(tasks, callback) -} - -exports.tryEcho = (conn, callback) => { - const values = [Buffer.from('echo')] - - pull( - pull.values(values), - conn, - pull.collect((err, _values) => { - expect(err).to.not.exist() - expect(_values).to.eql(values) - callback() - }) - ) -} - -/** - * A utility method for calling done multiple times to help with async - * testing - * - * @param {Number} n The number of times done will be called - * @param {Function} willFinish An optional callback for cleanup before done is called - * @param {Function} done - * @returns {void} - */ -exports.doneAfter = (n, willFinish, done) => { - if (!done) { - done = willFinish - willFinish = undefined - } - - let count = 0 - const errors = [] - return (err) => { - count++ - if (err) errors.push(err) - if (count >= n) { - if (willFinish) willFinish() - done(errors.length > 0 ? errors : null) - } - } -} diff --git a/test/transports.browser.js b/test/transports.browser.js deleted file mode 100644 index 88614078..00000000 --- a/test/transports.browser.js +++ /dev/null @@ -1,450 +0,0 @@ -/* eslint max-nested-callbacks: ["error", 8] */ -/* eslint-env mocha */ -'use strict' - -const chai = require('chai') -chai.use(require('dirty-chai')) -chai.use(require('chai-checkmark')) -const expect = chai.expect -const PeerInfo = require('peer-info') -const PeerId = require('peer-id') -const Mplex = require('pull-mplex') -const pull = require('pull-stream') -const parallel = require('async/parallel') -const goodbye = require('pull-goodbye') -const serializer = require('pull-serializer') -const wrtcSupport = self.RTCPeerConnection && ('createDataChannel' in self.RTCPeerConnection.prototype) -const tryEcho = require('./utils/try-echo') - -const Node = require('./utils/bundle-browser') -const { getPeerRelay } = require('./utils/constants') - -describe('transports', () => { - describe('websockets', () => { - let peerB - let peerBMultiaddr - let nodeA - - before(async () => { - const peerInfo = await getPeerRelay() - peerB = new PeerInfo(peerInfo.id) - peerBMultiaddr = `/ip4/127.0.0.1/tcp/9200/ws/p2p/${peerInfo.id.toB58String()}` - peerB.multiaddrs.add(peerBMultiaddr) - }) - - after((done) => nodeA.stop(done)) - - it('create a libp2p Node', (done) => { - PeerInfo.create((err, peerInfo) => { - expect(err).to.not.exist() - - nodeA = new Node({ - peerInfo: peerInfo - }) - done() - }) - }) - - it('create a libp2p Node with mplex only', (done) => { - PeerInfo.create((err, peerInfo) => { - expect(err).to.not.exist() - - const b = new Node({ - peerInfo: peerInfo, - modules: { - streamMuxer: [Mplex] - } - }) - expect(b._modules.streamMuxer).to.eql([require('pull-mplex')]) - done() - }) - }) - - it('start libp2pNode', (done) => { - nodeA.start(done) - }) - - // General connectivity tests - - it('.dial using Multiaddr', (done) => { - nodeA.dial(peerBMultiaddr, (err) => { - expect(err).to.not.exist() - - setTimeout(check, 500) // Some time for Identify to finish - - function check () { - const peers = nodeA.peerBook.getAll() - expect(Object.keys(peers)).to.have.length(1) - done() - } - }) - }) - - it('.dialProtocol using Multiaddr', (done) => { - nodeA.dialProtocol(peerBMultiaddr, '/echo/1.0.0', (err, conn) => { - expect(err).to.not.exist() - - const peers = nodeA.peerBook.getAll() - expect(Object.keys(peers)).to.have.length(1) - - tryEcho(conn, done) - }) - }) - - it('.hangUp using Multiaddr', (done) => { - nodeA.hangUp(peerBMultiaddr, (err) => { - expect(err).to.not.exist() - - setTimeout(check, 500) - - function check () { - const peers = nodeA.peerBook.getAll() - expect(Object.keys(peers)).to.have.length(1) - expect(nodeA._switch.connection.getAll()).to.have.length(0) - done() - } - }) - }) - - it('.dial using PeerInfo', (done) => { - nodeA.dial(peerB, (err) => { - expect(err).to.not.exist() - - setTimeout(check, 500) // Some time for Identify to finish - - function check () { - const peers = nodeA.peerBook.getAll() - expect(Object.keys(peers)).to.have.length(1) - done() - } - }) - }) - - it('.dialProtocol using PeerInfo', (done) => { - nodeA.dialProtocol(peerB, '/echo/1.0.0', (err, conn) => { - expect(err).to.not.exist() - const peers = nodeA.peerBook.getAll() - expect(err).to.not.exist() - expect(Object.keys(peers)).to.have.length(1) - - tryEcho(conn, done) - }) - }) - - it('.hangUp using PeerInfo', (done) => { - nodeA.hangUp(peerB, (err) => { - expect(err).to.not.exist() - setTimeout(check, 500) - - function check () { - const peers = nodeA.peerBook.getAll() - expect(err).to.not.exist() - expect(Object.keys(peers)).to.have.length(1) - expect(nodeA._switch.connection.getAll()).to.have.length(0) - done() - } - }) - }) - - it('.dialFSM check conn and close', (done) => { - nodeA.dialFSM(peerB, (err, connFSM) => { - expect(err).to.not.exist() - - connFSM.once('muxed', () => { - expect( - nodeA._switch.connection.getAllById(peerB.id.toB58String()) - ).to.have.length(1) - - connFSM.once('error', done) - connFSM.once('close', () => { - // ensure the connection is closed - expect( - nodeA._switch.connection.getAllById(peerB.id.toB58String()) - ).to.have.length(0) - - done() - }) - - connFSM.close() - }) - }) - }) - - it('.dialFSM with a protocol, do an echo and close', (done) => { - nodeA.dialFSM(peerB, '/echo/1.0.0', (err, connFSM) => { - expect(err).to.not.exist() - connFSM.once('connection', (conn) => { - tryEcho(conn, () => { - connFSM.close() - }) - }) - connFSM.once('error', done) - connFSM.once('close', done) - }) - }) - - describe('stress', () => { - it('one big write', (done) => { - nodeA.dialProtocol(peerB, '/echo/1.0.0', (err, conn) => { - expect(err).to.not.exist() - const rawMessage = Buffer.alloc(100000) - rawMessage.fill('a') - - const s = serializer(goodbye({ - source: pull.values([rawMessage]), - sink: pull.collect((err, results) => { - expect(err).to.not.exist() - expect(results).to.have.length(1) - expect(Buffer.from(results[0])).to.have.length(rawMessage.length) - done() - }) - })) - pull(s, conn, s) - }) - }) - - it('many writes', function (done) { - this.timeout(10000) - - nodeA.dialProtocol(peerB, '/echo/1.0.0', (err, conn) => { - expect(err).to.not.exist() - - const s = serializer(goodbye({ - source: pull( - pull.infinite(), - pull.take(1000), - pull.map((val) => Buffer.from(val.toString())) - ), - sink: pull.collect((err, result) => { - expect(err).to.not.exist() - expect(result).to.have.length(1000) - done() - }) - })) - - pull(s, conn, s) - }) - }) - }) - }) - - describe('webrtc-star', () => { - /* eslint-disable-next-line no-console */ - if (!wrtcSupport) { return console.log('NO WEBRTC SUPPORT') } - - let peer1 - let peer2 - let node1 - let node2 - let node3 - - after((done) => { - parallel([ - (cb) => node1.stop(cb), - (cb) => node2.stop(cb), - (cb) => node3.stop(cb) - ], done) - }) - - it('create two peerInfo with webrtc-star addrs', (done) => { - parallel([ - (cb) => PeerId.create({ bits: 512 }, cb), - (cb) => PeerId.create({ bits: 512 }, cb) - ], (err, ids) => { - expect(err).to.not.exist() - - peer1 = new PeerInfo(ids[0]) - const ma1 = '/ip4/127.0.0.1/tcp/15555/ws/p2p-webrtc-star/p2p/' + ids[0].toB58String() - peer1.multiaddrs.add(ma1) - - peer2 = new PeerInfo(ids[1]) - const ma2 = '/ip4/127.0.0.1/tcp/15555/ws/p2p-webrtc-star/p2p/' + ids[1].toB58String() - peer2.multiaddrs.add(ma2) - - done() - }) - }) - - it('create two libp2p nodes with those peers', (done) => { - node1 = new Node({ - peerInfo: peer1 - }) - node2 = new Node({ - peerInfo: peer2 - }) - done() - }) - - it('start two libp2p nodes', (done) => { - parallel([ - (cb) => node1.start(cb), - (cb) => node2.start(cb) - ], done) - }) - - it('.handle echo on first node', () => { - node2.handle('/echo/1.0.0', (protocol, conn) => pull(conn, conn)) - }) - - it('.dialProtocol from the second node to the first node', (done) => { - node1.dialProtocol(peer2, '/echo/1.0.0', (err, conn) => { - expect(err).to.not.exist() - setTimeout(check, 500) - - function check () { - const peers1 = node1.peerBook.getAll() - expect(Object.keys(peers1)).to.have.length(1) - - const peers2 = node2.peerBook.getAll() - expect(Object.keys(peers2)).to.have.length(1) - - tryEcho(conn, done) - } - }) - }) - - it('node1 hangUp node2', (done) => { - node1.hangUp(peer2, (err) => { - expect(err).to.not.exist() - setTimeout(check, 500) - - function check () { - const peers = node1.peerBook.getAll() - expect(Object.keys(peers)).to.have.length(1) - expect(node1._switch.connection.getAll()).to.have.length(0) - done() - } - }) - }) - - it('create a third node and check that discovery works', (done) => { - PeerId.create({ bits: 512 }, (err, id3) => { - expect(err).to.not.exist() - - const b58Id = id3.toB58String() - - function check () { - // Verify both nodes are connected to node 3 - if (node1._switch.connection.getAllById(b58Id) && node2._switch.connection.getAllById(b58Id)) { - done() - } - } - - const peer3 = new PeerInfo(id3) - const ma3 = '/ip4/127.0.0.1/tcp/15555/ws/p2p-webrtc-star/p2p/' + b58Id - peer3.multiaddrs.add(ma3) - - node1.on('peer:discovery', (peerInfo) => node1.dial(peerInfo, check)) - node2.on('peer:discovery', (peerInfo) => node2.dial(peerInfo, check)) - - node3 = new Node({ - peerInfo: peer3 - }) - node3.start(check) - }) - }) - }) - - describe('websocket-star', () => { - let peer1 - let peer2 - let node1 - let node2 - - it('create two peerInfo with websocket-star addrs', (done) => { - parallel([ - (cb) => PeerId.create({ bits: 512 }, cb), - (cb) => PeerId.create({ bits: 512 }, cb) - ], (err, ids) => { - expect(err).to.not.exist() - - peer1 = new PeerInfo(ids[0]) - const ma1 = '/ip4/127.0.0.1/tcp/14444/ws/p2p-websocket-star/' - peer1.multiaddrs.add(ma1) - - peer2 = new PeerInfo(ids[1]) - const ma2 = '/ip4/127.0.0.1/tcp/14444/ws/p2p-websocket-star/' - peer2.multiaddrs.add(ma2) - - done() - }) - }) - - it('create two libp2p nodes with those peers', (done) => { - node1 = new Node({ - peerInfo: peer1 - }) - node2 = new Node({ - peerInfo: peer2 - }) - done() - }) - - it('listen on the two libp2p nodes', (done) => { - parallel([ - (cb) => node1.start(cb), - (cb) => node2.start(cb) - ], done) - }) - - it('handle a protocol on the first node', () => { - node2.handle('/echo/1.0.0', (protocol, conn) => pull(conn, conn)) - }) - - it('.dialProtocol from the second node to the first node', (done) => { - node1.dialProtocol(peer2, '/echo/1.0.0', (err, conn) => { - expect(err).to.not.exist() - setTimeout(check, 500) - - function check () { - const peers1 = node1.peerBook.getAll() - expect(Object.keys(peers1)).to.have.length(1) - - const peers2 = node2.peerBook.getAll() - expect(Object.keys(peers2)).to.have.length(1) - - tryEcho(conn, done) - } - }) - }) - - it('node1 hangUp node2', (done) => { - node1.hangUp(peer2, (err) => { - expect(err).to.not.exist() - const peers = node1.peerBook.getAll() - expect(Object.keys(peers)).to.have.length(1) - expect(node1._switch.connection.getAll()).to.have.length(0) - done() - }) - }) - - it('create a third node and check that discovery works', function (done) { - this.timeout(10 * 1000) - const expectedPeers = [ - node1.peerInfo.id.toB58String(), - node2.peerInfo.id.toB58String() - ] - - PeerId.create((err, id3) => { - expect(err).to.not.exist() - - const peer3 = new PeerInfo(id3) - const ma3 = '/ip4/127.0.0.1/tcp/14444/ws/p2p-websocket-star/p2p/' + id3.toB58String() - peer3.multiaddrs.add(ma3) - - // 2 connects and 1 start - expect(3).checks(done) - - const node3 = new Node({ - peerInfo: peer3 - }) - node3.on('peer:connect', (peerInfo) => { - expect(expectedPeers).to.include(peerInfo.id.toB58String()).mark() - }) - node3.start((err) => { - expect(err).to.not.exist().mark() - }) - }) - }) - }) -}) diff --git a/test/transports.node.js b/test/transports.node.js deleted file mode 100644 index 3b4faf96..00000000 --- a/test/transports.node.js +++ /dev/null @@ -1,723 +0,0 @@ -/* eslint-env mocha */ -'use strict' - -const chai = require('chai') -chai.use(require('dirty-chai')) -const expect = chai.expect -const parallel = require('async/parallel') -const series = require('async/series') -const rendezvous = require('libp2p-websocket-star-rendezvous') -const TCP = require('libp2p-tcp') -const WS = require('libp2p-websockets') -const WSStar = require('libp2p-websocket-star') -const WRTCStar = require('libp2p-webrtc-star') -const wrtc = require('wrtc') - -const createNode = require('./utils/create-node.js') -const tryEcho = require('./utils/try-echo') -const echo = require('./utils/echo') - -const { - WRTC_RENDEZVOUS_MULTIADDR -} = require('./utils/constants') - -describe('transports', () => { - describe('TCP only', () => { - let nodeA - let nodeB - - before((done) => { - parallel([ - (cb) => createNode('/ip4/0.0.0.0/tcp/0', (err, node) => { - expect(err).to.not.exist() - nodeA = node - node.handle('/echo/1.0.0', echo) - node.start(cb) - }), - (cb) => createNode('/ip4/0.0.0.0/tcp/0', (err, node) => { - expect(err).to.not.exist() - nodeB = node - node.handle('/echo/1.0.0', echo) - node.start(cb) - }) - ], done) - }) - - after((done) => { - parallel([ - (cb) => nodeA.stop(cb), - (cb) => nodeB.stop(cb) - ], done) - }) - - it('nodeA.dial nodeB using PeerInfo without proto (warmup)', (done) => { - nodeA.dial(nodeB.peerInfo, (err) => { - expect(err).to.not.exist() - - // Some time for Identify to finish - setTimeout(check, 500) - - function check () { - parallel([ - (cb) => { - const peers = nodeA.peerBook.getAll() - expect(err).to.not.exist() - expect(Object.keys(peers)).to.have.length(1) - cb() - }, - (cb) => { - const peers = nodeB.peerBook.getAll() - expect(err).to.not.exist() - expect(Object.keys(peers)).to.have.length(1) - cb() - } - ], done) - } - }) - }) - - it('nodeA.dial nodeB using PeerInfo', (done) => { - nodeA.dialProtocol(nodeB.peerInfo, '/echo/1.0.0', (err, conn) => { - expect(err).to.not.exist() - - tryEcho(conn, done) - }) - }) - - it('nodeA.hangUp nodeB using PeerInfo (first)', (done) => { - nodeA.hangUp(nodeB.peerInfo, (err) => { - expect(err).to.not.exist() - setTimeout(check, 500) - - function check () { - parallel([ - (cb) => { - const peers = nodeA.peerBook.getAll() - expect(Object.keys(peers)).to.have.length(1) - expect(nodeA._switch.connection.getAll()).to.have.length(0) - cb() - }, - (cb) => { - const peers = nodeB.peerBook.getAll() - expect(Object.keys(peers)).to.have.length(1) - expect(nodeB._switch.connection.getAll()).to.have.length(0) - cb() - } - ], done) - } - }) - }) - - it('nodeA.dialProtocol nodeB using multiaddr', (done) => { - nodeA.dialProtocol(nodeB.peerInfo.multiaddrs.toArray()[0], '/echo/1.0.0', (err, conn) => { - // Some time for Identify to finish - setTimeout(check, 500) - - function check () { - expect(err).to.not.exist() - series([ - (cb) => { - const peers = nodeA.peerBook.getAll() - expect(Object.keys(peers)).to.have.length(1) - expect(nodeA._switch.connection.getAll()).to.have.length(1) - cb() - }, - (cb) => { - const peers = nodeB.peerBook.getAll() - expect(Object.keys(peers)).to.have.length(1) - expect(nodeA._switch.connection.getAll()).to.have.length(1) - cb() - } - ], () => tryEcho(conn, done)) - } - }) - }) - - it('nodeA.hangUp nodeB using multiaddr (second)', (done) => { - nodeA.hangUp(nodeB.peerInfo.multiaddrs.toArray()[0], (err) => { - expect(err).to.not.exist() - setTimeout(check, 500) - - function check () { - parallel([ - (cb) => { - const peers = nodeA.peerBook.getAll() - expect(Object.keys(peers)).to.have.length(1) - expect(nodeA._switch.connection.getAll()).to.have.length(0) - cb() - }, - (cb) => { - const peers = nodeB.peerBook.getAll() - expect(Object.keys(peers)).to.have.length(1) - expect(nodeB._switch.connection.getAll()).to.have.length(0) - cb() - } - ], done) - } - }) - }) - - it('nodeA.dialProtocol nodeB using PeerId', (done) => { - nodeA.dialProtocol(nodeB.peerInfo.id, '/echo/1.0.0', (err, conn) => { - // Some time for Identify to finish - setTimeout(check, 500) - - function check () { - expect(err).to.not.exist() - series([ - (cb) => { - const peers = nodeA.peerBook.getAll() - expect(Object.keys(peers)).to.have.length(1) - expect(nodeA._switch.connection.getAll()).to.have.length(1) - cb() - }, - (cb) => { - const peers = nodeB.peerBook.getAll() - expect(Object.keys(peers)).to.have.length(1) - expect(nodeA._switch.connection.getAll()).to.have.length(1) - cb() - } - ], () => tryEcho(conn, done)) - } - }) - }) - - it('nodeA.hangUp nodeB using PeerId (third)', (done) => { - nodeA.hangUp(nodeB.peerInfo.id, (err) => { - expect(err).to.not.exist() - setTimeout(check, 500) - - function check () { - parallel([ - (cb) => { - const peers = nodeA.peerBook.getAll() - expect(Object.keys(peers)).to.have.length(1) - expect(nodeA._switch.connection.getAll()).to.have.length(0) - cb() - }, - (cb) => { - const peers = nodeB.peerBook.getAll() - expect(Object.keys(peers)).to.have.length(1) - expect(nodeB._switch.connection.getAll()).to.have.length(0) - cb() - } - ], done) - } - }) - }) - - it('.dialFSM check conn and close', (done) => { - nodeA.dialFSM(nodeB.peerInfo, (err, connFSM) => { - expect(err).to.not.exist() - - connFSM.once('muxed', () => { - expect( - nodeA._switch.connection.getAllById(nodeB.peerInfo.id.toB58String()) - ).to.have.length(1) - - connFSM.once('error', done) - connFSM.once('close', () => { - // ensure the connection is closed - expect( - nodeA._switch.connection.getAllById(nodeB.peerInfo.id.toB58String()) - ).to.have.length(0) - done() - }) - - connFSM.close() - }) - }) - }) - - it('.dialFSM with a protocol, do an echo and close', (done) => { - nodeA.dialFSM(nodeB.peerInfo, '/echo/1.0.0', (err, connFSM) => { - expect(err).to.not.exist() - connFSM.once('connection', (conn) => { - expect( - nodeA._switch.connection.getAllById(nodeB.peerInfo.id.toB58String()) - ).to.have.length(1) - tryEcho(conn, () => { - connFSM.close() - }) - }) - connFSM.once('error', done) - connFSM.once('close', () => { - // ensure the connection is closed - expect( - nodeA._switch.connection.getAllById(nodeB.peerInfo.id.toB58String()) - ).to.have.length(0) - done() - }) - }) - }) - }) - - describe('TCP + WebSockets', () => { - let nodeTCP - let nodeTCPnWS - let nodeWS - - before((done) => { - parallel([ - (cb) => createNode([ - '/ip4/0.0.0.0/tcp/0' - ], (err, node) => { - expect(err).to.not.exist() - nodeTCP = node - node.handle('/echo/1.0.0', echo) - node.start(cb) - }), - (cb) => createNode([ - '/ip4/0.0.0.0/tcp/0', - '/ip4/127.0.0.1/tcp/25011/ws' - ], (err, node) => { - expect(err).to.not.exist() - nodeTCPnWS = node - node.handle('/echo/1.0.0', echo) - node.start(cb) - }), - (cb) => createNode([ - '/ip4/127.0.0.1/tcp/25022/ws' - ], (err, node) => { - expect(err).to.not.exist() - nodeWS = node - node.handle('/echo/1.0.0', echo) - node.start(cb) - }) - ], done) - }) - - after((done) => { - parallel([ - (cb) => nodeTCP.stop(cb), - (cb) => nodeTCPnWS.stop(cb), - (cb) => nodeWS.stop(cb) - ], done) - }) - - it('nodeTCP.dial nodeTCPnWS using PeerInfo', (done) => { - nodeTCP.dial(nodeTCPnWS.peerInfo, (err) => { - expect(err).to.not.exist() - - // Some time for Identify to finish - setTimeout(check, 500) - - function check () { - parallel([ - (cb) => { - const peers = nodeTCP.peerBook.getAll() - expect(Object.keys(peers)).to.have.length(1) - expect(nodeTCP._switch.connection.getAll()).to.have.length(1) - cb() - }, - (cb) => { - const peers = nodeTCPnWS.peerBook.getAll() - expect(Object.keys(peers)).to.have.length(1) - expect(nodeTCPnWS._switch.connection.getAll()).to.have.length(1) - cb() - } - ], done) - } - }) - }) - - it('nodeTCP.hangUp nodeTCPnWS using PeerInfo', (done) => { - nodeTCP.hangUp(nodeTCPnWS.peerInfo, (err) => { - expect(err).to.not.exist() - setTimeout(check, 500) - - function check () { - parallel([ - (cb) => { - const peers = nodeTCP.peerBook.getAll() - expect(Object.keys(peers)).to.have.length(1) - expect(nodeTCP._switch.connection.getAll()).to.have.length(0) - cb() - }, - (cb) => { - const peers = nodeTCPnWS.peerBook.getAll() - expect(Object.keys(peers)).to.have.length(1) - expect(nodeTCPnWS._switch.connection.getAll()).to.have.length(0) - cb() - } - ], done) - } - }) - }) - - it('nodeTCPnWS.dial nodeWS using PeerInfo', (done) => { - nodeTCPnWS.dial(nodeWS.peerInfo, (err) => { - expect(err).to.not.exist() - - // Some time for Identify to finish - setTimeout(check, 500) - - function check () { - parallel([ - (cb) => { - const peers = nodeTCPnWS.peerBook.getAll() - expect(Object.keys(peers)).to.have.length(2) - expect(nodeTCPnWS._switch.connection.getAll()).to.have.length(1) - cb() - }, - (cb) => { - const peers = nodeWS.peerBook.getAll() - expect(Object.keys(peers)).to.have.length(1) - expect(nodeWS._switch.connection.getAll()).to.have.length(1) - cb() - } - ], done) - } - }) - }) - - it('nodeTCPnWS.hangUp nodeWS using PeerInfo', (done) => { - nodeTCPnWS.hangUp(nodeWS.peerInfo, (err) => { - expect(err).to.not.exist() - setTimeout(check, 500) - - function check () { - parallel([ - (cb) => { - const peers = nodeTCPnWS.peerBook.getAll() - expect(Object.keys(peers)).to.have.length(2) - expect(nodeTCPnWS._switch.connection.getAll()).to.have.length(0) - - cb() - }, - (cb) => { - const peers = nodeWS.peerBook.getAll() - expect(Object.keys(peers)).to.have.length(1) - expect(nodeWS._switch.connection.getAll()).to.have.length(0) - cb() - } - ], done) - } - }) - }) - - // Until https://github.com/libp2p/js-libp2p/issues/46 is resolved - // Everynode will be able to dial in WebSockets - it.skip('nodeTCP.dial nodeWS using PeerInfo is unsuccesful', (done) => { - nodeTCP.dial(nodeWS.peerInfo, (err) => { - expect(err).to.exist() - done() - }) - }) - }) - - describe('TCP + WebSockets + WebRTCStar', () => { - let nodeAll - let nodeTCP - let nodeWS - let nodeWebRTCStar - - before(function (done) { - this.timeout(5 * 1000) - - parallel([ - (cb) => { - const wstar = new WRTCStar({ wrtc: wrtc }) - - createNode([ - '/ip4/0.0.0.0/tcp/0', - '/ip4/127.0.0.1/tcp/25011/ws', - `${WRTC_RENDEZVOUS_MULTIADDR.toString()}/p2p-webrtc-star` - ], { - modules: { - transport: [ - TCP, - WS, - wstar - ], - peerDiscovery: [wstar.discovery] - }, - config: { - peerDiscovery: { - autoDial: false, - [wstar.discovery.tag]: { - enabled: true - } - } - } - }, (err, node) => { - expect(err).to.not.exist() - nodeAll = node - node.handle('/echo/1.0.0', echo) - node.start(cb) - }) - }, - (cb) => createNode([ - '/ip4/0.0.0.0/tcp/0' - ], { - config: { - peerDiscovery: { - autoDial: false - } - } - }, (err, node) => { - expect(err).to.not.exist() - nodeTCP = node - node.handle('/echo/1.0.0', echo) - node.start(cb) - }), - (cb) => createNode([ - '/ip4/127.0.0.1/tcp/25022/ws' - ], { - config: { - peerDiscovery: { - autoDial: false - } - } - }, (err, node) => { - expect(err).to.not.exist() - nodeWS = node - node.handle('/echo/1.0.0', echo) - node.start(cb) - }), - (cb) => { - const wstar = new WRTCStar({ wrtc: wrtc }) - - createNode([ - `${WRTC_RENDEZVOUS_MULTIADDR.toString()}/p2p-webrtc-star` - ], { - modules: { - transport: [wstar], - peerDiscovery: [wstar.discovery] - }, - config: { - peerDiscovery: { - autoDial: false, - [wstar.discovery.tag]: { - enabled: true - } - } - } - }, (err, node) => { - expect(err).to.not.exist() - nodeWebRTCStar = node - node.handle('/echo/1.0.0', echo) - node.start(cb) - }) - } - ], done) - }) - - after(function (done) { - this.timeout(10 * 1000) - - parallel([ - (cb) => nodeAll.stop(cb), - (cb) => nodeTCP.stop(cb), - (cb) => nodeWS.stop(cb), - (cb) => nodeWebRTCStar.stop(cb) - ], done) - }) - - function check (otherNode, muxed, peers, callback) { - let i = 1; - [nodeAll, otherNode].forEach((node) => { - expect(Object.keys(node.peerBook.getAll())).to.have.length(i-- ? peers : 1) - expect(node._switch.connection.getAll()).to.have.length(muxed) - }) - callback() - } - - it('nodeAll.dial nodeTCP using PeerInfo', (done) => { - nodeAll.dial(nodeTCP.peerInfo, (err) => { - expect(err).to.not.exist() - // Some time for Identify to finish - setTimeout(() => check(nodeTCP, 1, 1, done), 500) - }) - }) - - it('nodeAll.hangUp nodeTCP using PeerInfo', (done) => { - nodeAll.hangUp(nodeTCP.peerInfo, (err) => { - expect(err).to.not.exist() - // Some time for Identify to finish - setTimeout(() => check(nodeTCP, 0, 1, done), 500) - }) - }) - - it('nodeAll.dial nodeWS using PeerInfo', (done) => { - nodeAll.dial(nodeWS.peerInfo, (err) => { - expect(err).to.not.exist() - // Some time for Identify to finish - setTimeout(() => check(nodeWS, 1, 2, done), 500) - }) - }) - - it('nodeAll.hangUp nodeWS using PeerInfo', (done) => { - nodeAll.hangUp(nodeWS.peerInfo, (err) => { - expect(err).to.not.exist() - // Some time for Identify to finish - setTimeout(() => check(nodeWS, 0, 2, done), 500) - }) - }) - - it('nodeAll.dial nodeWebRTCStar using PeerInfo', function (done) { - this.timeout(40 * 1000) - - nodeAll.dial(nodeWebRTCStar.peerInfo, (err) => { - expect(err).to.not.exist() - // Some time for Identify to finish - setTimeout(() => check(nodeWebRTCStar, 1, 3, done), 500) - }) - }) - - it('nodeAll.hangUp nodeWebRTCStar using PeerInfo', (done) => { - nodeAll.hangUp(nodeWebRTCStar.peerInfo, (err) => { - expect(err).to.not.exist() - setTimeout(() => check(nodeWebRTCStar, 0, 3, done), 500) - }) - }) - }) - - describe('TCP + WebSockets + WebSocketStar', () => { - let nodeAll - let nodeTCP - let nodeWS - let nodeWebSocketStar - let ss - const PORT = 24642 - - before(async () => { - ss = await rendezvous.start({ - port: PORT - }) - }) - - before((done) => { - parallel([ - (cb) => { - const wstar = new WSStar() - - createNode([ - '/ip4/0.0.0.0/tcp/0', - '/ip4/127.0.0.1/tcp/25011/ws', - `/ip4/127.0.0.1/tcp/${PORT}/ws/p2p-websocket-star` - ], { - modules: { - transport: [ - TCP, - WS, - wstar - ], - peerDiscovery: [wstar.discovery] - } - }, (err, node) => { - expect(err).to.not.exist() - nodeAll = node - wstar.lazySetId(node.peerInfo.id) - node.handle('/echo/1.0.0', echo) - node.start(cb) - }) - }, - (cb) => createNode([ - '/ip4/0.0.0.0/tcp/0' - ], (err, node) => { - expect(err).to.not.exist() - nodeTCP = node - node.handle('/echo/1.0.0', echo) - node.start(cb) - }), - (cb) => createNode([ - '/ip4/127.0.0.1/tcp/25022/ws' - ], (err, node) => { - expect(err).to.not.exist() - nodeWS = node - node.handle('/echo/1.0.0', echo) - node.start(cb) - }), - - (cb) => { - const wstar = new WSStar({}) - - createNode([ - `/ip4/127.0.0.1/tcp/${PORT}/ws/p2p-websocket-star` - ], { - modules: { - transport: [wstar], - peerDiscovery: [wstar.discovery] - } - }, (err, node) => { - expect(err).to.not.exist() - nodeWebSocketStar = node - wstar.lazySetId(node.peerInfo.id) - node.handle('/echo/1.0.0', echo) - node.start(cb) - }) - } - ], done) - }) - - after((done) => { - parallel([ - (cb) => nodeAll.stop(cb), - (cb) => nodeTCP.stop(cb), - (cb) => nodeWS.stop(cb), - (cb) => nodeWebSocketStar.stop(cb), - async () => { - await ss.stop() - } - ], done) - }) - - function check (otherNode, muxed, peers, done) { - let i = 1; - [nodeAll, otherNode].forEach((node) => { - expect(Object.keys(node.peerBook.getAll())).to.have.length(i-- ? peers : 1) - expect(node._switch.connection.getAll()).to.have.length(muxed) - }) - done() - } - - it('nodeAll.dial nodeTCP using PeerInfo', (done) => { - nodeAll.dial(nodeTCP.peerInfo, (err) => { - expect(err).to.not.exist() - // Some time for Identify to finish - setTimeout(() => check(nodeTCP, 1, 1, done), 500) - }) - }) - - it('nodeAll.hangUp nodeTCP using PeerInfo', (done) => { - nodeAll.hangUp(nodeTCP.peerInfo, (err) => { - expect(err).to.not.exist() - // Some time for Identify to finish - setTimeout(() => check(nodeTCP, 0, 1, done), 500) - }) - }) - - it('nodeAll.dial nodeWS using PeerInfo', (done) => { - nodeAll.dial(nodeWS.peerInfo, (err) => { - expect(err).to.not.exist() - // Some time for Identify to finish - setTimeout(() => check(nodeWS, 1, 2, done), 500) - }) - }) - - it('nodeAll.hangUp nodeWS using PeerInfo', (done) => { - nodeAll.hangUp(nodeWS.peerInfo, (err) => { - expect(err).to.not.exist() - // Some time for Identify to finish - setTimeout(() => check(nodeWS, 0, 2, done), 500) - }) - }) - - it('nodeAll.dial nodeWebSocketStar using PeerInfo', (done) => { - nodeAll.dial(nodeWebSocketStar.peerInfo, (err) => { - expect(err).to.not.exist() - // Some time for Identify to finish - setTimeout(() => check(nodeWebSocketStar, 1, 3, done), 500) - }) - }) - - it('nodeAll.hangUp nodeWebSocketStar using PeerInfo', (done) => { - nodeAll.hangUp(nodeWebSocketStar.peerInfo, (err) => { - expect(err).to.not.exist() - // Some time for Identify to finish - setTimeout(() => check(nodeWebSocketStar, 0, 3, done), 500) - }) - }) - }) -}) diff --git a/test/transports/transport-manager.node.js b/test/transports/transport-manager.node.js new file mode 100644 index 00000000..71d776fc --- /dev/null +++ b/test/transports/transport-manager.node.js @@ -0,0 +1,56 @@ +'use strict' +/* eslint-env mocha */ + +const chai = require('chai') +chai.use(require('dirty-chai')) +const { expect } = chai +const TransportManager = require('../../src/transport-manager') +const Transport = require('libp2p-tcp') +const multiaddr = require('multiaddr') +const mockUpgrader = require('../utils/mockUpgrader') +const addrs = [ + multiaddr('/ip4/127.0.0.1/tcp/0'), + multiaddr('/ip4/127.0.0.1/tcp/0') +] + +describe('Transport Manager (TCP)', () => { + let tm + + before(() => { + tm = new TransportManager({ + libp2p: {}, + upgrader: mockUpgrader, + onConnection: () => {} + }) + }) + + afterEach(async () => { + await tm.removeAll() + expect(tm._transports.size).to.equal(0) + }) + + it('should be able to add and remove a transport', async () => { + tm.add(Transport.prototype[Symbol.toStringTag], Transport) + expect(tm._transports.size).to.equal(1) + await tm.remove(Transport.prototype[Symbol.toStringTag]) + }) + + it('should be able to listen', async () => { + tm.add(Transport.prototype[Symbol.toStringTag], Transport) + await tm.listen(addrs) + expect(tm._listeners.size).to.equal(1) + // Ephemeral ip addresses may result in multiple listeners + expect(tm.getAddrs().length).to.equal(addrs.length) + await tm.close() + expect(tm._listeners.size).to.equal(0) + }) + + it('should be able to dial', async () => { + tm.add(Transport.prototype[Symbol.toStringTag], Transport) + await tm.listen(addrs) + const addr = tm.getAddrs().shift() + const connection = await tm.dial(addr) + expect(connection).to.exist() + await connection.close() + }) +}) diff --git a/test/transports/transport-manager.spec.js b/test/transports/transport-manager.spec.js new file mode 100644 index 00000000..91a976eb --- /dev/null +++ b/test/transports/transport-manager.spec.js @@ -0,0 +1,139 @@ +'use strict' +/* eslint-env mocha */ + +const chai = require('chai') +chai.use(require('dirty-chai')) +const { expect } = chai +const sinon = require('sinon') + +const multiaddr = require('multiaddr') +const Transport = require('libp2p-websockets') +const TransportManager = require('../../src/transport-manager') +const mockUpgrader = require('../utils/mockUpgrader') +const { MULTIADDRS_WEBSOCKETS } = require('../fixtures/browser') +const { codes: ErrorCodes } = require('../../src/errors') +const Libp2p = require('../../src') +const Peers = require('../fixtures/peers') +const PeerId = require('peer-id') +const PeerInfo = require('peer-info') + +describe('Transport Manager (WebSockets)', () => { + let tm + + before(() => { + tm = new TransportManager({ + libp2p: {}, + upgrader: mockUpgrader, + onConnection: () => {} + }) + }) + + afterEach(async () => { + await tm.removeAll() + expect(tm._transports.size).to.equal(0) + }) + + it('should be able to add and remove a transport', async () => { + tm.add(Transport.prototype[Symbol.toStringTag], Transport) + expect(tm._transports.size).to.equal(1) + await tm.remove(Transport.prototype[Symbol.toStringTag]) + }) + + it('should not be able to add a transport without a key', () => { + expect(() => { + tm.add(undefined, Transport) + }).to.throw().that.satisfies((err) => { + return err.code === ErrorCodes.ERR_INVALID_KEY + }) + }) + + it('should not be able to add a transport twice', () => { + tm.add(Transport.prototype[Symbol.toStringTag], Transport) + expect(() => { + tm.add(Transport.prototype[Symbol.toStringTag], Transport) + }).to.throw().that.satisfies((err) => { + return err.code === ErrorCodes.ERR_DUPLICATE_TRANSPORT + }) + }) + + it('should be able to dial', async () => { + tm.add(Transport.prototype[Symbol.toStringTag], Transport) + const addr = MULTIADDRS_WEBSOCKETS[0] + const connection = await tm.dial(addr) + expect(connection).to.exist() + await connection.close() + }) + + it('should fail to dial an unsupported address', async () => { + tm.add(Transport.prototype[Symbol.toStringTag], Transport) + const addr = multiaddr('/ip4/127.0.0.1/tcp/0') + try { + await tm.dial(addr) + } catch (err) { + expect(err).to.satisfy((err) => err.code === ErrorCodes.ERR_TRANSPORT_UNAVAILABLE) + return + } + + expect.fail('Dial attempt should have failed') + }) + + it('should fail to listen with no valid address', async () => { + tm.add(Transport.prototype[Symbol.toStringTag], Transport) + const addrs = [multiaddr('/ip4/127.0.0.1/tcp/0')] + try { + await tm.listen(addrs) + } catch (err) { + expect(err).to.satisfy((err) => err.code === ErrorCodes.ERR_NO_VALID_ADDRESSES) + return + } + + expect.fail('should have failed') + }) +}) + +describe('libp2p.transportManager', () => { + let peerInfo + let libp2p + + before(async () => { + const peerId = await PeerId.createFromJSON(Peers[0]) + peerInfo = new PeerInfo(peerId) + }) + + afterEach(async () => { + sinon.restore() + libp2p && await libp2p.stop() + libp2p = null + }) + + it('should create a TransportManager', () => { + libp2p = new Libp2p({ + peerInfo, + modules: { + transport: [Transport] + } + }) + + expect(libp2p.transportManager).to.exist() + expect(libp2p.transportManager._transports.size).to.equal(1) + }) + + it('starting and stopping libp2p should start and stop TransportManager', async () => { + libp2p = new Libp2p({ + peerInfo, + modules: { + transport: [Transport] + } + }) + + // We don't need to listen, stub it + sinon.stub(libp2p.transportManager, 'listen').returns(true) + sinon.spy(libp2p.transportManager, 'close') + + await libp2p.start() + await libp2p.stop() + + expect(libp2p.transportManager.listen.callCount).to.equal(1) + expect(libp2p.transportManager.close.callCount).to.equal(1) + }) +}) diff --git a/test/utils/bundle-browser.js b/test/utils/bundle-browser.js deleted file mode 100644 index a5140da9..00000000 --- a/test/utils/bundle-browser.js +++ /dev/null @@ -1,103 +0,0 @@ -'use strict' - -const WS = require('libp2p-websockets') -const WebRTCStar = require('libp2p-webrtc-star') -const WebSocketStar = require('libp2p-websocket-star') -const Bootstrap = require('libp2p-bootstrap') -const SPDY = require('libp2p-spdy') -const MPLEX = require('libp2p-mplex') -const PULLMPLEX = require('pull-mplex') -const KadDHT = require('libp2p-kad-dht') -const GossipSub = require('libp2p-gossipsub') -const SECIO = require('libp2p-secio') -const defaultsDeep = require('@nodeutils/defaults-deep') -const libp2p = require('../..') - -function mapMuxers (list) { - return list.map((pref) => { - if (typeof pref !== 'string') { return pref } - switch (pref.trim().toLowerCase()) { - case 'spdy': return SPDY - case 'mplex': return MPLEX - case 'pullmplex': return PULLMPLEX - default: - throw new Error(pref + ' muxer not available') - } - }) -} - -function getMuxers (options) { - if (options) { - return mapMuxers(options) - } else { - return [PULLMPLEX, MPLEX, SPDY] - } -} - -class Node extends libp2p { - constructor (_options) { - _options = _options || {} - - const starOpts = { id: _options.peerInfo.id } - const wrtcStar = new WebRTCStar(starOpts) - const wsStar = new WebSocketStar(starOpts) - - const defaults = { - modules: { - transport: [ - wrtcStar, - wsStar, - new WS() - ], - streamMuxer: getMuxers(_options.muxer), - connEncryption: [ - SECIO - ], - peerDiscovery: [ - wrtcStar.discovery, - wsStar.discovery, - Bootstrap - ], - dht: KadDHT, - pubsub: GossipSub - }, - config: { - peerDiscovery: { - autoDial: true, - webRTCStar: { - enabled: true - }, - websocketStar: { - enabled: true - }, - bootstrap: { - interval: 10000, - enabled: false, - list: _options.boostrapList - } - }, - relay: { - enabled: false, - hop: { - enabled: false, - active: false - } - }, - dht: { - kBucketSize: 20, - randomWalk: { - enabled: true - }, - enabled: false - }, - pubsub: { - enabled: false - } - } - } - - super(defaultsDeep(_options, defaults)) - } -} - -module.exports = Node diff --git a/test/utils/bundle-nodejs.js b/test/utils/bundle-nodejs.js deleted file mode 100644 index 842224ca..00000000 --- a/test/utils/bundle-nodejs.js +++ /dev/null @@ -1,96 +0,0 @@ -'use strict' - -const TCP = require('libp2p-tcp') -const MulticastDNS = require('libp2p-mdns') -const WS = require('libp2p-websockets') -const Bootstrap = require('libp2p-bootstrap') -const SPDY = require('libp2p-spdy') -const KadDHT = require('libp2p-kad-dht') -const GossipSub = require('libp2p-gossipsub') -const MPLEX = require('libp2p-mplex') -const PULLMPLEX = require('pull-mplex') -const SECIO = require('libp2p-secio') -const defaultsDeep = require('@nodeutils/defaults-deep') -const libp2p = require('../..') - -function mapMuxers (list) { - return list.map((pref) => { - if (typeof pref !== 'string') { return pref } - switch (pref.trim().toLowerCase()) { - case 'spdy': return SPDY - case 'mplex': return MPLEX - case 'pullmplex': return PULLMPLEX - default: - throw new Error(pref + ' muxer not available') - } - }) -} - -function getMuxers (muxers) { - const muxerPrefs = process.env.LIBP2P_MUXER - if (muxerPrefs && !muxers) { - return mapMuxers(muxerPrefs.split(',')) - } else if (muxers) { - return mapMuxers(muxers) - } else { - return [PULLMPLEX, MPLEX, SPDY] - } -} - -class Node extends libp2p { - constructor (_options) { - const defaults = { - modules: { - transport: [ - TCP, - WS - ], - streamMuxer: getMuxers(_options.muxer), - connEncryption: [ - SECIO - ], - peerDiscovery: [ - MulticastDNS, - Bootstrap - ], - dht: KadDHT, - pubsub: GossipSub - }, - config: { - peerDiscovery: { - autoDial: true, - mdns: { - interval: 10000, - enabled: false - }, - bootstrap: { - interval: 10000, - enabled: false, - list: _options.bootstrapList - } - }, - relay: { - enabled: false, - hop: { - enabled: false, - active: false - } - }, - dht: { - kBucketSize: 20, - randomWalk: { - enabled: true - }, - enabled: true - }, - pubsub: { - enabled: false - } - } - } - - super(defaultsDeep(_options, defaults)) - } -} - -module.exports = Node diff --git a/test/utils/constants.js b/test/utils/constants.js deleted file mode 100644 index 33eaf45c..00000000 --- a/test/utils/constants.js +++ /dev/null @@ -1,41 +0,0 @@ -'use strict' - -const PeerId = require('peer-id') -const PeerInfo = require('peer-info') -const nextTick = require('async/nextTick') -const peerJSON = require('../fixtures/test-peer') -const multiaddr = require('multiaddr') -const promisify = require('promisify-es6') - -let peerRelay = null - -/** - * Creates a `PeerInfo` that can be used across testing. Once the - * relay `PeerInfo` has been requested, it will be reused for each - * additional request. - * - * This is currently being used to create a relay on test bootstrapping - * so that it can be used by browser nodes during their test suite. This - * is necessary for running a TCP node during browser tests. - * @private - * @param {function(error, PeerInfo)} callback - * @returns {void} - */ -module.exports.getPeerRelay = promisify((callback) => { - if (peerRelay) return nextTick(callback, null, peerRelay) - - PeerId.createFromJSON(peerJSON, (err, peerId) => { - if (err) { - return callback(err) - } - peerRelay = new PeerInfo(peerId) - - peerRelay.multiaddrs.add('/ip4/127.0.0.1/tcp/9200/ws') - peerRelay.multiaddrs.add('/ip4/127.0.0.1/tcp/9245') - - callback(null, peerRelay) - }) -}) - -module.exports.WS_RENDEZVOUS_MULTIADDR = multiaddr('/ip4/127.0.0.1/tcp/14444/ws') -module.exports.WRTC_RENDEZVOUS_MULTIADDR = multiaddr('/ip4/127.0.0.1/tcp/15555/ws') diff --git a/test/utils/create-node.js b/test/utils/create-node.js deleted file mode 100644 index 3b9cf43b..00000000 --- a/test/utils/create-node.js +++ /dev/null @@ -1,41 +0,0 @@ -/* eslint-env mocha */ -'use strict' - -const chai = require('chai') -chai.use(require('dirty-chai')) -const PeerInfo = require('peer-info') -const PeerId = require('peer-id') -const waterfall = require('async/waterfall') -const Node = require('./bundle-nodejs') - -function createNode (multiaddrs, options, callback) { - if (typeof options === 'function') { - callback = options - options = {} - } - - options = options || {} - - if (!Array.isArray(multiaddrs)) { - multiaddrs = [multiaddrs] - } - - waterfall([ - (cb) => createPeerInfo(cb), - (peerInfo, cb) => { - multiaddrs.map((ma) => peerInfo.multiaddrs.add(ma)) - options.peerInfo = peerInfo - cb(null, new Node(options)) - } - ], callback) -} - -function createPeerInfo (callback) { - waterfall([ - (cb) => PeerId.create({ bits: 512 }, cb), - (peerId, cb) => PeerInfo.create(peerId, cb) - ], callback) -} - -module.exports = createNode -module.exports.createPeerInfo = createPeerInfo diff --git a/test/utils/echo.js b/test/utils/echo.js deleted file mode 100644 index 31aa584b..00000000 --- a/test/utils/echo.js +++ /dev/null @@ -1,11 +0,0 @@ -/* eslint-env mocha */ -'use strict' - -const pull = require('pull-stream') - -function echo (protocol, conn) { - pull(conn, conn) -} - -module.exports = echo -module.exports.multicodec = '/echo/1.0.0' diff --git a/test/utils/mockUpgrader.js b/test/utils/mockUpgrader.js new file mode 100644 index 00000000..5e72e2de --- /dev/null +++ b/test/utils/mockUpgrader.js @@ -0,0 +1,6 @@ +'use strict' + +module.exports = { + upgradeInbound: (maConn) => maConn, + upgradeOutbound: (maConn) => maConn +} diff --git a/test/utils/try-echo.js b/test/utils/try-echo.js deleted file mode 100644 index b8874b74..00000000 --- a/test/utils/try-echo.js +++ /dev/null @@ -1,21 +0,0 @@ -'use strict' - -const pull = require('pull-stream') -const chai = require('chai') -const dirtyChai = require('dirty-chai') -const expect = chai.expect -chai.use(dirtyChai) - -module.exports = (conn, callback) => { - const values = [Buffer.from('echo')] - - pull( - pull.values(values), - conn, - pull.collect((err, _values) => { - expect(err).to.not.exist() - expect(_values).to.eql(values) - callback() - }) - ) -}