Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gs1.1: configurable overlay params #112

Merged
merged 1 commit into from
Jul 22, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 23 additions & 16 deletions ts/heartbeat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,13 @@ export class Heartbeat {
* @returns {void}
*/
_heartbeat (): void {
const {
D,
Dlo,
Dhi,
Dscore,
Dout
} = this.gossipsub._options
this.gossipsub.heartbeatTicks++

// cache scores throught the heartbeat
Expand Down Expand Up @@ -154,9 +161,9 @@ export class Heartbeat {
})

// do we have enough peers?
if (peers.size < constants.GossipsubDlo) {
if (peers.size < Dlo) {
const backoff = this.gossipsub.backoff.get(topic)
const ineed = constants.GossipsubD - peers.size
const ineed = D - peers.size
const peersSet = getGossipPeers(this.gossipsub, topic, ineed, id => {
// filter out mesh peers, direct peers, peers we are backing off, peers with negative score
return !peers.has(id) && !this.gossipsub.direct.has(id) && (!backoff || !backoff.has(id)) && getScore(id) >= 0
Expand All @@ -166,26 +173,26 @@ export class Heartbeat {
}

// do we have to many peers?
if (peers.size > constants.GossipsubDhi) {
if (peers.size > Dhi) {
let peersArray = Array.from(peers)
// sort by score
peersArray.sort((a, b) => getScore(b) - getScore(a))
// We keep the first D_score peers by score and the remaining up to D randomly
// under the constraint that we keep D_out peers in the mesh (if we have that many)
peersArray = peersArray.slice(0, constants.GossipsubDscore).concat(
shuffle(peersArray.slice(constants.GossipsubDscore))
peersArray = peersArray.slice(0, Dscore).concat(
shuffle(peersArray.slice(Dscore))
)

// count the outbound peers we are keeping
let outbound = 0
peersArray.slice(0, constants.GossipsubD).forEach(p => {
peersArray.slice(0, D).forEach(p => {
if (this.gossipsub.outbound.get(p)) {
outbound++
}
})

// if it's less than D_out, bubble up some outbound peers from the random selection
if (outbound < constants.GossipsubDout) {
if (outbound < Dout) {
const rotate = (i: number): void => {
// rotate the peersArray to the right and put the ith peer in the front
const p = peersArray[i]
Expand All @@ -198,7 +205,7 @@ export class Heartbeat {
// first bubble up all outbound peers already in the selection to the front
if (outbound > 0) {
let ihave = outbound
for (let i = 1; i < constants.GossipsubD && ihave > 0; i++) {
for (let i = 1; i < D && ihave > 0; i++) {
if (this.gossipsub.outbound.get(peersArray[i])) {
rotate(i)
ihave--
Expand All @@ -207,8 +214,8 @@ export class Heartbeat {
}

// now bubble up enough outbound peers outside the selection to the front
let ineed = constants.GossipsubD - outbound
for (let i = constants.GossipsubD; i < peersArray.length && ineed > 0; i++) {
let ineed = D - outbound
for (let i = D; i < peersArray.length && ineed > 0; i++) {
if (this.gossipsub.outbound.get(peersArray[i])) {
rotate(i)
ineed--
Expand All @@ -217,11 +224,11 @@ export class Heartbeat {
}

// prune the excess peers
peersArray.slice(0, constants.GossipsubD).forEach(prunePeer)
peersArray.slice(0, D).forEach(prunePeer)
}

// do we have enough outbound peers?
if (peers.size >= constants.GossipsubDlo) {
if (peers.size >= Dlo) {
// count the outbound peers we have
let outbound = 0
peers.forEach(p => {
Expand All @@ -231,8 +238,8 @@ export class Heartbeat {
})

// if it's less than D_out, select some peers with outbound connections and graft them
if (outbound < constants.GossipsubDout) {
const ineed = constants.GossipsubDout - outbound
if (outbound < Dout) {
const ineed = Dout - outbound
const backoff = this.gossipsub.backoff.get(topic)
getGossipPeers(this.gossipsub, topic, ineed, (id: string): boolean => {
// filter our current mesh peers, direct peers, peers we are backing off, peers with negative score
Expand Down Expand Up @@ -301,8 +308,8 @@ export class Heartbeat {
})

// do we need more peers?
if (fanoutPeers.size < constants.GossipsubD) {
const ineed = constants.GossipsubD - fanoutPeers.size
if (fanoutPeers.size < D) {
const ineed = D - fanoutPeers.size
const peersSet = getGossipPeers(this.gossipsub, topic, ineed, (id: string): boolean => {
// filter out existing fanout peers, direct peers, and peers with score above the publish threshold
return !fanoutPeers.has(id) &&
Expand Down
42 changes: 36 additions & 6 deletions ts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,30 @@ interface GossipInputOptions {
scoreParams: Partial<PeerScoreParams>
scoreThresholds: Partial<PeerScoreThresholds>
directPeers: AddrInfo[]
/**
* D sets the optimal degree for a Gossipsub topic mesh.
*/
D: number
/**
* Dlo sets the lower bound on the number of peers we keep in a Gossipsub topic mesh.
*/
Dlo: number
/**
* Dhi sets the upper bound on the number of peers we keep in a Gossipsub topic mesh.
*/
Dhi: number
/**
* Dscore affects how peers are selected when pruning a mesh due to over subscription.
*/
Dscore: number
/**
* Dout sets the quota for the number of outbound connections to maintain in a topic mesh.
*/
Dout: number
/**
* Dlazy affects how many peers we will emit gossip to at each heartbeat.
*/
Dlazy: number
}

interface GossipOptions extends GossipInputOptions {
Expand Down Expand Up @@ -83,6 +107,12 @@ class Gossipsub extends BasicPubsub {
fallbackToFloodsub: true,
floodPublish: true,
directPeers: [],
D: constants.GossipsubD,
Dlo: constants.GossipsubDlo,
Dhi: constants.GossipsubDhi,
Dscore: constants.GossipsubDscore,
Dout: constants.GossipsubDout,
Dlazy: constants.GossipsubDlazy,
...options,
scoreParams: createPeerScoreParams(options.scoreParams),
scoreThresholds: createPeerScoreThresholds(options.scoreThresholds)
Expand Down Expand Up @@ -611,7 +641,7 @@ class Gossipsub extends BasicPubsub {
// check the number of mesh peers; if it is at (or over) Dhi, we only accept grafts
// from peers with outbound connections; this is a defensive check to restrict potential
// mesh takeover attacks combined with love bombing
if (peersInMesh.size >= constants.GossipsubDhi && !this.outbound.get(id)) {
if (peersInMesh.size >= this._options.Dhi && !this.outbound.get(id)) {
prune.push(topicID)
this._addBackoff(id, topicID)
return
Expand Down Expand Up @@ -843,9 +873,9 @@ class Gossipsub extends BasicPubsub {
fanoutPeers.delete(id)
}
})
if (fanoutPeers.size < constants.GossipsubD) {
if (fanoutPeers.size < this._options.D) {
// we need more peers; eager, as this would get fixed in the next heartbeat
getGossipPeers(this, topic, constants.GossipsubD - fanoutPeers.size, (id: string): boolean => {
getGossipPeers(this, topic, this._options.D - fanoutPeers.size, (id: string): boolean => {
// filter our current peers, direct peers, and peers with negative scores
return !fanoutPeers.has(id) && !this.direct.has(id) && this.score.score(id) >= 0
}).forEach(id => fanoutPeers.add(id))
Expand All @@ -854,7 +884,7 @@ class Gossipsub extends BasicPubsub {
this.fanout.delete(topic)
this.lastpub.delete(topic)
} else {
const peers = getGossipPeers(this, topic, constants.GossipsubD, (id: string): boolean => {
const peers = getGossipPeers(this, topic, this._options.D, (id: string): boolean => {
// filter direct peers and peers with negative score
return !this.direct.has(id) && this.score.score(id) >= 0
})
Expand Down Expand Up @@ -959,7 +989,7 @@ class Gossipsub extends BasicPubsub {
meshPeers = this.fanout.get(topic)
if (!meshPeers) {
// If we are not in the fanout, then pick peers in topic above the publishThreshold
const peers = getGossipPeers(this, topic, constants.GossipsubD, id => {
const peers = getGossipPeers(this, topic, this._options.D, id => {
return this.score.score(id) >= this._options.scoreThresholds.publishThreshold
})

Expand Down Expand Up @@ -1144,7 +1174,7 @@ class Gossipsub extends BasicPubsub {
}
})

let target = constants.GossipsubDlazy
let target = this._options.Dlazy
const factor = constants.GossipsubGossipFactor * peersToGossip.length
if (factor > target) {
target = factor
Expand Down