diff --git a/elements/lisk-p2p/src/p2p.ts b/elements/lisk-p2p/src/p2p.ts index 7a7dd88f0d6..febb89e5fdf 100644 --- a/elements/lisk-p2p/src/p2p.ts +++ b/elements/lisk-p2p/src/p2p.ts @@ -117,6 +117,7 @@ export { export const EVENT_NEW_INBOUND_PEER = 'newInboundPeer'; export const EVENT_FAILED_TO_ADD_INBOUND_PEER = 'failedToAddInboundPeer'; export const EVENT_NEW_PEER = 'newPeer'; +export const EVENT_NETWORK_READY = 'networkReady'; export const DEFAULT_NODE_HOST_IP = '0.0.0.0'; export const DEFAULT_DISCOVERY_INTERVAL = 30000; @@ -156,6 +157,7 @@ export class P2P extends EventEmitter { private readonly _sanitizedPeerLists: PeerLists; private readonly _httpServer: http.Server; private _isActive: boolean; + private _hasConnected: boolean; private readonly _peerBook: PeerBook; private readonly _bannedPeers: Set; private readonly _populatorInterval: number; @@ -214,6 +216,7 @@ export class P2P extends EventEmitter { ); this._config = config; this._isActive = false; + this._hasConnected = false; this._peerBook = new PeerBook({ secret: config.secret ? config.secret : DEFAULT_RANDOM_SECRET, }); @@ -260,6 +263,9 @@ export class P2P extends EventEmitter { // Re-emit the message to allow it to bubble up the class hierarchy. this.emit(EVENT_CONNECT_OUTBOUND, peerInfo); + if (this._isNetworkReady()) { + this.emit(EVENT_NETWORK_READY); + } }; this._handleOutboundPeerConnectAbort = (peerInfo: P2PPeerInfo) => { @@ -777,6 +783,16 @@ export class P2P extends EventEmitter { } } + private _isNetworkReady(): boolean { + if (!this._hasConnected && this._peerPool.getConnectedPeers().length > 0) { + this._hasConnected = true; + + return true; + } + + return false; + } + private _pickRandomPeers(count: number): ReadonlyArray { const peerList: ReadonlyArray = this._peerBook.getAllPeers(); // Peers whose values has been updated at least once. @@ -865,6 +881,7 @@ export class P2P extends EventEmitter { throw new Error('Cannot stop the node because it is not active'); } this._isActive = false; + this._hasConnected = false; this._stopPopulator(); this._peerPool.removeAllPeers(); await this._stopPeerServer(); diff --git a/elements/lisk-p2p/src/peer_pool.ts b/elements/lisk-p2p/src/peer_pool.ts index 6fe66158832..2f3baf79a52 100644 --- a/elements/lisk-p2p/src/peer_pool.ts +++ b/elements/lisk-p2p/src/peer_pool.ts @@ -316,9 +316,6 @@ export class PeerPool extends EventEmitter { ); const selectedPeers = this._peerSelectForRequest({ peers: getUniquePeersbyIp(listOfPeerInfo), - nodeInfo: this._nodeInfo, - peerLimit: 1, - requestPacket: packet, }); if (selectedPeers.length <= 0) { @@ -326,6 +323,7 @@ export class PeerPool extends EventEmitter { 'Request failed due to no peers found in peer selection', ); } + const selectedPeerId = constructPeerIdFromPeerInfo(selectedPeers[0]); return this.requestFromPeer(packet, selectedPeerId); diff --git a/elements/lisk-p2p/src/peer_selection.ts b/elements/lisk-p2p/src/peer_selection.ts index 1b5971a8c5e..0c250999de4 100644 --- a/elements/lisk-p2p/src/peer_selection.ts +++ b/elements/lisk-p2p/src/peer_selection.ts @@ -23,14 +23,6 @@ import { } from './p2p_types'; /* tslint:disable: readonly-keyword*/ -interface Histogram { - [key: number]: number; -} -interface HistogramValues { - height: number; - histogram: Histogram; - max: number; -} export const getUniquePeersbyIp = ( peerList: ReadonlyArray, @@ -55,60 +47,18 @@ export const getUniquePeersbyIp = ( export const selectPeersForRequest = ( input: P2PPeerSelectionForRequestInput, ): ReadonlyArray => { - const { peers, nodeInfo } = input; + const { peers } = input; const peerLimit = input.peerLimit; - const nodeHeight = nodeInfo ? nodeInfo.height : 0; - const filteredPeers = peers.filter( - // Remove unreachable peers or heights below last block height - (peer: P2PDiscoveredPeerInfo) => peer.height >= nodeHeight, - ); - if (filteredPeers.length === 0) { + if (peers.length === 0) { return []; } - // Order peers by descending height - const sortedPeers = filteredPeers.sort((a, b) => b.height - a.height); - - const aggregation = 2; - - const calculatedHistogramValues = sortedPeers.reduce( - (histogramValues: HistogramValues, peer: P2PDiscoveredPeerInfo) => { - const val = Math.floor(peer.height / aggregation) * aggregation; - histogramValues.histogram[val] = - (histogramValues.histogram[val] ? histogramValues.histogram[val] : 0) + - 1; - if (histogramValues.histogram[val] > histogramValues.max) { - histogramValues.max = histogramValues.histogram[val]; - histogramValues.height = val; - } - - return histogramValues; - }, - { height: 0, histogram: {}, max: -1 }, - ); - - // Perform histogram cut of peers too far from histogram maximum - const processedPeers = sortedPeers.filter( - peer => - peer && - Math.abs(calculatedHistogramValues.height - peer.height) < - aggregation + 1, - ); - if (peerLimit === undefined) { - return processedPeers; - } - - if (peerLimit === 1) { - const goodPeer: ReadonlyArray = [ - processedPeers[Math.floor(Math.random() * processedPeers.length)], - ]; - - return goodPeer; + return shuffle(peers); } - return shuffle(processedPeers).slice(0, peerLimit); + return shuffle(peers).slice(0, peerLimit); }; export const selectPeersForSend = ( diff --git a/elements/lisk-p2p/test/unit/peer_selection.ts b/elements/lisk-p2p/test/unit/peer_selection.ts index 2c3c6d4c9c7..19e1fb9de41 100644 --- a/elements/lisk-p2p/test/unit/peer_selection.ts +++ b/elements/lisk-p2p/test/unit/peer_selection.ts @@ -54,7 +54,7 @@ describe('peer selector', () => { it('returned array should contain good peers according to algorithm', () => { return expect(selectPeersForRequest({ peers: peerList, nodeInfo })) .and.be.an('array') - .and.of.length(3); + .and.of.length(5); }); it('return empty peer list for no peers as an argument', () => { @@ -82,7 +82,7 @@ describe('peer selector', () => { it('should return an array having all good peers', () => { return expect(selectPeersForRequest({ peers: peerList, nodeInfo })) .and.be.an('array') - .and.of.length(3); + .and.of.length(5); }); it('should return an array of equal length equal to requested number of peers', () => { @@ -102,7 +102,7 @@ describe('peer selector', () => { peer => peer.height < nodeInfo.height, ); - it('should return an array with 0 good peers', () => { + it('should return an array with 1 good peer', () => { return expect( selectPeersForRequest({ peers: lowHeightPeers, @@ -111,7 +111,7 @@ describe('peer selector', () => { }), ) .and.be.an('array') - .and.of.length(0); + .and.of.length(1); }); }); }); diff --git a/framework/src/modules/chain/chain.js b/framework/src/modules/chain/chain.js index 8fbe99b1d06..d1ffa6da36e 100644 --- a/framework/src/modules/chain/chain.js +++ b/framework/src/modules/chain/chain.js @@ -160,11 +160,14 @@ module.exports = class Chain { this._subscribeToEvents(); this.channel.subscribe('network:bootstrap', async () => { - this._startLoader(); this._calculateConsensus(); await this._startForging(); }); + this.channel.subscribe('network:ready', async () => { + this._startLoader(); + }); + // Avoid receiving blocks/transactions from the network during snapshotting process if (!this.options.loading.rebuildUpToRound) { this.channel.subscribe('network:event', ({ data: { event, data } }) => { diff --git a/framework/src/modules/network/index.js b/framework/src/modules/network/index.js index 717debca490..3bede3c8aa3 100644 --- a/framework/src/modules/network/index.js +++ b/framework/src/modules/network/index.js @@ -49,7 +49,7 @@ module.exports = class NetworkModule extends BaseModule { } get events() { - return ['bootstrap', 'event']; + return ['bootstrap', 'event', 'ready']; } get actions() { diff --git a/framework/src/modules/network/network.js b/framework/src/modules/network/network.js index 87bb635c509..3a78f02f903 100644 --- a/framework/src/modules/network/network.js +++ b/framework/src/modules/network/network.js @@ -17,6 +17,7 @@ const { getRandomBytes } = require('@liskhq/lisk-cryptography'); const { P2P, + EVENT_NETWORK_READY, EVENT_NEW_INBOUND_PEER, EVENT_CLOSE_INBOUND, EVENT_CLOSE_OUTBOUND, @@ -171,6 +172,10 @@ module.exports = class Network { }); // ---- START: Bind event handlers ---- + this.p2p.on(EVENT_NETWORK_READY, () => { + this.logger.debug('Node connected to the network'); + this.channel.publish('network:ready'); + }); this.p2p.on(EVENT_CLOSE_OUTBOUND, closePacket => { this.logger.debug(