more block range request updates
* handle skip == 0 gracefully * avoid memory allocation at expense of more complex API * add more tests * log block request results
This commit is contained in:
parent
fccce85f0d
commit
da988e4e40
|
@ -450,33 +450,42 @@ func getRef*(pool: BlockPool, root: Eth2Digest): BlockRef =
|
||||||
pool.blocks.getOrDefault(root, nil)
|
pool.blocks.getOrDefault(root, nil)
|
||||||
|
|
||||||
proc getBlockRange*(
|
proc getBlockRange*(
|
||||||
pool: BlockPool, startSlot: Slot, count, skipStep: Natural): seq[BlockRef] =
|
pool: BlockPool, startSlot: Slot, skipStep: Natural,
|
||||||
|
output: var openArray[BlockRef]): Natural =
|
||||||
## This function populates an `output` buffer of blocks
|
## This function populates an `output` buffer of blocks
|
||||||
## with a range starting from `startSlot` and skipping
|
## with a slots ranging from `startSlot` up to, but not including,
|
||||||
## every `skipTest` number of blocks.
|
## `startSlot + skipStep * output.len`, skipping any slots that don't have
|
||||||
|
## a block.
|
||||||
##
|
##
|
||||||
|
## Blocks will be written to `output` from the end without gaps, even if
|
||||||
|
## a block is missing in a particular slot. The return value shows how
|
||||||
|
## many slots were missing blocks - to iterate over the result, start
|
||||||
|
## at this index.
|
||||||
|
##
|
||||||
|
## If there were no blocks in the range, `output.len` will be returned.
|
||||||
|
let count = output.len
|
||||||
trace "getBlockRange entered",
|
trace "getBlockRange entered",
|
||||||
head = shortLog(pool.head.blck.root), count, startSlot, skipStep
|
head = shortLog(pool.head.blck.root), count, startSlot, skipStep
|
||||||
|
|
||||||
if startSlot > pool.head.blck.slot:
|
|
||||||
debug "Range request for future slot",
|
|
||||||
head = shortLog(pool.head.blck.slot), count, startSlot, skipStep
|
|
||||||
return
|
|
||||||
|
|
||||||
let
|
let
|
||||||
|
skipStep = max(1, skipStep) # Treat 0 step as 1
|
||||||
endSlot = startSlot + uint64(count * skipStep)
|
endSlot = startSlot + uint64(count * skipStep)
|
||||||
|
|
||||||
var
|
var
|
||||||
ret = newSeq[BlockRef](count)
|
|
||||||
b = pool.head.blck.atSlot(endSlot)
|
b = pool.head.blck.atSlot(endSlot)
|
||||||
|
o = count
|
||||||
for i in 0..<count:
|
for i in 0..<count:
|
||||||
for j in 0..<skipStep:
|
for j in 0..<skipStep:
|
||||||
b = b.parent
|
b = b.parent
|
||||||
if b.blck.slot == b.slot:
|
if b.blck.slot == b.slot:
|
||||||
ret[ret.len - i - 1] = b.blck
|
output[o - 1] = b.blck
|
||||||
|
dec o
|
||||||
|
|
||||||
ret
|
# Make sure the given input is cleared, just in case
|
||||||
|
for i in 0..<o:
|
||||||
|
output[i] = nil
|
||||||
|
|
||||||
|
o # Return the index of the first non-nil item in the output
|
||||||
|
|
||||||
func getBlockBySlot*(pool: BlockPool, slot: Slot): BlockRef =
|
func getBlockBySlot*(pool: BlockPool, slot: Slot): BlockRef =
|
||||||
## Retrieves the first block in the current canonical chain
|
## Retrieves the first block in the current canonical chain
|
||||||
|
|
|
@ -134,13 +134,22 @@ p2pProtocol BeaconSync(version = 1,
|
||||||
trace "got range request", peer, startSlot, count, step
|
trace "got range request", peer, startSlot, count, step
|
||||||
|
|
||||||
if count > 0'u64:
|
if count > 0'u64:
|
||||||
|
var blocks: array[MAX_REQUESTED_BLOCKS, BlockRef]
|
||||||
let
|
let
|
||||||
count = if step != 0: min(count, MAX_REQUESTED_BLOCKS.uint64) else: 1
|
|
||||||
pool = peer.networkState.blockPool
|
pool = peer.networkState.blockPool
|
||||||
for b in pool.getBlockRange(startSlot, count, step):
|
# Limit number of blocks in response
|
||||||
if not b.isNil:
|
count = min(count.Natural, blocks.len)
|
||||||
trace "wrote response block", slot = b.slot, roor = shortLog(b.root)
|
|
||||||
await response.write(pool.get(b).data)
|
let startIndex =
|
||||||
|
pool.getBlockRange(startSlot, step, blocks.toOpenArray(0, count - 1))
|
||||||
|
|
||||||
|
for b in blocks[startIndex..^1]:
|
||||||
|
doAssert not b.isNil, "getBlockRange should return non-nil blocks only"
|
||||||
|
trace "wrote response block", slot = b.slot, roor = shortLog(b.root)
|
||||||
|
await response.write(pool.get(b).data)
|
||||||
|
|
||||||
|
debug "Block range request done",
|
||||||
|
peer, startSlot, count, step, found = count - startIndex
|
||||||
|
|
||||||
proc beaconBlocksByRoot(
|
proc beaconBlocksByRoot(
|
||||||
peer: Peer,
|
peer: Peer,
|
||||||
|
@ -149,10 +158,15 @@ p2pProtocol BeaconSync(version = 1,
|
||||||
let
|
let
|
||||||
pool = peer.networkState.blockPool
|
pool = peer.networkState.blockPool
|
||||||
|
|
||||||
|
var found = 0
|
||||||
for root in blockRoots:
|
for root in blockRoots:
|
||||||
let blockRef = pool.getRef(root)
|
let blockRef = pool.getRef(root)
|
||||||
if not isNil(blockRef):
|
if not isNil(blockRef):
|
||||||
await response.write(pool.get(blockRef).data)
|
await response.write(pool.get(blockRef).data)
|
||||||
|
inc found
|
||||||
|
|
||||||
|
debug "Block root request done",
|
||||||
|
peer, roots = blockRoots.len, found
|
||||||
|
|
||||||
proc beaconBlocks(
|
proc beaconBlocks(
|
||||||
peer: Peer,
|
peer: Peer,
|
||||||
|
|
|
@ -11,7 +11,7 @@ import
|
||||||
unittest, chronicles,
|
unittest, chronicles,
|
||||||
./testutil,
|
./testutil,
|
||||||
../beacon_chain/spec/datatypes,
|
../beacon_chain/spec/datatypes,
|
||||||
../beacon_chain/[beacon_node_types, block_pool, ssz]
|
../beacon_chain/[beacon_node_types, block_pool, ssz, state_transition]
|
||||||
|
|
||||||
suiteReport "BlockRef and helpers" & preset():
|
suiteReport "BlockRef and helpers" & preset():
|
||||||
timedTest "isAncestorOf sanity" & preset():
|
timedTest "isAncestorOf sanity" & preset():
|
||||||
|
@ -133,6 +133,47 @@ when const_preset == "minimal": # Too much stack space used on mainnet
|
||||||
pool.heads.len == 1
|
pool.heads.len == 1
|
||||||
pool.heads[0].blck == b2Add
|
pool.heads[0].blck == b2Add
|
||||||
|
|
||||||
|
# Skip one slot to get a gap
|
||||||
|
process_slots(state, state.slot + 1)
|
||||||
|
|
||||||
|
let
|
||||||
|
b4 = addTestBlock(state, b2Root)
|
||||||
|
b4Root = hash_tree_root(b4.message)
|
||||||
|
b4Add = pool.add(b4Root, b4)
|
||||||
|
|
||||||
|
check:
|
||||||
|
b4Add.parent == b2Add
|
||||||
|
|
||||||
|
pool.updateHead(b4Add)
|
||||||
|
|
||||||
|
var blocks: array[3, BlockRef]
|
||||||
|
|
||||||
|
check:
|
||||||
|
pool.getBlockRange(Slot(0), 1, blocks.toOpenArray(0, 0)) == 0
|
||||||
|
blocks[0..<1] == [pool.tail]
|
||||||
|
|
||||||
|
pool.getBlockRange(Slot(0), 1, blocks.toOpenArray(0, 1)) == 0
|
||||||
|
blocks[0..<2] == [pool.tail, b1Add]
|
||||||
|
|
||||||
|
pool.getBlockRange(Slot(0), 2, blocks.toOpenArray(0, 1)) == 0
|
||||||
|
blocks[0..<2] == [pool.tail, b2Add]
|
||||||
|
|
||||||
|
pool.getBlockRange(Slot(0), 3, blocks.toOpenArray(0, 1)) == 1
|
||||||
|
blocks[0..<2] == [nil, pool.tail] # block 3 is missing!
|
||||||
|
|
||||||
|
pool.getBlockRange(Slot(2), 2, blocks.toOpenArray(0, 1)) == 0
|
||||||
|
blocks[0..<2] == [b2Add, b4Add] # block 3 is missing!
|
||||||
|
|
||||||
|
# empty length
|
||||||
|
pool.getBlockRange(Slot(2), 2, blocks.toOpenArray(0, -1)) == 0
|
||||||
|
|
||||||
|
# No blocks in sight
|
||||||
|
pool.getBlockRange(Slot(5), 1, blocks.toOpenArray(0, 1)) == 2
|
||||||
|
|
||||||
|
# No blocks in sight either due to gaps
|
||||||
|
pool.getBlockRange(Slot(3), 2, blocks.toOpenArray(0, 1)) == 2
|
||||||
|
blocks[0..<2] == [BlockRef nil, nil] # block 3 is missing!
|
||||||
|
|
||||||
timedTest "Reverse order block add & get" & preset():
|
timedTest "Reverse order block add & get" & preset():
|
||||||
discard pool.add(b2Root, b2)
|
discard pool.add(b2Root, b2)
|
||||||
|
|
||||||
|
@ -274,10 +315,6 @@ when const_preset == "minimal": # Too much stack space used on mainnet
|
||||||
pool.head.justified.slot.compute_epoch_at_slot() == 5
|
pool.head.justified.slot.compute_epoch_at_slot() == 5
|
||||||
pool.tail.children.len == 1
|
pool.tail.children.len == 1
|
||||||
|
|
||||||
pool.getBlockRange(Slot(1), 1, 1).mapIt(it.slot) == [Slot(1)]
|
|
||||||
pool.getBlockRange(Slot(1), 2, 1).mapIt(it.slot) == [Slot(1), Slot(2)]
|
|
||||||
pool.getBlockRange(Slot(1), 2, 2).mapIt(it.slot) == [Slot(1), Slot(3)]
|
|
||||||
|
|
||||||
let
|
let
|
||||||
pool2 = BlockPool.init(db)
|
pool2 = BlockPool.init(db)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue