Fix integer overflow issue in sync_manager. (#2564)

* Make Refactor rewind point assignment more concrete.

* Fix overflow issue in getRewindPoint().
Add tests.
This commit is contained in:
Eugene Kabanov 2021-05-18 13:25:14 +03:00 committed by GitHub
parent cf06c4e87e
commit 5b5ea2e813
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 124 additions and 19 deletions

View File

@ -15,7 +15,8 @@ import ../spec/[datatypes, digest, helpers, eth2_apis/callsigs_types],
import ../gossip_processing/gossip_to_consensus
import ../consensus_object_pools/block_pools_types
export datatypes, digest, chronos, chronicles, results, block_pools_types
export datatypes, digest, chronos, chronicles, results, block_pools_types,
helpers
logScope:
topics = "syncman"
@ -432,6 +433,12 @@ proc toDebtsQueue[T](sq: SyncQueue[T], sr: SyncRequest[T]) =
proc getRewindPoint*[T](sq: SyncQueue[T], failSlot: Slot,
finalizedSlot: Slot): Slot =
# Calculate the latest finalized epoch.
let finalizedEpoch = compute_epoch_at_slot(finalizedSlot)
# Calculate failure epoch.
let failEpoch = compute_epoch_at_slot(failSlot)
# Calculate exponential rewind point in number of epochs.
let epochCount =
if sq.rewind.isSome():
@ -439,33 +446,73 @@ proc getRewindPoint*[T](sq: SyncQueue[T], failSlot: Slot,
if failSlot == rewind.failSlot:
# `MissingParent` happened at same slot so we increase rewind point by
# factor of 2.
let epochs = rewind.epochCount * 2
sq.rewind = some(RewindPoint(failSlot: failSlot, epochCount: epochs))
epochs
if failEpoch > finalizedEpoch:
let rewindPoint = rewind.epochCount shl 1
if rewindPoint < rewind.epochCount:
# If exponential rewind point produces `uint64` overflow we will
# make rewind to latest finalized epoch.
failEpoch - finalizedEpoch
else:
if (failEpoch < rewindPoint) or
(failEpoch - rewindPoint < finalizedEpoch):
# If exponential rewind point points to position which is far
# behind latest finalized epoch.
failEpoch - finalizedEpoch
else:
rewindPoint
else:
warn "Trying to rewind over the last finalized epoch",
finalized_slot = finalizedSlot, fail_slot = failSlot,
finalized_epoch = finalizedEpoch, fail_epoch = failEpoch,
rewind_epoch_count = rewind.epochCount,
finalized_epoch = finalizedEpoch
0'u64
else:
# `MissingParent` happened at different slot so we going to rewind for
# 1 epoch only.
sq.rewind = some(RewindPoint(failSlot: failSlot, epochCount: 1'u64))
1'u64
if (failEpoch < 1'u64) or (failEpoch - 1'u64 < finalizedEpoch):
warn "Сould not rewind further than the last finalized epoch",
finalized_slot = finalizedSlot, fail_slot = failSlot,
finalized_epoch = finalizedEpoch, fail_epoch = failEpoch,
rewind_epoch_count = rewind.epochCount,
finalized_epoch = finalizedEpoch
0'u64
else:
1'u64
else:
# `MissingParent` happened first time.
sq.rewind = some(RewindPoint(failSlot: failSlot, epochCount: 1'u64))
1'u64
if (failEpoch < 1'u64) or (failEpoch - 1'u64 < finalizedEpoch):
warn "Сould not rewind further than the last finalized epoch",
finalized_slot = finalizedSlot, fail_slot = failSlot,
finalized_epoch = finalizedEpoch, fail_epoch = failEpoch,
finalized_epoch = finalizedEpoch
0'u64
else:
1'u64
# Calculate the latest finalized epoch.
let finalizedEpoch = compute_epoch_at_slot(finalizedSlot)
# echo "epochCount = ", epochCount
# Calculate the rewind epoch, which should not be less than the latest
# finalized epoch.
let rewindEpoch =
block:
let failEpoch = compute_epoch_at_slot(failSlot)
if failEpoch < finalizedEpoch + epochCount:
if epochCount == 0'u64:
warn "Unable to continue syncing, please restart the node",
finalized_slot = finalizedSlot, fail_slot = failSlot,
finalized_epoch = finalizedEpoch, fail_epoch = failEpoch,
finalized_epoch = finalizedEpoch
# Calculate the rewind epoch, which will be equal to last rewind point or
# finalizedEpoch
let rewindEpoch =
if sq.rewind.isNone():
finalizedEpoch
else:
failEpoch - epochCount
compute_start_slot_at_epoch(rewindEpoch)
compute_epoch_at_slot(sq.rewind.get().failSlot) -
sq.rewind.get().epochCount
compute_start_slot_at_epoch(rewindEpoch)
else:
# Calculate the rewind epoch, which should not be less than the latest
# finalized epoch.
let rewindEpoch = failEpoch - epochCount
# Update and save new rewind point in SyncQueue.
sq.rewind = some(RewindPoint(failSlot: failSlot, epochCount: epochCount))
compute_start_slot_at_epoch(rewindEpoch)
proc push*[T](sq: SyncQueue[T], sr: SyncRequest[T],
data: seq[SignedBeaconBlock]) {.async, gcsafe.} =

View File

@ -1,5 +1,6 @@
{.used.}
import std/strutils
import unittest2
import chronos
import ../beacon_chain/gossip_processing/gossip_to_consensus,
@ -515,3 +516,60 @@ suite "SyncManager test suite":
checkResponse(r22, @[chain[4], chain[5]]) == false
checkResponse(r22, @[chain[4]]) == false
checkResponse(r22, @[chain[3], chain[1]]) == false
test "[SyncQueue] getRewindPoint() test":
let aq = newVerifQueues()
block:
var queue = SyncQueue.init(SomeTPeer,
Slot(0), Slot(0xFFFF_FFFF_FFFF_FFFFF'u64),
1'u64, getFirstSlotAtFinalizedEpoch, aq, 2)
let finalizedSlot = compute_start_slot_at_epoch(Epoch(0'u64))
let startSlot = compute_start_slot_at_epoch(Epoch(0'u64)) + 1'u64
let finishSlot = compute_start_slot_at_epoch(Epoch(2'u64))
for i in uint64(startSlot) ..< uint64(finishSlot):
check queue.getRewindPoint(Slot(i), finalizedSlot) == finalizedSlot
block:
var queue = SyncQueue.init(SomeTPeer,
Slot(0), Slot(0xFFFF_FFFF_FFFF_FFFFF'u64),
1'u64, getFirstSlotAtFinalizedEpoch, aq, 2)
let finalizedSlot = compute_start_slot_at_epoch(Epoch(1'u64))
let startSlot = compute_start_slot_at_epoch(Epoch(1'u64)) + 1'u64
let finishSlot = compute_start_slot_at_epoch(Epoch(3'u64))
for i in uint64(startSlot) ..< uint64(finishSlot) :
check queue.getRewindPoint(Slot(i), finalizedSlot) == finalizedSlot
block:
var queue = SyncQueue.init(SomeTPeer,
Slot(0), Slot(0xFFFF_FFFF_FFFF_FFFFF'u64),
1'u64, getFirstSlotAtFinalizedEpoch, aq, 2)
let finalizedSlot = compute_start_slot_at_epoch(Epoch(0'u64))
let failSlot = Slot(0xFFFF_FFFF_FFFF_FFFFF'u64)
let failEpoch = compute_epoch_at_slot(failSlot)
var counter = 1'u64
for i in 0 ..< 64:
if counter >= failEpoch:
break
let rewindEpoch = failEpoch - counter
let rewindSlot = compute_start_slot_at_epoch(rewindEpoch)
check queue.getRewindPoint(failSlot, finalizedSlot) == rewindSlot
counter = counter shl 1
block:
var queue = SyncQueue.init(SomeTPeer,
Slot(0), Slot(0xFFFF_FFFF_FFFF_FFFFF'u64),
1'u64, getFirstSlotAtFinalizedEpoch, aq, 2)
let finalizedSlot = compute_start_slot_at_epoch(Epoch(1'u64))
let failSlot = Slot(0xFFFF_FFFF_FFFF_FFFFF'u64)
let failEpoch = compute_epoch_at_slot(failSlot)
var counter = 1'u64
for i in 0 ..< 64:
if counter >= failEpoch:
break
let rewindEpoch = failEpoch - counter
let rewindSlot = compute_start_slot_at_epoch(rewindEpoch)
check queue.getRewindPoint(failSlot, finalizedSlot) == rewindSlot
counter = counter shl 1