2018-11-23 23:58:49 +00:00
|
|
|
import
|
2019-05-22 07:13:15 +00:00
|
|
|
options, tables, sequtils, algorithm, sets, macros,
|
2019-09-07 17:48:05 +00:00
|
|
|
chronicles, chronos, metrics, stew/ranges/bitranges,
|
2019-05-01 09:19:29 +00:00
|
|
|
spec/[datatypes, crypto, digest, helpers], eth/rlp,
|
2019-03-12 15:03:14 +00:00
|
|
|
beacon_node_types, eth2_network, beacon_chain_db, block_pool, time, ssz
|
|
|
|
|
2019-08-15 16:00:12 +00:00
|
|
|
when networkBackend == rlpxBackend:
|
|
|
|
import eth/rlp/options as rlpOptions
|
|
|
|
template libp2pProtocol*(name: string, version: int) {.pragma.}
|
|
|
|
|
2019-09-07 17:48:05 +00:00
|
|
|
declareGauge libp2p_peers, "Number of libp2p peers"
|
|
|
|
|
2018-11-23 23:58:49 +00:00
|
|
|
type
|
2019-07-08 13:19:52 +00:00
|
|
|
ValidatorSetDeltaFlags {.pure.} = enum
|
|
|
|
Activation = 0
|
|
|
|
Exit = 1
|
|
|
|
|
2018-11-23 23:58:49 +00:00
|
|
|
ValidatorChangeLogEntry* = object
|
|
|
|
case kind*: ValidatorSetDeltaFlags
|
2018-12-03 17:46:22 +00:00
|
|
|
of Activation:
|
2018-11-29 01:08:34 +00:00
|
|
|
pubkey: ValidatorPubKey
|
2018-11-23 23:58:49 +00:00
|
|
|
else:
|
|
|
|
index: uint32
|
|
|
|
|
2019-01-17 18:27:11 +00:00
|
|
|
ValidatorSet = seq[Validator]
|
2018-11-23 23:58:49 +00:00
|
|
|
|
2019-09-08 22:03:41 +00:00
|
|
|
BeaconSyncNetworkState* = ref object
|
2019-02-18 10:34:39 +00:00
|
|
|
node*: BeaconNode
|
|
|
|
db*: BeaconChainDB
|
|
|
|
|
2019-09-08 22:03:41 +00:00
|
|
|
BeaconSyncPeerState* = ref object
|
|
|
|
initialHelloReceived: bool
|
|
|
|
|
2019-07-03 07:35:05 +00:00
|
|
|
BlockRootSlot* = object
|
|
|
|
blockRoot: Eth2Digest
|
|
|
|
slot: Slot
|
|
|
|
|
2019-04-09 07:53:40 +00:00
|
|
|
const
|
2019-09-10 05:50:37 +00:00
|
|
|
maxBlocksToRequest = 64'u64
|
2019-04-26 06:21:46 +00:00
|
|
|
MaxAncestorBlocksResponse = 256
|
2019-04-09 07:53:40 +00:00
|
|
|
|
2019-06-10 11:13:53 +00:00
|
|
|
func toHeader(b: BeaconBlock): BeaconBlockHeader =
|
|
|
|
BeaconBlockHeader(
|
|
|
|
slot: b.slot,
|
2019-06-14 16:21:04 +00:00
|
|
|
parent_root: b.parent_root,
|
2019-03-01 20:09:20 +00:00
|
|
|
state_root: b.state_root,
|
2019-06-14 16:21:04 +00:00
|
|
|
body_root: hash_tree_root(b.body),
|
2019-06-10 11:13:53 +00:00
|
|
|
signature: b.signature
|
2019-03-01 20:09:20 +00:00
|
|
|
)
|
2019-02-18 10:34:39 +00:00
|
|
|
|
2019-06-10 11:13:53 +00:00
|
|
|
proc fromHeaderAndBody(b: var BeaconBlock, h: BeaconBlockHeader, body: BeaconBlockBody) =
|
2019-06-14 16:21:04 +00:00
|
|
|
doAssert(hash_tree_root(body) == h.body_root)
|
2019-06-10 11:13:53 +00:00
|
|
|
b.slot = h.slot
|
2019-06-14 16:21:04 +00:00
|
|
|
b.parent_root = h.parent_root
|
2019-02-18 10:34:39 +00:00
|
|
|
b.state_root = h.state_root
|
2019-03-05 13:55:09 +00:00
|
|
|
b.body = body
|
2019-06-10 11:13:53 +00:00
|
|
|
b.signature = h.signature
|
2019-02-18 10:34:39 +00:00
|
|
|
|
2019-03-13 21:23:01 +00:00
|
|
|
proc importBlocks(node: BeaconNode,
|
2019-06-10 11:13:53 +00:00
|
|
|
blocks: openarray[BeaconBlock]) =
|
|
|
|
for blk in blocks:
|
2019-06-17 11:08:05 +00:00
|
|
|
node.onBeaconBlock(node, blk)
|
2019-06-10 11:13:53 +00:00
|
|
|
info "Forward sync imported blocks", len = blocks.len
|
|
|
|
|
2019-09-08 22:03:41 +00:00
|
|
|
type
|
|
|
|
HelloMsg = object
|
|
|
|
forkVersion*: array[4, byte]
|
2019-09-09 23:55:01 +00:00
|
|
|
finalizedRoot*: Eth2Digest
|
|
|
|
finalizedEpoch*: Epoch
|
|
|
|
headRoot*: Eth2Digest
|
|
|
|
headSlot*: Slot
|
|
|
|
|
|
|
|
proc getCurrentHello(node: BeaconNode): HelloMsg =
|
|
|
|
let
|
|
|
|
blockPool = node.blockPool
|
|
|
|
finalizedHead = blockPool.finalizedHead
|
|
|
|
headBlock = blockPool.head.blck
|
|
|
|
headRoot = headBlock.root
|
|
|
|
headSlot = headBlock.slot
|
|
|
|
finalizedEpoch = finalizedHead.slot.compute_epoch_of_slot()
|
|
|
|
|
|
|
|
HelloMsg(
|
|
|
|
fork_version: node.forkVersion,
|
|
|
|
finalizedRoot: finalizedHead.blck.root,
|
|
|
|
finalizedEpoch: finalizedEpoch,
|
|
|
|
headRoot: headRoot,
|
|
|
|
headSlot: headSlot)
|
2019-09-08 22:03:41 +00:00
|
|
|
|
|
|
|
proc handleInitialHello(peer: Peer,
|
|
|
|
node: BeaconNode,
|
2019-09-09 23:55:01 +00:00
|
|
|
ourHello: HelloMsg,
|
|
|
|
theirHello: HelloMsg) {.async, gcsafe.}
|
2019-09-08 13:08:44 +00:00
|
|
|
|
2018-11-29 01:08:34 +00:00
|
|
|
p2pProtocol BeaconSync(version = 1,
|
2019-08-05 00:00:49 +00:00
|
|
|
rlpxName = "bcs",
|
2019-09-08 22:03:41 +00:00
|
|
|
networkState = BeaconSyncNetworkState,
|
|
|
|
peerState = BeaconSyncPeerState):
|
2019-02-18 10:34:39 +00:00
|
|
|
|
2019-06-05 02:00:07 +00:00
|
|
|
onPeerConnected do (peer: Peer):
|
2019-09-08 22:03:41 +00:00
|
|
|
if peer.wasDialed:
|
|
|
|
let
|
|
|
|
node = peer.networkState.node
|
2019-09-09 23:55:01 +00:00
|
|
|
ourHello = node.getCurrentHello
|
|
|
|
theirHello = await peer.hello(ourHello)
|
|
|
|
|
|
|
|
if theirHello.isSome:
|
|
|
|
await peer.handleInitialHello(node, ourHello, theirHello.get)
|
2019-02-18 10:34:39 +00:00
|
|
|
else:
|
2019-09-08 22:03:41 +00:00
|
|
|
warn "Hello response not received in time"
|
2019-02-18 10:34:39 +00:00
|
|
|
|
2019-09-07 17:48:05 +00:00
|
|
|
onPeerDisconnected do (peer: Peer):
|
|
|
|
libp2p_peers.set peer.network.peers.len.int64
|
|
|
|
|
2019-09-08 22:03:41 +00:00
|
|
|
requestResponse:
|
2019-09-09 23:55:01 +00:00
|
|
|
proc hello(peer: Peer, theirHello: HelloMsg) {.libp2pProtocol("hello", 1).} =
|
2019-09-08 22:03:41 +00:00
|
|
|
let
|
|
|
|
node = peer.networkState.node
|
2019-09-09 23:55:01 +00:00
|
|
|
ourHello = node.getCurrentHello
|
|
|
|
|
|
|
|
await response.send(ourHello)
|
2019-09-08 22:03:41 +00:00
|
|
|
|
|
|
|
if not peer.state.initialHelloReceived:
|
|
|
|
peer.state.initialHelloReceived = true
|
2019-09-09 23:55:01 +00:00
|
|
|
await peer.handleInitialHello(node, ourHello, theirHello)
|
2019-09-08 22:03:41 +00:00
|
|
|
|
|
|
|
proc helloResp(peer: Peer, msg: HelloMsg) {.libp2pProtocol("hello", 1).}
|
2019-02-18 10:34:39 +00:00
|
|
|
|
2019-09-10 05:50:37 +00:00
|
|
|
proc goodbye(peer: Peer, reason: DisconnectionReason) {.libp2pProtocol("goodbye", 1).}
|
2019-05-22 07:13:15 +00:00
|
|
|
|
2019-08-05 00:00:49 +00:00
|
|
|
requestResponse:
|
2019-09-08 14:54:31 +00:00
|
|
|
proc beaconBlocksByRange(
|
2019-08-05 00:00:49 +00:00
|
|
|
peer: Peer,
|
|
|
|
headBlockRoot: Eth2Digest,
|
2019-09-10 05:50:37 +00:00
|
|
|
startSlot: Slot,
|
2019-08-05 00:00:49 +00:00
|
|
|
count: uint64,
|
|
|
|
step: uint64) {.
|
2019-09-08 22:03:41 +00:00
|
|
|
libp2pProtocol("beacon_blocks_by_range", 1).} =
|
2019-09-10 05:50:37 +00:00
|
|
|
|
|
|
|
if count > 0'u64:
|
|
|
|
let count = if step != 0: min(count, maxBlocksToRequest.uint64) else: 1
|
2019-09-08 13:08:44 +00:00
|
|
|
let pool = peer.networkState.node.blockPool
|
2019-09-10 05:50:37 +00:00
|
|
|
var results: array[maxBlocksToRequest, BlockRef]
|
|
|
|
let
|
|
|
|
lastPos = min(count.int, results.len) - 1
|
|
|
|
firstPos = pool.getBlockRange(headBlockRoot, startSlot, step,
|
|
|
|
results.toOpenArray(0, lastPos))
|
|
|
|
for i in firstPos.int .. lastPos.int:
|
|
|
|
await response.write(pool.get(results[i]).data)
|
2019-08-05 00:00:49 +00:00
|
|
|
|
2019-09-08 14:54:31 +00:00
|
|
|
proc beaconBlocksByRoot(
|
2019-08-05 00:00:49 +00:00
|
|
|
peer: Peer,
|
|
|
|
blockRoots: openarray[Eth2Digest]) {.
|
2019-09-08 22:03:41 +00:00
|
|
|
libp2pProtocol("beacon_blocks_by_root", 1).} =
|
2019-09-09 02:39:44 +00:00
|
|
|
let
|
|
|
|
pool = peer.networkState.node.blockPool
|
|
|
|
db = peer.networkState.db
|
2019-08-05 00:00:49 +00:00
|
|
|
|
|
|
|
for root in blockRoots:
|
2019-09-08 13:08:44 +00:00
|
|
|
let blockRef = pool.getRef(root)
|
2019-09-09 02:39:44 +00:00
|
|
|
if not isNil(blockRef):
|
|
|
|
await response.write(pool.get(blockRef).data)
|
2019-08-05 00:00:49 +00:00
|
|
|
|
|
|
|
proc beaconBlocks(
|
|
|
|
peer: Peer,
|
2019-09-08 13:08:44 +00:00
|
|
|
blocks: openarray[BeaconBlock])
|
2019-05-22 07:13:15 +00:00
|
|
|
|
2019-09-09 23:55:01 +00:00
|
|
|
proc handleInitialHello(peer: Peer,
|
|
|
|
node: BeaconNode,
|
|
|
|
ourHello: HelloMsg,
|
|
|
|
theirHello: HelloMsg) {.async, gcsafe.} =
|
|
|
|
|
|
|
|
if theirHello.forkVersion != node.forkVersion:
|
|
|
|
await peer.disconnect(IrrelevantNetwork)
|
|
|
|
return
|
|
|
|
|
|
|
|
# TODO: onPeerConnected runs unconditionally for every connected peer, but we
|
|
|
|
# don't need to sync with everybody. The beacon node should detect a situation
|
|
|
|
# where it needs to sync and it should execute the sync algorithm with a certain
|
|
|
|
# number of randomly selected peers. The algorithm itself must be extracted in a proc.
|
|
|
|
try:
|
|
|
|
libp2p_peers.set peer.network.peers.len.int64
|
|
|
|
|
|
|
|
debug "Peer connected. Initiating sync", peer,
|
|
|
|
headSlot = ourHello.headSlot,
|
|
|
|
remoteHeadSlot = theirHello.headSlot
|
|
|
|
|
|
|
|
let bestDiff = cmp((ourHello.finalizedEpoch, ourHello.headSlot),
|
|
|
|
(theirHello.finalizedEpoch, theirHello.headSlot))
|
|
|
|
if bestDiff >= 0:
|
|
|
|
# Nothing to do?
|
|
|
|
debug "Nothing to sync", peer
|
|
|
|
else:
|
|
|
|
# TODO: Check for WEAK_SUBJECTIVITY_PERIOD difference and terminate the
|
|
|
|
# connection if it's too big.
|
|
|
|
var s = ourHello.headSlot + 1
|
|
|
|
var theirHello = theirHello
|
|
|
|
while s <= theirHello.headSlot:
|
|
|
|
let numBlocksToRequest = min(uint64(theirHello.headSlot - s),
|
|
|
|
maxBlocksToRequest)
|
2019-09-10 05:50:37 +00:00
|
|
|
|
|
|
|
debug "Requesting blocks", peer, remoteHeadSlot = theirHello.headSlot,
|
|
|
|
ourHeadSlot = s,
|
|
|
|
numBlocksToRequest
|
|
|
|
|
|
|
|
let blocks = await peer.beaconBlocksByRange(theirHello.headRoot, s,
|
2019-09-09 23:55:01 +00:00
|
|
|
numBlocksToRequest, 1'u64)
|
|
|
|
if blocks.isSome:
|
2019-09-10 05:50:37 +00:00
|
|
|
info "got blocks", total = blocks.get.len
|
2019-09-09 23:55:01 +00:00
|
|
|
if blocks.get.len == 0:
|
|
|
|
info "Got 0 blocks while syncing", peer
|
|
|
|
break
|
2019-09-10 05:50:37 +00:00
|
|
|
|
2019-09-09 23:55:01 +00:00
|
|
|
node.importBlocks blocks.get
|
|
|
|
let lastSlot = blocks.get[^1].slot
|
|
|
|
if lastSlot <= s:
|
|
|
|
info "Slot did not advance during sync", peer
|
|
|
|
break
|
|
|
|
|
|
|
|
s = lastSlot + 1
|
|
|
|
|
|
|
|
# TODO: Maybe this shouldn't happen so often.
|
|
|
|
# The alternative could be watching up a timer here.
|
|
|
|
let helloResp = await peer.hello(node.getCurrentHello)
|
|
|
|
if helloResp.isSome:
|
|
|
|
theirHello = helloResp.get
|
|
|
|
else:
|
|
|
|
# We'll ignore this error and we'll try to request
|
|
|
|
# another range optimistically. If that fails, the
|
|
|
|
# syncing will be interrupted.
|
|
|
|
discard
|
|
|
|
else:
|
2019-09-10 05:50:37 +00:00
|
|
|
error "didn't got objectes in time"
|
2019-09-09 23:55:01 +00:00
|
|
|
break
|
|
|
|
|
|
|
|
except CatchableError:
|
|
|
|
warn "Failed to sync with peer", peer, err = getCurrentExceptionMsg()
|
|
|
|
|