Skip to content
This repository has been archived by the owner on Jun 11, 2024. It is now read-only.

Introduce management of peer buckets within new/tried peers list #3334

Closed
ishantiw opened this issue Feb 18, 2019 · 2 comments · Fixed by #4030
Closed

Introduce management of peer buckets within new/tried peers list #3334

ishantiw opened this issue Feb 18, 2019 · 2 comments · Fixed by #4030
Assignees
Milestone

Comments

@ishantiw
Copy link
Contributor

ishantiw commented Feb 18, 2019

Description

Introduce a module to handle newPeers and triedPeers list and buckets within them according to LIP0004. With this, we can separate the concern of managing buckets within these lists from P2P class. Also, there will be one instance of a bucket management class which will make sure that discovery or selection processes use the updated buckets to select any peer.

Since we want to maintain banned peers temporarily and also move them back forth based on ban score and banTime from the newPeer and triedPeer list buckets.

triedPeers bucket system:

To each IP address, we associate a bucket b contained in {0,1,..., 63}. For every bucket b, we only accept 32 addresses in the collection triedPeers. This way the collection triedPeers contains at most 2048 peers.

group = /16 IPv4 prefix of peer's IP address
k = Hash(random_secret, IP) % 4
b = Hash(random_secret, group, k) % 64
  • If a new address is supposed to be added, but there are already 32 addresses with the same bucket b in the collection, then a randomly selected address is evicted and moved to the newPeers collection
  • To ensure that the peers are randomly selected from a collection with sufficient diversity because IP addresses from the same group only hash to 4 different buckets. This protects against a local attacker that has a lot of IP addresses only in one group.

newPeers bucket system:

To each IP address, we associate a bucket b contained in {0,1,..., 127}
The bucket is computed from the IP address to be added, the IP address of the peer sending the addresses and a random secret of the node as follows:

group = /16 IPv4 prefix of peer's IP address
source_group = /16 IPv4 prefix of peer sending the address to the Lisk node
k = Hash(random_secret, source_group, group) % 16
b = Hash(random_secret, source_group, k) % 128
  • For a given source_group all IP addresses advertised by this source_group are hashed to at most 16 buckets.
  • If a new address is supposed to be added, but there are already 32 addresses with the same bucket b in the newPeers collection, then we do the following: We first evict any address that is more than 30 days old. If there is still no free slot, we evict a randomly chosen address.
  • When a peer is advertised, its source_group is stored together with its IP address and port so that it is available when a peer is moved between the triedPeers and newPeers collections.
Outgoing connections

. Additionally, at most 3 outgoing connections for every network group (/16 IPv4 prefix) are allowed in all cases.

@shuse2 shuse2 transferred this issue from LiskArchive/lisk-elements Apr 15, 2019
@diego-G diego-G removed their assignment May 13, 2019
@jondubois
Copy link
Contributor

Note that buckets affect the newPeers and triedPeers maps but buckets should not be coupled to the PeerPool (connected peers) in the code.

@ishantiw
Copy link
Contributor Author

Below is the raw idea of bucket system that I thought about a few months back. We instantiate it in P2P class and only add to a bucket or remove it, everything else could be managed inside the bucket itself and also banned peers list.

import { PeerInfo } from './peer';

export class PeerBucket {
	private readonly _bannedPeers: ReadonlyArray<PeerInfo>;
	private readonly _newPeers: Map<number, Set<PeerInfo>>;
	private readonly _triedPeers: Map<number, Set<PeerInfo>>;

	public constructor() {
		this._newPeers = new Map();
		this._triedPeers = new Map();
		this._bannedPeers = [];
	}

	public get newPeers(): Map<number, Set<PeerInfo>> {
		return this._newPeers;
	}

	public get triedPeers(): Map<number, Set<PeerInfo>> {
		return this._newPeers;
	}

	public get bannedPeers(): ReadonlyArray<PeerInfo> {
		return this._bannedPeers;
	}
	// TODO: Add logic to calculate bucket and add it to bucket group based on LIPS
	public calculateNewPeerBucketGroup(peerInfo: PeerInfo): number {
		// TODO: Based on IP, IP prefix and hash of random node secret with bucket size of 128
		return 1;
	}

	// TODO: Add logic to calculate bucket and add it to bucket group based on LIPS
	public calculateTriedPeerBucketGroup(peerInfo: PeerInfo): number {
		// TODO: Based on IP, IP prefix and hash of random node secret with bucket of 64
		return 1;
	}

	// TODO: Add logic to calculate bucket and add it to bucket group
	public addToNewPeers(peerInfo: PeerInfo): void {
		const bucketGroup = this.calculateNewPeerBucketGroup(peerInfo);
		const bucketId = this._newPeers.get(bucketGroup);

		if (bucketId) {
			bucketId.add(peerInfo);
		} else {
			const setForNewBucket = new Set();
			this._newPeers.set(bucketGroup, setForNewBucket.add(peerInfo));
		}
	}

	// TODO Add logic to calculate bucket and add it to bucket group
	public addToTriedPeers(peerInfo: PeerInfo): void {
		const bucketGroup = this.calculateNewPeerBucketGroup(peerInfo);
		const bucketId = this._triedPeers.get(bucketGroup);

		if (bucketId) {
			bucketId.add(peerInfo);
		} else {
			const setForNewBucket = new Set();
			this._triedPeers.set(bucketGroup, setForNewBucket.add(peerInfo));
		}
	}
}

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants