/
chaos.js
102 lines (87 loc) · 2.91 KB
/
chaos.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
const crypto = require('hypercore-crypto')
const random = require('math-random-seed')
const { timeout } = require('nonsynchronous')
const Hyperswarm = require('..')
const { test, destroyAll } = require('./helpers')
const BACKOFFS = [
100,
200,
300
]
test('chaos - recovers after random disconnections (takes ~60s)', async (bootstrap, t) => {
const SEED = 'hyperswarm v3'
const NUM_SWARMS = 10
const NUM_TOPICS = 15
const NUM_FORCE_DISCONNECTS = 30
const STARTUP_DURATION = 1000 * 5
const TEST_DURATION = 1000 * 45
const CHAOS_DURATION = 1000 * 10
const swarms = []
const topics = []
const connections = []
const peersBySwarm = new Map()
const rand = random(SEED)
for (let i = 0; i < NUM_SWARMS; i++) {
const swarm = new Hyperswarm({ bootstrap, backoffs: BACKOFFS, jitter: 0 })
swarms.push(swarm)
peersBySwarm.set(swarm, new Set())
swarm.on('connection', conn => {
connections.push(conn)
conn.on('error', noop)
conn.on('close', () => {
clearInterval(timer)
const idx = connections.indexOf(conn)
if (idx === -1) return
connections.splice(idx, 1)
})
const timer = setInterval(() => {
conn.write(Buffer.alloc(10))
}, 100)
conn.write(Buffer.alloc(10))
})
}
for (let i = 0; i < NUM_TOPICS; i++) {
const topic = crypto.randomBytes(32)
topics.push(topic)
}
for (const topic of topics) {
const numSwarms = Math.round(rand() * NUM_SWARMS)
const topicSwarms = []
for (let i = 0; i < numSwarms; i++) {
topicSwarms.push(swarms[Math.floor(rand() * NUM_SWARMS)])
}
for (const swarm of topicSwarms) {
const peers = peersBySwarm.get(swarm)
for (const s of topicSwarms) {
if (swarm === s) continue
peers.add(s.keyPair.publicKey.toString('hex'))
}
await swarm.join(topic).flushed()
}
}
await Promise.all(swarms.map(s => s.flush()))
await timeout(STARTUP_DURATION)
// Randomly destroy connections during the chaos period.
for (let i = 0; i < NUM_FORCE_DISCONNECTS; i++) {
const timeout = Math.floor(rand() * CHAOS_DURATION) // Leave a lot of room at the end for reestablishing connections (timeouts)
setTimeout(() => {
if (!connections.length) return
const idx = Math.floor(rand() * connections.length)
const conn = connections[idx]
conn.destroy()
}, timeout)
}
await timeout(TEST_DURATION) // Wait for the chaos to resolve
for (const [swarm, expectedPeers] of peersBySwarm) {
t.same(swarm.connections.size, expectedPeers.size, 'swarm has the correct number of connections')
const missingKeys = []
for (const conn of swarm.connections) {
const key = conn.remotePublicKey.toString('hex')
if (!expectedPeers.has(key)) missingKeys.push(key)
}
t.same(missingKeys.length, 0, 'swarm is not missing any expected peers')
}
await destroyAll(...swarms)
t.end()
})
function noop () {}