Skip to content
Snippets Groups Projects
Commit fb3df67d authored by Cédric Moreau's avatar Cédric Moreau
Browse files

[fix] #1093 Do not try to switch again on a same fork while HEAD remains unchanged

parent d9a51c7f
No related branches found
No related tags found
No related merge requests found
......@@ -21,6 +21,7 @@ export class Switcher<T extends SwitchBlock> {
constructor(
private dao:SwitcherDao<T>,
private invalidForks:string[],
private avgGenTime:number,
private forkWindowSize:number,
private switchOnHeadAdvance:number,
......@@ -36,7 +37,7 @@ export class Switcher<T extends SwitchBlock> {
const numberStart = current.number + this.switchOnHeadAdvance
const timeStart = current.medianTime + this.switchOnHeadAdvance * this.avgGenTime
// Phase 1: find potential chains
const suites = await this.findPotentialSuites(current, numberStart, timeStart)
const suites = await this.findPotentialSuites(numberStart, timeStart)
if (suites.length) {
this.logger && this.logger.info("Fork resolution: %s potential suite(s) found...", suites.length)
}
......@@ -62,19 +63,18 @@ export class Switcher<T extends SwitchBlock> {
async findPotentialSuitesHeads(current:T) {
const numberStart = current.number - this.forkWindowSize
const timeStart = current.medianTime - this.forkWindowSize * this.avgGenTime
const suites = await this.findPotentialSuites(current, numberStart, timeStart)
const suites = await this.findPotentialSuites(numberStart, timeStart)
return suites.map(suite => suite[suite.length - 1])
}
/**
* Looks at the potential blocks that could form fork chains in the sandbox, and sort them to have a maximum of unique
* chains.
* @param {SwitchBlock} current HEAD of local blockchain.
* @param numberStart The minimum number of a fork block.
* @param timeStart The minimum medianTime of a fork block.
* @returns {SwitchBlock[][]} The suites found.
*/
private async findPotentialSuites(current:T, numberStart:number, timeStart:number) {
private async findPotentialSuites(numberStart:number, timeStart:number) {
const suites:T[][] = []
const potentials:T[] = await this.dao.getPotentials(numberStart, timeStart, numberStart + this.forkWindowSize)
const knownForkBlocks:{ [k:string]: boolean } = {}
......@@ -115,6 +115,12 @@ export class Switcher<T extends SwitchBlock> {
previous = await this.dao.getSandboxBlock(previousNumber, previousHash)
if (previous) {
knownForkBlocks[BlockDTO.fromJSONObject(previous).blockstamp] = true
const alreadyKnownInvalidBlock = this.invalidForks.indexOf([previous.number, previous.hash].join('-')) !== -1
if (alreadyKnownInvalidBlock) {
// Incorrect = not found
this.logger && this.logger.info("Fork resolution: block #%s-%s is known as incorrect. Skipping.", previous.number, previous.hash.substr(0, 8))
previous = null
}
}
}
}
......@@ -164,6 +170,7 @@ export class Switcher<T extends SwitchBlock> {
this.logger && this.logger.info("Fork resolution: suite %s/%s added block#%s-%s", j, suites.length, s[i].number, s[i].hash)
successfulBlocks.push(s[i])
} catch (e) {
this.invalidForks.push([s[i].number, s[i].hash].join('-'))
this.logger && this.logger.info("Fork resolution: suite %s/%s REFUSED block#%s: %s", j, suites.length, s[0].number + i, e && e.message)
added = false
}
......@@ -171,6 +178,9 @@ export class Switcher<T extends SwitchBlock> {
}
// Pop the successfuly added blocks
if (successfulBlocks.length) {
for (const b of successfulBlocks) {
this.invalidForks.push([b.number, b.hash].join('-'))
}
const addedToHeadLevel = successfulBlocks[successfulBlocks.length-1].number - current.number
this.logger && this.logger.info("Fork resolution: suite %s/%s reached HEAD + %s. Now rolling back.", j, suites.length, addedToHeadLevel)
await this.dao.revertTo(forkPoint)
......
......@@ -28,6 +28,7 @@ export class BlockchainService extends FIFOService {
selfPubkey:string
quickSynchronizer:QuickSynchronizer
switcherDao:SwitcherDao<BlockDTO>
invalidForks:string[] = []
constructor(private server:any, fifoPromiseHandler:GlobalFifoPromise) {
super(fifoPromiseHandler)
......@@ -119,7 +120,7 @@ export class BlockchainService extends FIFOService {
async branches() {
const current = await this.current()
const switcher = new Switcher(this.switcherDao, this.conf.avgGenTime, this.conf.forksize, this.conf.switchOnHeadAdvance, this.logger)
const switcher = new Switcher(this.switcherDao, this.invalidForks, this.conf.avgGenTime, this.conf.forksize, this.conf.switchOnHeadAdvance, this.logger)
const heads = await switcher.findPotentialSuitesHeads(current)
return heads.concat([current])
}
......@@ -197,6 +198,8 @@ export class BlockchainService extends FIFOService {
bcEvent: OtherConstants.BC_EVENT.HEAD_CHANGED,
block: addedBlock
})
// Clear invalid forks' cache
this.invalidForks.splice(0, this.invalidForks.length)
} catch (e) {
this.logger.error(e)
added = false
......@@ -210,7 +213,7 @@ export class BlockchainService extends FIFOService {
}
async forkResolution() {
const switcher = new Switcher(this.switcherDao, this.conf.avgGenTime, this.conf.forksize, this.conf.switchOnHeadAdvance, this.logger)
const switcher = new Switcher(this.switcherDao, this.invalidForks, this.conf.avgGenTime, this.conf.forksize, this.conf.switchOnHeadAdvance, this.logger)
const newCurrent = await switcher.tryToFork()
if (newCurrent) {
this.push({
......
import * as assert from 'assert'
import * as assert from "assert"
import {SwitchBlock, Switcher, SwitcherDao} from "../../app/lib/blockchain/Switcher"
import {NewLogger} from "../../app/lib/logger"
......@@ -27,7 +27,7 @@ describe("Fork resolution 3-3 algo", () => {
Block.from("C15"),
Block.from("C16")
])
const switcher = new Switcher(new TestingSwitcherDao(bc, sbx), avgGenTime, forkWindowSize, switchOnHeadAdvance, logger)
const switcher = new Switcher(new TestingSwitcherDao(bc, sbx), [], avgGenTime, forkWindowSize, switchOnHeadAdvance, logger)
await switcher.tryToFork()
assert.equal(bc.current.number, 16)
assert.equal(bc.current.hash, "C16")
......@@ -49,7 +49,7 @@ describe("Fork resolution 3-3 algo", () => {
Block.from("C14"),
Block.from("C15")
])
const switcher = new Switcher(new TestingSwitcherDao(bc, sbx), avgGenTime, forkWindowSize, switchOnHeadAdvance)
const switcher = new Switcher(new TestingSwitcherDao(bc, sbx), [], avgGenTime, forkWindowSize, switchOnHeadAdvance)
await switcher.tryToFork()
assert.equal(bc.current.number, 13)
assert.equal(bc.current.hash, "B13")
......@@ -69,7 +69,7 @@ describe("Fork resolution 3-3 algo", () => {
Block.from("C14"),
Block.from("C15")
])
const switcher = new Switcher(new TestingSwitcherDao(bc, sbx), avgGenTime, forkWindowSize, switchOnHeadAdvance)
const switcher = new Switcher(new TestingSwitcherDao(bc, sbx), [], avgGenTime, forkWindowSize, switchOnHeadAdvance)
await switcher.tryToFork()
assert.equal(bc.current.number, 13)
assert.equal(bc.current.hash, "B13")
......@@ -94,7 +94,7 @@ describe("Fork resolution 3-3 algo", () => {
Block.from("C15"),
Block.from("C16")
])
const switcher = new Switcher(new TestingSwitcherDao(bc, sbx), avgGenTime, forkWindowSize, switchOnHeadAdvance)
const switcher = new Switcher(new TestingSwitcherDao(bc, sbx), [], avgGenTime, forkWindowSize, switchOnHeadAdvance)
await switcher.tryToFork()
assert.equal(bc.current.number, 13)
assert.equal(bc.current.hash, "B13")
......@@ -118,7 +118,7 @@ describe("Fork resolution 3-3 algo", () => {
Block.from("C15"),
Block.from("C16")
])
const switcher = new Switcher(new TestingSwitcherDao(bc, sbx), avgGenTime, forkWindowSize, switchOnHeadAdvance)
const switcher = new Switcher(new TestingSwitcherDao(bc, sbx), [], avgGenTime, forkWindowSize, switchOnHeadAdvance)
await switcher.tryToFork()
assert.equal(bc.current.number, 13)
assert.equal(bc.current.hash, "B13")
......@@ -141,7 +141,7 @@ describe("Fork resolution 3-3 algo", () => {
Block.from("C15"),
Block.from("C16")
])
const switcher = new Switcher(new TestingSwitcherDao(bc, sbx), avgGenTime, forkWindowSize, switchOnHeadAdvance)
const switcher = new Switcher(new TestingSwitcherDao(bc, sbx), [], avgGenTime, forkWindowSize, switchOnHeadAdvance)
await switcher.tryToFork()
assert.equal(bc.current.number, 13)
assert.equal(bc.current.hash, "B13")
......@@ -170,7 +170,7 @@ describe("Fork resolution 3-3 algo", () => {
Block.from("E14"),
Block.from("E15")
])
const switcher = new Switcher(new TestingSwitcherDao(bc, sbx), avgGenTime, forkWindowSize, 1)
const switcher = new Switcher(new TestingSwitcherDao(bc, sbx), [], avgGenTime, forkWindowSize, 1)
await switcher.tryToFork()
assert.equal(16, bc.current.number)
assert.equal("D16", bc.current.hash)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment