Skip to content

Commit 8920da2

Browse files
committed
Merge branch 'skipLedgersChains' into deadlock_manifests
2 parents 003afbe + f71a4a3 commit 8920da2

File tree

6 files changed

+902
-5
lines changed

6 files changed

+902
-5
lines changed

src/connection-manager/chains.ts

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,12 @@ function addLedgerToChain(ledger: Ledger, chain: Chain): void {
3131
chain.validators.add(validator)
3232
}
3333

34-
chain.current = ledger.ledger_index
35-
chain.updated = ledger.first_seen
34+
// is the incoming ledger more recent than the current chain's tip (i.e. chain.current)? If so, update the chain's tip.
35+
chain.current = ledger.ledger_index > chain.current ? ledger.ledger_index : chain.current
36+
chain.updated = ledger.ledger_index > chain.current ? ledger.first_seen : chain.updated
37+
38+
log.info(`Ledgers in the chain ${chain.id}: ${Array.from(chain.ledgers)}}`)
39+
log.info(`Validators belonging to the chain ${chain.id}: ${Array.from(chain.validators)}}`)
3640
}
3741

3842
/**
@@ -231,6 +235,7 @@ class Chains {
231235
* @param ledger - The Ledger being handled in order to update the chains.
232236
*/
233237
private updateChains(ledger: Ledger): void {
238+
log.info(`Updating chains for ledger ${JSON.stringify(ledger)}`)
234239
const next = ledger.ledger_index
235240
const validators = ledger.validations
236241

@@ -243,6 +248,7 @@ class Chains {
243248
.shift()
244249

245250
if (chainAtNextIndex !== undefined) {
251+
log.info(`Adding ledger ${ledger.ledger_hash} to chain ${chainAtNextIndex.id} (chain at next index)`)
246252
addLedgerToChain(ledger, chainAtNextIndex)
247253
return
248254
}
@@ -256,6 +262,7 @@ class Chains {
256262
.shift()
257263

258264
if (chainAtThisIndex !== undefined) {
265+
log.info(`Found this ledger ${ledger.ledger_hash} in chain ${chainAtThisIndex.id}. No action taken.`)
259266
return
260267
}
261268

@@ -269,19 +276,32 @@ class Chains {
269276

270277
if (chainWithThisValidator !== undefined) {
271278
const skipped = ledger.ledger_index - chainWithThisValidator.current
272-
log.warn(`Possibly skipped ${skipped} ledgers`)
273-
if (skipped > 1 && skipped < 20) {
279+
log.warn(`Processing ledger ${ledger.ledger_hash}: Discovered chain with an overlap of validators ${chainWithThisValidator.id}. Possibly skipped ${skipped} ledgers`)
280+
if (skipped !== 0) {
281+
log.info(`Adding ledger ${ledger.ledger_hash} to chain ${chainWithThisValidator.id} (chain with an overlap of validators); Existing chain id: ${JSON.stringify(chainWithThisValidator)}`)
274282
chainWithThisValidator.incomplete = true
275283
addLedgerToChain(ledger, chainWithThisValidator)
276284
}
277285
}
278286

279287
if (chainWithThisValidator !== undefined || chainWithLedger !== undefined) {
288+
log.info(`No action taken for ledger ${ledger.ledger_hash}; \nINFO: Chain with an overlap of validators: ${chainWithThisValidator?.id};\n Chain that contains this ledger: ${chainWithLedger?.id}`)
280289
return
281290
}
282291

292+
log.info(`Adding new chain for ledger ${ledger.ledger_hash}`)
283293
this.addNewChain(ledger)
284294
}
295+
296+
/**
297+
*
298+
* VHS tests use this method to reset the chains singleton value. This ensures that tests are not contaminating each other.
299+
*/
300+
public __resetChainsSingletonForTests(): void {
301+
this.ledgersByHash.clear()
302+
this.chains = []
303+
this.index = 0
304+
}
285305
}
286306

287307
let chains: Chains | undefined

test/chains/chains.test.ts

Lines changed: 100 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
import chains from '../../src/connection-manager/chains'
22
import { destroy, query, setupTables } from '../../src/shared/database'
3-
3+
import skippedLedgersValidations from './fixtures/skipped-ledgers-validations.json'
4+
import outOfOrderValidations from './fixtures/out-of-order-validations.json'
45
import validations from './fixtures/all-validations.json'
6+
import { Chain } from '../../src/shared/types'
7+
import duplicateValidations from './fixtures/duplicate-validations.json'
8+
import newValidatorsVotes from './fixtures/new-validators-votes.json'
59

610
jest.useFakeTimers()
711

@@ -17,6 +21,7 @@ describe('Creates chains', () => {
1721
beforeEach(async () => {
1822
await query('connection_health').delete('*')
1923
await query('crawls').delete('*')
24+
chains.__resetChainsSingletonForTests()
2025
})
2126

2227
test('Perfect validation scores', async () => {
@@ -43,6 +48,17 @@ describe('Creates chains', () => {
4348
})
4449

4550
test('Purge chains removes ledgers', async () => {
51+
// This test validates the behavior of purgeChains. As a pre-requisite, assign sample ledgers into appropriate chain-IDs before the purge operation.
52+
for (const validation of validations) {
53+
chains.updateLedgers(validation)
54+
}
55+
// Mock date.now
56+
const time = Date.now() + 11000
57+
Date.now = (): number => time
58+
59+
// assign ledgers into the appropriate chains
60+
chains.calculateChainsFromLedgers()
61+
4662
await chains.purgeChains()
4763

4864
const constructed: Array<{
@@ -56,4 +72,87 @@ describe('Creates chains', () => {
5672
expect(constructed[0].validators).toContain('VALIDATOR2')
5773
expect(constructed[0].validators).toContain('VALIDATOR3')
5874
})
75+
76+
test(`Validate the case where VHS misses > 20 ledgers`, async() => {
77+
for (const validation of skippedLedgersValidations) {
78+
chains.updateLedgers(validation)
79+
}
80+
81+
const time = Date.now() + 11000
82+
83+
// Mock date.now
84+
Date.now = (): number => time
85+
const updatedChains = chains.calculateChainsFromLedgers()
86+
expect(updatedChains).toHaveLength(1)
87+
88+
expect(updatedChains[0].id).toBe('chain.0')
89+
expect(updatedChains[0].current).toBe(38)
90+
expect(updatedChains[0].first).toBe(1)
91+
expect(updatedChains[0].validators).toEqual(new Set(['VALIDATOR1', 'VALIDATOR2', 'VALIDATOR3']))
92+
// Note: It is fragile to test the time when the chain was updated with the latest values.
93+
expect(updatedChains[0].ledgers).toEqual(new Set(['LEDGER1', 'LEDGER2', 'LEDGER3', 'LEDGER33', 'LEDGER34', 'LEDGER35', 'LEDGER36', 'LEDGER37', 'LEDGER38']))
94+
expect(updatedChains[0].incomplete).toBe(true)
95+
})
96+
97+
test(`Simulate the case where VHS misses <0 ledgers (VHS receives out-of-sync historical ledger)`, async () => {
98+
for(const validation of outOfOrderValidations) {
99+
chains.updateLedgers(validation)
100+
}
101+
102+
const time = Date.now() + 11000
103+
104+
// Mock date.now
105+
Date.now = (): number => time
106+
const constructed: Array<Chain> = chains.calculateChainsFromLedgers()
107+
108+
expect(constructed).toHaveLength(1)
109+
expect(constructed[0].id).toBe('chain.0')
110+
expect(constructed[0].current).toBe(38)
111+
expect(constructed[0].first).toBe(1)
112+
expect(constructed[0].validators).toEqual(new Set(['VALIDATOR1', 'VALIDATOR2', 'VALIDATOR3']))
113+
expect(constructed[0].ledgers).toEqual(new Set(['LEDGER1', 'LEDGER2', 'LEDGER3', 'LEDGER33', 'LEDGER34', 'LEDGER35', 'LEDGER36', 'LEDGER37', 'LEDGER38', 'LEDGER20', 'LEDGER11']))
114+
expect(constructed[0].incomplete).toBe(true)
115+
})
116+
117+
test(`VHS receives identical copies of the validationReceived message`, async() => {
118+
for (const validation of duplicateValidations) {
119+
chains.updateLedgers(validation)
120+
}
121+
122+
const time = Date.now() + 11000
123+
124+
// Mock date.now
125+
Date.now = (): number => time
126+
const constructed: Array<Chain> = chains.calculateChainsFromLedgers()
127+
128+
expect(constructed).toHaveLength(1)
129+
// Note: duplicate validations are ignored.
130+
expect(constructed[0].ledgers).toEqual(new Set(['LEDGER1', 'LEDGER2', 'LEDGER3']))
131+
expect(constructed[0].validators).toEqual(new Set(['VALIDATOR1', 'VALIDATOR2', 'VALIDATOR3']))
132+
expect(constructed[0].id).toBe('chain.0')
133+
expect(constructed[0].current).toBe(3)
134+
expect(constructed[0].first).toBe(1)
135+
expect(constructed[0].incomplete).toBe(true)
136+
})
137+
138+
test(`Simulate the inclusion of two new validators in the validationReceived messages`, async() => {
139+
for (const validation of newValidatorsVotes) {
140+
chains.updateLedgers(validation)
141+
}
142+
143+
const time = Date.now() + 11000
144+
145+
// Mock date.now
146+
Date.now = (): number => time
147+
const constructed: Array<Chain> = chains.calculateChainsFromLedgers()
148+
149+
expect(constructed).toHaveLength(1)
150+
// Note: duplicate validations are ignored.
151+
expect(constructed[0].ledgers).toEqual(new Set(['LEDGER1', 'LEDGER2', 'LEDGER3', 'LEDGER4']))
152+
expect(constructed[0].validators).toEqual(new Set(['VALIDATOR1', 'VALIDATOR2', 'VALIDATOR3', 'VALIDATOR4', 'VALIDATOR5']))
153+
expect(constructed[0].id).toBe('chain.0')
154+
expect(constructed[0].current).toBe(4)
155+
expect(constructed[0].first).toBe(1)
156+
expect(constructed[0].incomplete).toBe(true)
157+
})
59158
})
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
[
2+
{
3+
"flags": 2147483649,
4+
"full": true,
5+
"ledger_hash": "LEDGER1",
6+
"ledger_index": "1",
7+
"master_key": "VALIDATOR1MASTER",
8+
"signature": "30440220342DFBFBA1ACF758805A1CD5FF0C4E39F0A2800D0400F430A22BEBDB2B9E327A02204776C0E90942FB9CACDB763535AFAADBA1506E94CD92A605296153D8362D01E3",
9+
"signing_time": 669928656,
10+
"type": "validationReceived",
11+
"validation_public_key": "VALIDATOR1"
12+
},
13+
{
14+
"flags": 2147483649,
15+
"full": true,
16+
"ledger_hash": "LEDGER1",
17+
"ledger_index": "1",
18+
"master_key": "VALIDATOR2MASTER",
19+
"signature": "30440220342DFBFBA1ACF758805A1CD5FF0C4E39F0A2800D0400F430A22BEBDB2B9E327A02204776C0E90942FB9CACDB763535AFAADBA1506E94CD92A605296153D8362D01E3",
20+
"signing_time": 669928656,
21+
"type": "validationReceived",
22+
"validation_public_key": "VALIDATOR2"
23+
},
24+
{
25+
"flags": 2147483649,
26+
"full": true,
27+
"ledger_hash": "LEDGER1",
28+
"ledger_index": "1",
29+
"master_key": "VALIDATOR3MASTER",
30+
"signature": "30440220342DFBFBA1ACF758805A1CD5FF0C4E39F0A2800D0400F430A22BEBDB2B9E327A02204776C0E90942FB9CACDB763535AFAADBA1506E94CD92A605296153D8362D01E3",
31+
"signing_time": 669928656,
32+
"type": "validationReceived",
33+
"validation_public_key": "VALIDATOR3"
34+
},
35+
{
36+
"flags": 2147483649,
37+
"full": true,
38+
"ledger_hash": "LEDGER2",
39+
"ledger_index": "2",
40+
"master_key": "VALIDATOR1MASTER",
41+
"signature": "30440220342DFBFBA1ACF758805A1CD5FF0C4E39F0A2800D0400F430A22BEBDB2B9E327A02204776C0E90942FB9CACDB763535AFAADBA1506E94CD92A605296153D8362D01E3",
42+
"signing_time": 669928656,
43+
"type": "validationReceived",
44+
"validation_public_key": "VALIDATOR1"
45+
},
46+
{
47+
"flags": 2147483649,
48+
"full": true,
49+
"ledger_hash": "LEDGER2",
50+
"ledger_index": "2",
51+
"master_key": "VALIDATOR2MASTER",
52+
"signature": "30440220342DFBFBA1ACF758805A1CD5FF0C4E39F0A2800D0400F430A22BEBDB2B9E327A02204776C0E90942FB9CACDB763535AFAADBA1506E94CD92A605296153D8362D01E3",
53+
"signing_time": 669928656,
54+
"type": "validationReceived",
55+
"validation_public_key": "VALIDATOR2"
56+
},
57+
{
58+
"flags": 2147483649,
59+
"full": true,
60+
"ledger_hash": "LEDGER2",
61+
"ledger_index": "2",
62+
"master_key": "VALIDATOR3MASTER",
63+
"signature": "30440220342DFBFBA1ACF758805A1CD5FF0C4E39F0A2800D0400F430A22BEBDB2B9E327A02204776C0E90942FB9CACDB763535AFAADBA1506E94CD92A605296153D8362D01E3",
64+
"signing_time": 669928656,
65+
"type": "validationReceived",
66+
"validation_public_key": "VALIDATOR3"
67+
},
68+
{
69+
"flags": 2147483649,
70+
"full": true,
71+
"ledger_hash": "LEDGER3",
72+
"ledger_index": "3",
73+
"master_key": "VALIDATOR1MASTER",
74+
"signature": "30440220342DFBFBA1ACF758805A1CD5FF0C4E39F0A2800D0400F430A22BEBDB2B9E327A02204776C0E90942FB9CACDB763535AFAADBA1506E94CD92A605296153D8362D01E3",
75+
"signing_time": 669928656,
76+
"type": "validationReceived",
77+
"validation_public_key": "VALIDATOR1"
78+
},
79+
{
80+
"flags": 2147483649,
81+
"full": true,
82+
"ledger_hash": "LEDGER3",
83+
"ledger_index": "3",
84+
"master_key": "VALIDATOR2MASTER",
85+
"signature": "30440220342DFBFBA1ACF758805A1CD5FF0C4E39F0A2800D0400F430A22BEBDB2B9E327A02204776C0E90942FB9CACDB763535AFAADBA1506E94CD92A605296153D8362D01E3",
86+
"signing_time": 669928656,
87+
"type": "validationReceived",
88+
"validation_public_key": "VALIDATOR2"
89+
},
90+
{
91+
"flags": 2147483649,
92+
"full": true,
93+
"ledger_hash": "LEDGER3",
94+
"ledger_index": "3",
95+
"master_key": "VALIDATOR3MASTER",
96+
"signature": "30440220342DFBFBA1ACF758805A1CD5FF0C4E39F0A2800D0400F430A22BEBDB2B9E327A02204776C0E90942FB9CACDB763535AFAADBA1506E94CD92A605296153D8362D01E3",
97+
"signing_time": 669928656,
98+
"type": "validationReceived",
99+
"validation_public_key": "VALIDATOR3"
100+
},
101+
{
102+
"flags": 2147483649,
103+
"full": true,
104+
"ledger_hash": "LEDGER1",
105+
"ledger_index": "1",
106+
"master_key": "VALIDATOR1MASTER",
107+
"signature": "30440220342DFBFBA1ACF758805A1CD5FF0C4E39F0A2800D0400F430A22BEBDB2B9E327A02204776C0E90942FB9CACDB763535AFAADBA1506E94CD92A605296153D8362D01E3",
108+
"signing_time": 669928656,
109+
"type": "validationReceived",
110+
"validation_public_key": "VALIDATOR1"
111+
},
112+
{
113+
"flags": 2147483649,
114+
"full": true,
115+
"ledger_hash": "LEDGER2",
116+
"ledger_index": "2",
117+
"master_key": "VALIDATOR1MASTER",
118+
"signature": "30440220342DFBFBA1ACF758805A1CD5FF0C4E39F0A2800D0400F430A22BEBDB2B9E327A02204776C0E90942FB9CACDB763535AFAADBA1506E94CD92A605296153D8362D01E3",
119+
"signing_time": 669928656,
120+
"type": "validationReceived",
121+
"validation_public_key": "VALIDATOR1"
122+
},
123+
{
124+
"flags": 2147483649,
125+
"full": true,
126+
"ledger_hash": "LEDGER3",
127+
"ledger_index": "3",
128+
"master_key": "VALIDATOR2MASTER",
129+
"signature": "30440220342DFBFBA1ACF758805A1CD5FF0C4E39F0A2800D0400F430A22BEBDB2B9E327A02204776C0E90942FB9CACDB763535AFAADBA1506E94CD92A605296153D8362D01E3",
130+
"signing_time": 669928656,
131+
"type": "validationReceived",
132+
"validation_public_key": "VALIDATOR2"
133+
}
134+
]

0 commit comments

Comments
 (0)