nimbus-eth1/nimbus/sync/protocol/snap1.nim

232 lines
6.2 KiB
Nim
Raw Normal View History

# Nimbus - Ethereum Snap Protocol (SNAP), version 1
#
# Copyright (c) 2021-2024 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
# http://www.apache.org/licenses/LICENSE-2.0)
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or
# http://opensource.org/licenses/MIT)
# at your option. This file may not be copied, modified, or distributed
# except according to those terms.
## This module implements Ethereum Snapshot Protocol version 1, `snap/1`.
## Specification:
## `snap/1 <https://github.com/ethereum/devp2p/blob/master/caps/snap.md>`_
import
std/options,
chronicles,
chronos,
eth/[common, p2p, p2p/private/p2p_types],
./snap/snap_types,
../../constants
Snap sync simplify object inheritance (#1098) * Reorg SnapPeerBase descriptor, notably start/stop flags details: Instead of using three boolean flags startedFetch, stopped, and stopThisState a single enum type is used with values SyncRunningOk, SyncStopRequest, and SyncStopped. * Restricting snap to eth66 and later why: Id-tracked request/response wire protocol can handle overlapped responses when requests are sent in row. * Align function names with source code file names why: Easier to reconcile when following the implemented logic. * Update trace logging (want file locations) why: The macros previously used hid the relevant file location (when `chroniclesLineNumbers` turned on.) It rather printed the file location of the template that was wrapping `trace`. * Use KeyedQueue table instead of sequence why: Quick access, easy configuration as LRU or FIFO with max entries (currently LRU.) * Dissolve `SnapPeerEx` object extension into `SnapPeer` why; It is logically cleaner and more obvious not to inherit from `SnapPeerBase` but to specify opaque field object references of the merged `SnapPeer` object. These can then be locally inherited. * Dissolve `SnapSyncEx` object extension into `SnapSync` why; It is logically cleaner and more obvious not to inherit from `SnapSyncEx` but to specify opaque field object references of the `SnapPeer` object. These can then be locally inherited. Also, in the re-factored code here the interface descriptor `SnapSyncCtx` inherited `SnapSyncEx` which was sub-optimal (OO inheritance makes it easier to work with call back functions.)
2022-05-23 16:53:19 +00:00
export
snap_types
logScope:
topics = "snap1"
const
snapVersion* = 1
prettySnapProtoName* = "[snap/" & $snapVersion & "]"
Snap sync simplify object inheritance (#1098) * Reorg SnapPeerBase descriptor, notably start/stop flags details: Instead of using three boolean flags startedFetch, stopped, and stopThisState a single enum type is used with values SyncRunningOk, SyncStopRequest, and SyncStopped. * Restricting snap to eth66 and later why: Id-tracked request/response wire protocol can handle overlapped responses when requests are sent in row. * Align function names with source code file names why: Easier to reconcile when following the implemented logic. * Update trace logging (want file locations) why: The macros previously used hid the relevant file location (when `chroniclesLineNumbers` turned on.) It rather printed the file location of the template that was wrapping `trace`. * Use KeyedQueue table instead of sequence why: Quick access, easy configuration as LRU or FIFO with max entries (currently LRU.) * Dissolve `SnapPeerEx` object extension into `SnapPeer` why; It is logically cleaner and more obvious not to inherit from `SnapPeerBase` but to specify opaque field object references of the merged `SnapPeer` object. These can then be locally inherited. * Dissolve `SnapSyncEx` object extension into `SnapSync` why; It is logically cleaner and more obvious not to inherit from `SnapSyncEx` but to specify opaque field object references of the `SnapPeer` object. These can then be locally inherited. Also, in the re-factored code here the interface descriptor `SnapSyncCtx` inherited `SnapSyncEx` which was sub-optimal (OO inheritance makes it easier to work with call back functions.)
2022-05-23 16:53:19 +00:00
# Pickeled tracer texts
trSnapRecvReceived* =
"<< " & prettySnapProtoName & " Received "
trSnapRecvProtocolViolation* =
"<< " & prettySnapProtoName & " Protocol violation, "
trSnapRecvError* =
"<< " & prettySnapProtoName & " Error "
trSnapRecvTimeoutWaiting* =
"<< " & prettySnapProtoName & " Timeout waiting "
Snap sync simplify object inheritance (#1098) * Reorg SnapPeerBase descriptor, notably start/stop flags details: Instead of using three boolean flags startedFetch, stopped, and stopThisState a single enum type is used with values SyncRunningOk, SyncStopRequest, and SyncStopped. * Restricting snap to eth66 and later why: Id-tracked request/response wire protocol can handle overlapped responses when requests are sent in row. * Align function names with source code file names why: Easier to reconcile when following the implemented logic. * Update trace logging (want file locations) why: The macros previously used hid the relevant file location (when `chroniclesLineNumbers` turned on.) It rather printed the file location of the template that was wrapping `trace`. * Use KeyedQueue table instead of sequence why: Quick access, easy configuration as LRU or FIFO with max entries (currently LRU.) * Dissolve `SnapPeerEx` object extension into `SnapPeer` why; It is logically cleaner and more obvious not to inherit from `SnapPeerBase` but to specify opaque field object references of the merged `SnapPeer` object. These can then be locally inherited. * Dissolve `SnapSyncEx` object extension into `SnapSync` why; It is logically cleaner and more obvious not to inherit from `SnapSyncEx` but to specify opaque field object references of the `SnapPeer` object. These can then be locally inherited. Also, in the re-factored code here the interface descriptor `SnapSyncCtx` inherited `SnapSyncEx` which was sub-optimal (OO inheritance makes it easier to work with call back functions.)
2022-05-23 16:53:19 +00:00
trSnapSendSending* =
">> " & prettySnapProtoName & " Sending "
trSnapSendReplying* =
">> " & prettySnapProtoName & " Replying "
proc read(rlp: var Rlp, t: var SnapAccount, T: type Account): T =
## RLP mixin, decoding
rlp.snapRead T
proc read(rlp: var Rlp; T: type SnapProofNodes): T =
## RLP mixin, decoding
rlp.snapRead T
proc read(rlp: var Rlp; T: type SnapTriePaths): T =
## RLP mixin, decoding
rlp.snapRead T
proc append(writer: var RlpWriter, t: SnapAccount, account: Account) =
## RLP mixin, encoding
writer.snapAppend account
proc append(writer: var RlpWriter; spn: SnapProofNodes) =
## RLP mixin, encoding
writer.snapAppend spn
proc append(writer: var RlpWriter; stn: SnapTriePaths) =
## RLP mixin, encoding
writer.snapAppend stn
template handleHandlerError(x: untyped) =
if x.isErr:
raise newException(EthP2PError, x.error)
p2pProtocol snap1(version = snapVersion,
rlpxName = "snap",
peerState = SnapPeerState,
networkState = SnapWireBase,
useRequestIds = true):
requestResponse:
# User message 0x00: GetAccountRange.
proc getAccountRange(
peer: Peer;
root: Hash256;
origin: openArray[byte];
limit: openArray[byte];
replySizeMax: uint64;
) =
trace trSnapRecvReceived & "GetAccountRange (0x00)", peer, root,
nOrigin=origin.len, nLimit=limit.len, replySizeMax
let
ctx = peer.networkState()
res = ctx.getAccountRange(
root, origin, limit, replySizeMax)
handleHandlerError(res)
let
(accounts, proof) = res.get
# For logging only
nAccounts = accounts.len
nProof = proof.nodes.len
if nAccounts == 0 and nProof == 0:
trace trSnapSendReplying & "EMPTY AccountRange (0x01)", peer
else:
trace trSnapSendReplying & "AccountRange (0x01)", peer,
nAccounts, nProof
await response.send(accounts, proof)
# User message 0x01: AccountRange.
proc accountRange(
peer: Peer;
accounts: openArray[SnapAccount];
proof: SnapProofNodes)
requestResponse:
# User message 0x02: GetStorageRanges.
proc getStorageRanges(
peer: Peer;
root: Hash256;
accounts: openArray[Hash256];
origin: openArray[byte];
limit: openArray[byte];
replySizeMax: uint64;
) =
trace trSnapRecvReceived & "GetStorageRanges (0x02)", peer, root,
nAccounts=accounts.len, nOrigin=origin.len, nLimit=limit.len,
replySizeMax
let
ctx = peer.networkState()
res = ctx.getStorageRanges(
root, accounts, origin, limit, replySizeMax)
handleHandlerError(res)
let
(slots, proof) = res.get
# For logging only
nSlots = slots.len
nProof = proof.nodes.len
if nSlots == 0 and nProof == 0:
trace trSnapSendReplying & "EMPTY StorageRanges (0x03)", peer
else:
trace trSnapSendReplying & "StorageRanges (0x03)", peer,
nSlots, nProof
await response.send(slots, proof)
# User message 0x03: StorageRanges.
# Note: See comments in this file for a list of Geth quirks to expect.
proc storageRanges(
peer: Peer;
slotLists: openArray[seq[SnapStorage]];
proof: SnapProofNodes)
requestResponse:
# User message 0x04: GetByteCodes.
proc getByteCodes(
peer: Peer;
nodes: openArray[Hash256];
replySizeMax: uint64;
) =
Snap sync simplify object inheritance (#1098) * Reorg SnapPeerBase descriptor, notably start/stop flags details: Instead of using three boolean flags startedFetch, stopped, and stopThisState a single enum type is used with values SyncRunningOk, SyncStopRequest, and SyncStopped. * Restricting snap to eth66 and later why: Id-tracked request/response wire protocol can handle overlapped responses when requests are sent in row. * Align function names with source code file names why: Easier to reconcile when following the implemented logic. * Update trace logging (want file locations) why: The macros previously used hid the relevant file location (when `chroniclesLineNumbers` turned on.) It rather printed the file location of the template that was wrapping `trace`. * Use KeyedQueue table instead of sequence why: Quick access, easy configuration as LRU or FIFO with max entries (currently LRU.) * Dissolve `SnapPeerEx` object extension into `SnapPeer` why; It is logically cleaner and more obvious not to inherit from `SnapPeerBase` but to specify opaque field object references of the merged `SnapPeer` object. These can then be locally inherited. * Dissolve `SnapSyncEx` object extension into `SnapSync` why; It is logically cleaner and more obvious not to inherit from `SnapSyncEx` but to specify opaque field object references of the `SnapPeer` object. These can then be locally inherited. Also, in the re-factored code here the interface descriptor `SnapSyncCtx` inherited `SnapSyncEx` which was sub-optimal (OO inheritance makes it easier to work with call back functions.)
2022-05-23 16:53:19 +00:00
trace trSnapRecvReceived & "GetByteCodes (0x04)", peer,
nNodes=nodes.len, replySizeMax
let
ctx = peer.networkState()
codes = ctx.getByteCodes(nodes, replySizeMax)
handleHandlerError(codes)
let
# For logging only
nCodes = codes.get.len
if nCodes == 0:
trace trSnapSendReplying & "EMPTY ByteCodes (0x05)", peer
else:
trace trSnapSendReplying & "ByteCodes (0x05)", peer, nCodes
await response.send(codes.get)
# User message 0x05: ByteCodes.
proc byteCodes(
peer: Peer;
codes: openArray[Blob])
requestResponse:
# User message 0x06: GetTrieNodes.
proc getTrieNodes(
peer: Peer;
root: Hash256;
pathGroups: openArray[SnapTriePaths];
replySizeMax: uint64;
) =
trace trSnapRecvReceived & "GetTrieNodes (0x06)", peer, root,
nPathGroups=pathGroups.len, replySizeMax
let
ctx = peer.networkState()
nodes = ctx.getTrieNodes(root, pathGroups, replySizeMax)
handleHandlerError(nodes)
let
# For logging only
nNodes = nodes.get.len
if nNodes == 0:
trace trSnapSendReplying & "EMPTY TrieNodes (0x07)", peer
else:
trace trSnapSendReplying & "TrieNodes (0x07)", peer, nNodes
await response.send(nodes.get)
# User message 0x07: TrieNodes.
proc trieNodes(
peer: Peer;
nodes: openArray[Blob])
# End