diff --git a/Makefile b/Makefile
index 0adb4d025..fe99887dc 100644
--- a/Makefile
+++ b/Makefile
@@ -56,6 +56,7 @@ EXCLUDED_NIM_PACKAGES := \
# we don't want an error here, so we can handle things later, in the ".DEFAULT" target
-include $(BUILD_SYSTEM_DIR)/makefiles/variables.mk
+-include ./nimbus/sync/protocol/eth/eth-variables.mk
# debugging tools + testing tools
TOOLS := \
@@ -195,15 +196,8 @@ ifneq ($(if $(ENABLE_VMLOWMEM),$(ENABLE_VMLOWMEM),0),0)
NIM_PARAMS += -d:lowmem:1
endif
-# chunked messages enabled by default, use ENABLE_CHUNKED_RLPX=0 to disable
-ifneq ($(if $(ENABLE_CHUNKED_RLPX),$(ENABLE_CHUNKED_RLPX),1),0)
-NIM_PARAMS := $(NIM_PARAMS) -d:chunked_rlpx_enabled
-endif
-
-# legacy wire protocol enabled by default, use ENABLE_LEGACY_ETH66=0 to disable
-ifneq ($(if $(ENABLE_LEGACY_ETH66),$(ENABLE_LEGACY_ETH66),1),0)
-NIM_PARAMS := $(NIM_PARAMS) -d:legacy_eth66_enabled
-endif
+# eth protocol settings, rules from "nimbus/sync/protocol/eth/variables.mk"
+NIM_PARAMS := $(NIM_PARAMS) $(NIM_ETH_PARAMS)
#- deletes and recreates "nimbus.nims" which on Windows is a copy instead of a proper symlink
update: | update-common
diff --git a/README.md b/README.md
index e5c606c77..eda1f5193 100644
--- a/README.md
+++ b/README.md
@@ -230,6 +230,9 @@ available.)
Disable legacy chunked RLPx messages which are enabled by default for
synchronising against `Nethermind` nodes
+ * ENABLE_ETH_VERSION=66
+ Enable legacy protocol `eth66` (or whatever other protocol version.)
+
* ENABLE_EVMC=1
Enable mostly EVMC compliant wrapper around the native Nim VM
diff --git a/nimbus/evm/async/data_sources/json_rpc_data_source.nim b/nimbus/evm/async/data_sources/json_rpc_data_source.nim
index dc1e81992..54d9301df 100644
--- a/nimbus/evm/async/data_sources/json_rpc_data_source.nim
+++ b/nimbus/evm/async/data_sources/json_rpc_data_source.nim
@@ -29,11 +29,6 @@ import
web3/conversions,
web3
-when defined(legacy_eth66_enabled):
- #import
- #../../../sync/protocol/eth66 as proto_eth66
- from ../../../sync/protocol/eth66 import getNodeData
-
export AsyncOperationFactory, AsyncDataSource
type
@@ -85,41 +80,7 @@ proc fetchBlockHeaderWithNumber*(rpcClient: RpcClient, n: common.BlockNumber): F
fetchCounter += 1
return blockHeaderFromBlockObject(blockObject)
-#[
-proc parseBlockBodyAndFetchUncles(rpcClient: RpcClient, r: JsonNode): Future[BlockBody] {.async.} =
- var body: BlockBody
- for tn in r["transactions"].getElems:
- body.transactions.add(parseTransaction(tn))
- for un in r["uncles"].getElems:
- let uncleHash: Hash256 = un.getStr.ethHash
- let uncleHeader = await fetchBlockHeaderWithHash(rpcClient, uncleHash)
- body.uncles.add(uncleHeader)
- return body
-
-proc fetchBlockHeaderAndBodyWithHash*(rpcClient: RpcClient, h: Hash256): Future[(BlockHeader, BlockBody)] {.async.} =
- let t0 = now()
- let r = request("eth_getBlockByHash", %[%h.prefixHex, %true], some(rpcClient))
- durationSpentDoingFetches += now() - t0
- fetchCounter += 1
- if r.kind == JNull:
- error "requested block not available", blockHash=h
- raise newException(ValueError, "Error when retrieving block header and body")
- let header = parseBlockHeader(r)
- let body = await parseBlockBodyAndFetchUncles(rpcClient, r)
- return (header, body)
-
-proc fetchBlockHeaderAndBodyWithNumber*(rpcClient: RpcClient, n: BlockNumber): Future[(BlockHeader, BlockBody)] {.async.} =
- let t0 = now()
- let r = request("eth_getBlockByNumber", %[%n.prefixHex, %true], some(rpcClient))
- durationSpentDoingFetches += now() - t0
- fetchCounter += 1
- if r.kind == JNull:
- error "requested block not available", blockNumber=n
- raise newException(ValueError, "Error when retrieving block header and body")
- let header = parseBlockHeader(r)
- let body = await parseBlockBodyAndFetchUncles(rpcClient, r)
- return (header, body)
-]#
+# Cruft moved to `json_rpc_data_source/currently_unused.nim`
proc fetchBlockHeaderAndBodyWithHash*(rpcClient: RpcClient, h: common.Hash256): Future[(common.BlockHeader, BlockBody)] {.async.} =
doAssert(false, "AARDVARK not implemented")
@@ -167,64 +128,7 @@ proc fetchCode*(client: RpcClient, blockNumber: common.BlockNumber, address: Eth
fetchCounter += 1
return fetchedCode
-#[
-const bytesLimit = 2 * 1024 * 1024
-const maxNumberOfPeersToAttempt = 3
-
-proc fetchUsingGetTrieNodes(peer: Peer, stateRoot: common.Hash256, paths: seq[SnapTriePaths]): Future[seq[seq[byte]]] {.async.} =
- let r = await peer.getTrieNodes(stateRoot, paths, bytesLimit)
- if r.isNone:
- raise newException(CatchableError, "AARDVARK: received None in GetTrieNodes response")
- else:
- return r.get.nodes
-
-proc fetchUsingGetNodeData(peer: Peer, nodeHashes: seq[common.Hash256]): Future[seq[seq[byte]]] {.async.} =
-
- let r: Option[seq[seq[byte]]] = none[seq[seq[byte]]]() # AARDVARK await peer.getNodeData(nodeHashes)
- if r.isNone:
- raise newException(CatchableError, "AARDVARK: received None in GetNodeData response")
- else:
- echo "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA fetchUsingGetNodeData received nodes: " & $(r.get.data)
- return r.get.data
-
- # AARDVARK whatever
- return @[]
-
-proc findPeersAndMakeSomeCalls[R](peerPool: PeerPool, protocolName: string, protocolType: typedesc, initiateAttempt: (proc(p: Peer): Future[R] {.gcsafe, raises: [].})): Future[seq[Future[R]]] {.async.} =
- var attempts: seq[Future[R]]
- while true:
- #info("AARDVARK: findPeersAndMakeSomeCalls about to loop through the peer pool", count=peerPool.connectedNodes.len)
- for nodeOfSomeSort, peer in peerPool.connectedNodes:
- if peer.supports(protocolType):
- info("AARDVARK: findPeersAndMakeSomeCalls calling peer", protocolName, peer)
- attempts.add(initiateAttempt(peer))
- if attempts.len >= maxNumberOfPeersToAttempt:
- break
- #else:
- # info("AARDVARK: peer does not support protocol", protocolName, peer)
- if attempts.len == 0:
- warn("AARDVARK: findPeersAndMakeSomeCalls did not find any peers; waiting and trying again", protocolName, totalPeerPoolSize=peerPool.connectedNodes.len)
- await sleepAsync(chronos.seconds(5))
- else:
- if attempts.len < maxNumberOfPeersToAttempt:
- warn("AARDVARK: findPeersAndMakeSomeCalls did not find enough peers, but found some", protocolName, totalPeerPoolSize=peerPool.connectedNodes.len, found=attempts.len)
- break
- return attempts
-
-proc findPeersAndMakeSomeAttemptsToCallGetTrieNodes(peerPool: PeerPool, stateRoot: common.Hash256, paths: seq[SnapTriePaths]): Future[seq[Future[seq[seq[byte]]]]] =
- findPeersAndMakeSomeCalls(peerPool, "snap", protocol.snap, (proc(peer: Peer): Future[seq[seq[byte]]] = fetchUsingGetTrieNodes(peer, stateRoot, paths)))
-
-proc findPeersAndMakeSomeAttemptsToCallGetNodeData(peerPool: PeerPool, stateRoot: Hash256, nodeHashes: seq[Hash256]): Future[seq[Future[seq[seq[byte]]]]] =
- findPeersAndMakeSomeCalls(peerPool, "eth66", eth66, (proc(peer: Peer): Future[seq[seq[byte]]] = fetchUsingGetNodeData(peer, nodeHashes)))
-
-proc fetchNodes(peerPool: PeerPool, stateRoot: common.Hash256, paths: seq[SnapTriePaths], nodeHashes: seq[common.Hash256]): Future[seq[seq[byte]]] {.async.} =
- let attempts = await findPeersAndMakeSomeAttemptsToCallGetTrieNodes(peerPool, stateRoot, paths)
- #let attempts = await findPeersAndMakeSomeAttemptsToCallGetNodeData(peerPool, stateRoot, nodeHashes)
- let completedAttempt = await one(attempts)
- let nodes: seq[seq[byte]] = completedAttempt.read
- info("AARDVARK: fetchNodes received nodes", nodes)
- return nodes
-]#
+# Cruft moved to json_rpc_data_source/currently_unused.nim
proc verifyFetchedAccount(stateRoot: common.Hash256, address: EthAddress, acc: Account, accProof: seq[seq[byte]]): Result[void, string] =
let accKey = toSeq(keccakHash(address).data)
diff --git a/nimbus/evm/async/data_sources/json_rpc_data_source/currently_unused.nim b/nimbus/evm/async/data_sources/json_rpc_data_source/currently_unused.nim
new file mode 100644
index 000000000..609ee3d96
--- /dev/null
+++ b/nimbus/evm/async/data_sources/json_rpc_data_source/currently_unused.nim
@@ -0,0 +1,133 @@
+# Nimbus
+# Copyright (c) 2023-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.
+
+import
+ std/[sequtils, typetraits, options, times, json],
+ chronicles,
+ chronos,
+ nimcrypto,
+ stint,
+ stew/byteutils,
+ json_rpc/rpcclient,
+ eth/common,
+ eth/rlp,
+ eth/trie/hexary_proof_verification,
+ eth/p2p,
+ eth/p2p/rlpx,
+ eth/p2p/private/p2p_types,
+ #../../../sync/protocol,
+ ../../../../db/[core_db, distinct_tries, incomplete_db, storage_types],
+ ../../data_sources,
+ ../../../../beacon/web3_eth_conv,
+ web3/conversions,
+ web3
+
+when defined(legacy_eth66_enabled):
+ from ../../../sync/protocol/eth66 import getNodeData
+
+# Comment extracted from `json_rpc_data_source.nim` line 83
+# ---------------------------------------------------------
+
+proc parseBlockBodyAndFetchUncles(rpcClient: RpcClient, r: JsonNode): Future[BlockBody] {.async.} =
+ var body: BlockBody
+ for tn in r["transactions"].getElems:
+ body.transactions.add(parseTransaction(tn))
+ for un in r["uncles"].getElems:
+ let uncleHash: Hash256 = un.getStr.ethHash
+ let uncleHeader = await fetchBlockHeaderWithHash(rpcClient, uncleHash)
+ body.uncles.add(uncleHeader)
+ return body
+
+proc fetchBlockHeaderAndBodyWithHash*(rpcClient: RpcClient, h: Hash256): Future[(BlockHeader, BlockBody)] {.async.} =
+ let t0 = now()
+ let r = request("eth_getBlockByHash", %[%h.prefixHex, %true], some(rpcClient))
+ durationSpentDoingFetches += now() - t0
+ fetchCounter += 1
+ if r.kind == JNull:
+ error "requested block not available", blockHash=h
+ raise newException(ValueError, "Error when retrieving block header and body")
+ let header = parseBlockHeader(r)
+ let body = await parseBlockBodyAndFetchUncles(rpcClient, r)
+ return (header, body)
+
+proc fetchBlockHeaderAndBodyWithNumber*(rpcClient: RpcClient, n: BlockNumber): Future[(BlockHeader, BlockBody)] {.async.} =
+ let t0 = now()
+ let r = request("eth_getBlockByNumber", %[%n.prefixHex, %true], some(rpcClient))
+ durationSpentDoingFetches += now() - t0
+ fetchCounter += 1
+ if r.kind == JNull:
+ error "requested block not available", blockNumber=n
+ raise newException(ValueError, "Error when retrieving block header and body")
+ let header = parseBlockHeader(r)
+ let body = await parseBlockBodyAndFetchUncles(rpcClient, r)
+ return (header, body)
+
+
+# Comment extracted from `json_rpc_data_source.nim` line 131
+# ----------------------------------------------------------
+
+const bytesLimit = 2 * 1024 * 1024
+const maxNumberOfPeersToAttempt = 3
+
+proc fetchUsingGetTrieNodes(peer: Peer, stateRoot: common.Hash256, paths: seq[SnapTriePaths]): Future[seq[seq[byte]]] {.async.} =
+ let r = await peer.getTrieNodes(stateRoot, paths, bytesLimit)
+ if r.isNone:
+ raise newException(CatchableError, "AARDVARK: received None in GetTrieNodes response")
+ else:
+ return r.get.nodes
+
+proc fetchUsingGetNodeData(peer: Peer, nodeHashes: seq[common.Hash256]): Future[seq[seq[byte]]] {.async.} =
+
+ let r: Option[seq[seq[byte]]] = none[seq[seq[byte]]]() # AARDVARK await peer.getNodeData(nodeHashes)
+ if r.isNone:
+ raise newException(CatchableError, "AARDVARK: received None in GetNodeData response")
+ else:
+ echo "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA fetchUsingGetNodeData received nodes: " & $(r.get.data)
+ return r.get.data
+
+ # AARDVARK whatever
+ return @[]
+
+proc findPeersAndMakeSomeCalls[R](peerPool: PeerPool, protocolName: string, protocolType: typedesc, initiateAttempt: (proc(p: Peer): Future[R] {.gcsafe, raises: [].})): Future[seq[Future[R]]] {.async.} =
+ var attempts: seq[Future[R]]
+ while true:
+ #info("AARDVARK: findPeersAndMakeSomeCalls about to loop through the peer pool", count=peerPool.connectedNodes.len)
+ for nodeOfSomeSort, peer in peerPool.connectedNodes:
+ if peer.supports(protocolType):
+ info("AARDVARK: findPeersAndMakeSomeCalls calling peer", protocolName, peer)
+ attempts.add(initiateAttempt(peer))
+ if attempts.len >= maxNumberOfPeersToAttempt:
+ break
+ #else:
+ # info("AARDVARK: peer does not support protocol", protocolName, peer)
+ if attempts.len == 0:
+ warn("AARDVARK: findPeersAndMakeSomeCalls did not find any peers; waiting and trying again", protocolName, totalPeerPoolSize=peerPool.connectedNodes.len)
+ await sleepAsync(chronos.seconds(5))
+ else:
+ if attempts.len < maxNumberOfPeersToAttempt:
+ warn("AARDVARK: findPeersAndMakeSomeCalls did not find enough peers, but found some", protocolName, totalPeerPoolSize=peerPool.connectedNodes.len, found=attempts.len)
+ break
+ return attempts
+
+proc findPeersAndMakeSomeAttemptsToCallGetTrieNodes(peerPool: PeerPool, stateRoot: common.Hash256, paths: seq[SnapTriePaths]): Future[seq[Future[seq[seq[byte]]]]] =
+ findPeersAndMakeSomeCalls(peerPool, "snap", protocol.snap, (proc(peer: Peer): Future[seq[seq[byte]]] = fetchUsingGetTrieNodes(peer, stateRoot, paths)))
+
+proc findPeersAndMakeSomeAttemptsToCallGetNodeData(peerPool: PeerPool, stateRoot: Hash256, nodeHashes: seq[Hash256]): Future[seq[Future[seq[seq[byte]]]]] =
+ findPeersAndMakeSomeCalls(peerPool, "eth66", eth66, (proc(peer: Peer): Future[seq[seq[byte]]] = fetchUsingGetNodeData(peer, nodeHashes)))
+
+proc fetchNodes(peerPool: PeerPool, stateRoot: common.Hash256, paths: seq[SnapTriePaths], nodeHashes: seq[common.Hash256]): Future[seq[seq[byte]]] {.async.} =
+ let attempts = await findPeersAndMakeSomeAttemptsToCallGetTrieNodes(peerPool, stateRoot, paths)
+ #let attempts = await findPeersAndMakeSomeAttemptsToCallGetNodeData(peerPool, stateRoot, nodeHashes)
+ let completedAttempt = await one(attempts)
+ let nodes: seq[seq[byte]] = completedAttempt.read
+ info("AARDVARK: fetchNodes received nodes", nodes)
+ return nodes
+
+# End
diff --git a/nimbus/sync/handlers/eth.nim b/nimbus/sync/handlers/eth.nim
index 9fab2eedb..bac64e313 100644
--- a/nimbus/sync/handlers/eth.nim
+++ b/nimbus/sync/handlers/eth.nim
@@ -21,6 +21,12 @@ import
../protocol/trace_config, # gossip noise control
../../core/[chain, tx_pool, tx_pool/tx_item]
+# There is only one eth protocol version possible at compile time. This
+# might change in future.
+when ethVersion == 68:
+ import
+ std/sequtils
+
logScope:
topics = "eth-wire"
@@ -221,7 +227,6 @@ proc sendNewTxHashes(ctx: EthWireRef,
txHashes: seq[Hash256],
peers: seq[Peer]): Future[void] {.async.} =
try:
-
for peer in peers:
# Add to known tx hashes and get hashes still to send to peer
var hashesToSend: seq[Hash256]
@@ -229,7 +234,15 @@ proc sendNewTxHashes(ctx: EthWireRef,
# Broadcast to peer if at least 1 new tx hash to announce
if hashesToSend.len > 0:
- await peer.newPooledTransactionHashes(hashesToSend)
+ # Currently only one protocol version is available as compiled
+ when ethVersion == 68:
+ await newPooledTransactionHashes(
+ peer,
+ 1u8.repeat hashesToSend.len, # type
+ 0.repeat hashesToSend.len, # sizes
+ hashesToSend)
+ else:
+ await newPooledTransactionHashes(peer, hashesToSend)
except TransportError:
debug "Transport got closed during sendNewTxHashes"
@@ -241,7 +254,6 @@ proc sendTransactions(ctx: EthWireRef,
txs: seq[Transaction],
peers: seq[Peer]): Future[void] {.async.} =
try:
-
for peer in peers:
# This is used to avoid re-sending along pooledTxHashes
# announcements/re-broadcasts
@@ -536,40 +548,53 @@ method handleAnnouncedTxs*(ctx: EthWireRef,
except CatchableError as exc:
return err(exc.msg)
-method handleAnnouncedTxsHashes*(ctx: EthWireRef,
- peer: Peer,
- txHashes: openArray[Hash256]):
- Result[void, string] =
- if ctx.enableTxPool != Enabled:
- when trMissingOrDisabledGossipOk:
- notEnabled("handleAnnouncedTxsHashes")
+when ethVersion == 68:
+ method handleAnnouncedTxsHashes*(
+ ctx: EthWireRef;
+ peer: Peer;
+ txTypes: Blob;
+ txSizes: openArray[int];
+ txHashes: openArray[Hash256];
+ ): Result[void, string] =
+ ## `Eth68` method
+ notImplemented "handleAnnouncedTxsHashes()/eth68"
+
+else:
+ method handleAnnouncedTxsHashes*(ctx: EthWireRef,
+ peer: Peer,
+ txHashes: openArray[Hash256]):
+ Result[void, string] =
+ ## Pre-eth68 method
+ if ctx.enableTxPool != Enabled:
+ when trMissingOrDisabledGossipOk:
+ notEnabled("handleAnnouncedTxsHashes")
+ return ok()
+
+ if txHashes.len == 0:
+ return ok()
+
+ if ctx.lastCleanup - getTime() > POOLED_STORAGE_TIME_LIMIT:
+ ctx.cleanupKnownByPeer()
+
+ ctx.addToKnownByPeer(txHashes, peer)
+ var reqHashes = newSeqOfCap[Hash256](txHashes.len)
+ for txHash in txHashes:
+ if txHash in ctx.pending or ctx.inPool(txHash):
+ continue
+ reqHashes.add txHash
+
+ if reqHashes.len == 0:
+ return ok()
+
+ debug "handleAnnouncedTxsHashes: received new tx hashes",
+ number = reqHashes.len
+
+ for txHash in reqHashes:
+ ctx.pending.incl txHash
+
+ asyncSpawn ctx.fetchTransactions(reqHashes, peer)
return ok()
- if txHashes.len == 0:
- return ok()
-
- if ctx.lastCleanup - getTime() > POOLED_STORAGE_TIME_LIMIT:
- ctx.cleanupKnownByPeer()
-
- ctx.addToKnownByPeer(txHashes, peer)
- var reqHashes = newSeqOfCap[Hash256](txHashes.len)
- for txHash in txHashes:
- if txHash in ctx.pending or ctx.inPool(txHash):
- continue
- reqHashes.add txHash
-
- if reqHashes.len == 0:
- return ok()
-
- debug "handleAnnouncedTxsHashes: received new tx hashes",
- number = reqHashes.len
-
- for txHash in reqHashes:
- ctx.pending.incl txHash
-
- asyncSpawn ctx.fetchTransactions(reqHashes, peer)
- return ok()
-
method handleNewBlock*(ctx: EthWireRef,
peer: Peer,
blk: EthBlock,
@@ -615,7 +640,9 @@ method handleNewBlockHashes*(ctx: EthWireRef,
except CatchableError as exc:
return err(exc.msg)
-when defined(legacy_eth66_enabled):
+# There is only one eth protocol version possible at compile time. This
+# might change in future.
+when ethVersion == 66:
method getStorageNodes*(ctx: EthWireRef,
hashes: openArray[Hash256]):
Result[seq[Blob], string] {.gcsafe.} =
diff --git a/nimbus/sync/protocol.nim b/nimbus/sync/protocol.nim
index a8b6ad493..1b3124409 100644
--- a/nimbus/sync/protocol.nim
+++ b/nimbus/sync/protocol.nim
@@ -1,5 +1,5 @@
# Nimbus
-# Copyright (c) 2018-2021 Status Research & Development GmbH
+# Copyright (c) 2021-2024 Status Research & Development GmbH
# Licensed and distributed under either of
# * MIT license (license terms in the root directory or at
# https://opensource.org/licenses/MIT).
@@ -8,13 +8,43 @@
# at your option. This file may not be copied, modified, or distributed
# except according to those terms.
-when defined(legacy_eth66_enabled):
- import ./protocol/eth66 as proto_eth
- type eth* = eth66
-else:
+## Provision of `eth` and `snap` protocol version parameters
+##
+## `Eth` related parameters:
+## `ethVersions`: seq[int] -- constant list of all available versions
+## `eth` -- type symbol of default version
+## `proto_eth` -- export of default version directives
+##
+## `Snap` related parameters:
+## `snap` -- type symbol of default version
+## `proto_snap` -- export of default version directives
+## ..aliases.. -- type names, syntactic sugar (see below)
+##
+
+include
+ ./protocol/eth/eth_versions
+
+assert 0 < ethVersions.len
+
+# multi protocol not functional, yet
+when 1 < buildEthVersions.len:
+ {.warning: "No multi eth-protocol yet (using latest version only)".}
+
+when 68 in ethVersions:
+ {.warning: "Protocol eth68 is not fully functional yet".}
+ import ./protocol/eth68 as proto_eth
+ type eth* = eth68
+
+elif 67 in ethVersions:
import ./protocol/eth67 as proto_eth
type eth* = eth67
+elif 66 in ethVersions:
+ import ./protocol/eth66 as proto_eth
+ type eth* = eth66
+
+# ---------------
+
import
./protocol/snap1 as proto_snap
diff --git a/nimbus/sync/protocol/eth/eth-variables.mk b/nimbus/sync/protocol/eth/eth-variables.mk
new file mode 100644
index 000000000..60b6c5a60
--- /dev/null
+++ b/nimbus/sync/protocol/eth/eth-variables.mk
@@ -0,0 +1,48 @@
+# Copyright (c) 2024 Status Research & Development GmbH. Licensed under
+# either of:
+# - Apache License, version 2.0
+# - MIT license
+# at your option. This file may not be copied, modified, or distributed except
+# according to those terms.
+
+# GNU-Makefile include
+#
+# When running make, the instructions here will define the variable
+# "NIM_ETH_PARAMS" which should be appended to the nim compiler options.
+#
+# This makefile snippet supports multi-protocol arguments as in
+# "ENABLE_ETH_VERSION=66:67:999" where available protocol versions are
+# extracted and processed. In this case, protocol version 999 will be
+# silently ignored.
+
+# default wire protocol, triggered by empty/unset variable or zero value
+ifeq ($(if $(ENABLE_ETH_VERSION),$(ENABLE_ETH_VERSION),0),0)
+NIM_ETH_PARAMS := $(NIM_ETH_PARAMS) -d:eth67_enabled
+endif
+
+# parse list for supported items
+ifneq ($(findstring :66:,:$(ENABLE_ETH_VERSION):),)
+NIM_ETH_PARAMS := $(NIM_ETH_PARAMS) -d:eth66_enabled
+endif
+
+ifneq ($(findstring :67:,:$(ENABLE_ETH_VERSION):),)
+NIM_ETH_PARAMS := $(NIM_ETH_PARAMS) -d:eth67_enabled
+endif
+
+ifneq ($(findstring :68:,:$(ENABLE_ETH_VERSION):),)
+NIM_ETH_PARAMS := $(NIM_ETH_PARAMS) -d:eth68_enabled
+endif
+
+# There must be at least one protocol version.
+ifeq ($(NIM_ETH_PARAMS),)
+$(error Unacceptable protocol versions in "ENABLE_ETH_VERSION=$(ENABLE_ETH_VERSION)")
+endif
+
+# ------------
+
+# chunked messages enabled by default, use ENABLE_CHUNKED_RLPX=0 to disable
+ifneq ($(if $(ENABLE_CHUNKED_RLPX),$(ENABLE_CHUNKED_RLPX),1),0)
+NIM_ETH_PARAMS := $(NIM_ETH_PARAMS) -d:chunked_rlpx_enabled
+endif
+
+# End
diff --git a/nimbus/sync/protocol/eth/eth_types.nim b/nimbus/sync/protocol/eth/eth_types.nim
index 800b08553..716ddcfd7 100644
--- a/nimbus/sync/protocol/eth/eth_types.nim
+++ b/nimbus/sync/protocol/eth/eth_types.nim
@@ -16,6 +16,9 @@ import
eth/[common, p2p, p2p/private/p2p_types],
../../types
+include
+ ./eth_versions # early compile time list of proto versions
+
logScope:
topics = "eth-wire"
@@ -93,12 +96,24 @@ method handleAnnouncedTxs*(ctx: EthWireBase,
{.base, gcsafe.} =
notImplemented("handleAnnouncedTxs")
-method handleAnnouncedTxsHashes*(ctx: EthWireBase,
- peer: Peer,
- txHashes: openArray[Hash256]):
- Result[void, string]
- {.base, gcsafe.} =
- notImplemented("handleAnnouncedTxsHashes")
+# Most recent setting, only the latest version is active
+when 68 in ethVersions:
+ method handleAnnouncedTxsHashes*(
+ ctx: EthWireBase;
+ peer: Peer;
+ txTypes: Blob;
+ txSizes: openArray[int];
+ txHashes: openArray[Hash256];
+ ): Result[void, string]
+ {.base, gcsafe.} =
+ notImplemented("handleAnnouncedTxsHashes/eth68")
+else:
+ method handleAnnouncedTxsHashes*(ctx: EthWireBase,
+ peer: Peer,
+ txHashes: openArray[Hash256]):
+ Result[void, string]
+ {.base, gcsafe.} =
+ notImplemented("handleAnnouncedTxsHashes")
method handleNewBlockHashes*(ctx: EthWireBase,
peer: Peer,
@@ -107,7 +122,8 @@ method handleNewBlockHashes*(ctx: EthWireBase,
{.base, gcsafe.} =
notImplemented("handleNewBlockHashes")
-when defined(legacy_eth66_enabled):
+# Legacy setting, currently the latest version is active only
+when 66 in ethVersions and ethVersions.len == 1:
method getStorageNodes*(ctx: EthWireBase,
hashes: openArray[Hash256]):
Result[seq[Blob], string]
diff --git a/nimbus/sync/protocol/eth/eth_versions.nim b/nimbus/sync/protocol/eth/eth_versions.nim
new file mode 100644
index 000000000..3b984eb39
--- /dev/null
+++ b/nimbus/sync/protocol/eth/eth_versions.nim
@@ -0,0 +1,37 @@
+# Nimbus
+# Copyright (c) 2024 Status Research & Development GmbH
+# Licensed and distributed under either of
+# * MIT license (license terms in the root directory or at
+# https://opensource.org/licenses/MIT).
+# * Apache v2 license (license terms in the root directory or at
+# https://www.apache.org/licenses/LICENSE-2.0).
+# at your option. This file may not be copied, modified, or distributed
+# except according to those terms.
+
+## Provision of `eth` versions list at compile time
+##
+## Note: This file allows read the `ethVersions` list at each level of the
+## source code import hierarchy. It needs to be *included* though if
+## `ethVersions` needs to be available at compile time (as of
+## NIM 1.6.18). This allows to construct directives like:
+## `when 66 in ethVersions: ..`
+##
+
+const
+ buildEthVersions = block:
+ var rc: seq[int]
+ when defined(eth66_enabled): rc.add 66
+ when defined(eth67_enabled): rc.add 67
+ when defined(eth68_enabled): rc.add 68
+ rc
+
+# Default protocol only
+when buildEthVersions.len == 0:
+ const ethVersions* = @[67]
+ ## Compile time list of available/supported eth versions
+else:
+ # One or more protocols
+ const ethVersions* = buildEthVersions
+ ## Compile time list of available/supported eth versions
+
+# End
diff --git a/nimbus/sync/protocol/eth68.nim b/nimbus/sync/protocol/eth68.nim
new file mode 100644
index 000000000..73b7cdac9
--- /dev/null
+++ b/nimbus/sync/protocol/eth68.nim
@@ -0,0 +1,299 @@
+# Nimbus - Ethereum Wire Protocol
+#
+# Copyright (c) 2018-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 Wire Protocol version 67, `eth/67`.
+## Specification:
+## `eth/68 `_
+
+import
+ stint,
+ chronicles,
+ chronos,
+ eth/[common, p2p, p2p/private/p2p_types],
+ stew/byteutils,
+ ./trace_config,
+ ./eth/eth_types,
+ ../types,
+ ../../utils/utils
+
+export
+ eth_types
+
+logScope:
+ topics = "eth68"
+
+const
+ ethVersion* = 68
+ prettyEthProtoName* = "[eth/" & $ethVersion & "]"
+
+ # Pickeled tracer texts
+ trEthRecvReceived* =
+ "<< " & prettyEthProtoName & " Received "
+ trEthRecvReceivedBlockHeaders* =
+ trEthRecvReceived & "BlockHeaders (0x04)"
+ trEthRecvReceivedBlockBodies* =
+ trEthRecvReceived & "BlockBodies (0x06)"
+
+ trEthRecvProtocolViolation* =
+ "<< " & prettyEthProtoName & " Protocol violation, "
+ trEthRecvError* =
+ "<< " & prettyEthProtoName & " Error "
+ trEthRecvTimeoutWaiting* =
+ "<< " & prettyEthProtoName & " Timeout waiting "
+ trEthRecvDiscarding* =
+ "<< " & prettyEthProtoName & " Discarding "
+
+ trEthSendSending* =
+ ">> " & prettyEthProtoName & " Sending "
+ trEthSendSendingGetBlockHeaders* =
+ trEthSendSending & "GetBlockHeaders (0x03)"
+ trEthSendSendingGetBlockBodies* =
+ trEthSendSending & "GetBlockBodies (0x05)"
+
+ trEthSendReplying* =
+ ">> " & prettyEthProtoName & " Replying "
+
+ trEthSendDelaying* =
+ ">> " & prettyEthProtoName & " Delaying "
+
+ trEthRecvNewBlock* =
+ "<< " & prettyEthProtoName & " Received NewBlock"
+ trEthRecvNewBlockHashes* =
+ "<< " & prettyEthProtoName & " Received NewBlockHashes"
+ trEthSendNewBlock* =
+ ">> " & prettyEthProtoName & " Sending NewBlock"
+ trEthSendNewBlockHashes* =
+ ">> " & prettyEthProtoName & " Sending NewBlockHashes"
+
+template handleHandlerError(x: untyped) =
+ if x.isErr:
+ raise newException(EthP2PError, x.error)
+
+p2pProtocol eth68(version = ethVersion,
+ rlpxName = "eth",
+ peerState = EthPeerState,
+ networkState = EthWireBase,
+ useRequestIds = true):
+
+ onPeerConnected do (peer: Peer):
+ let
+ network = peer.network
+ ctx = peer.networkState
+ statusRes = ctx.getStatus()
+
+ handleHandlerError(statusRes)
+ let status = statusRes.get
+
+ trace trEthSendSending & "Status (0x00)", peer,
+ td = status.totalDifficulty,
+ bestHash = short(status.bestBlockHash),
+ networkId = network.networkId,
+ genesis = short(status.genesisHash),
+ forkHash = status.forkId.forkHash.toHex,
+ forkNext = status.forkId.forkNext
+
+ let m = await peer.status(ethVersion,
+ network.networkId,
+ status.totalDifficulty,
+ status.bestBlockHash,
+ status.genesisHash,
+ status.forkId,
+ timeout = chronos.seconds(10))
+
+ when trEthTraceHandshakesOk:
+ trace "Handshake: Local and remote networkId",
+ local=network.networkId, remote=m.networkId
+ trace "Handshake: Local and remote genesisHash",
+ local=short(status.genesisHash), remote=short(m.genesisHash)
+ trace "Handshake: Local and remote forkId",
+ local=(status.forkId.forkHash.toHex & "/" & $status.forkId.forkNext),
+ remote=(m.forkId.forkHash.toHex & "/" & $m.forkId.forkNext)
+
+ if m.networkId != network.networkId:
+ trace "Peer for a different network (networkId)", peer,
+ expectNetworkId=network.networkId, gotNetworkId=m.networkId
+ raise newException(
+ UselessPeerError, "Eth handshake for different network")
+
+ if m.genesisHash != status.genesisHash:
+ trace "Peer for a different network (genesisHash)", peer,
+ expectGenesis=short(status.genesisHash), gotGenesis=short(m.genesisHash)
+ raise newException(
+ UselessPeerError, "Eth handshake for different network")
+
+ trace "Peer matches our network", peer
+ peer.state.initialized = true
+ peer.state.bestDifficulty = m.totalDifficulty
+ peer.state.bestBlockHash = m.bestHash
+
+ handshake:
+ # User message 0x00: Status.
+ proc status(peer: Peer,
+ ethVersionArg: uint,
+ networkId: NetworkId,
+ totalDifficulty: DifficultyInt,
+ bestHash: Hash256,
+ genesisHash: Hash256,
+ forkId: ChainForkId) =
+ trace trEthRecvReceived & "Status (0x00)", peer,
+ networkId, totalDifficulty, bestHash=short(bestHash), genesisHash=short(genesisHash),
+ forkHash=forkId.forkHash.toHex, forkNext=forkId.forkNext
+
+ # User message 0x01: NewBlockHashes.
+ proc newBlockHashes(peer: Peer, hashes: openArray[NewBlockHashesAnnounce]) =
+ when trEthTraceGossipOk:
+ trace trEthRecvReceived & "NewBlockHashes (0x01)", peer,
+ hashes=hashes.len
+
+ let ctx = peer.networkState()
+ let res = ctx.handleNewBlockHashes(peer, hashes)
+ handleHandlerError(res)
+
+ # User message 0x02: Transactions.
+ proc transactions(peer: Peer, transactions: openArray[Transaction]) =
+ when trEthTraceGossipOk:
+ trace trEthRecvReceived & "Transactions (0x02)", peer,
+ transactions=transactions.len
+
+ let ctx = peer.networkState()
+ let res = ctx.handleAnnouncedTxs(peer, transactions)
+ handleHandlerError(res)
+
+ requestResponse:
+ # User message 0x03: GetBlockHeaders.
+ proc getBlockHeaders(peer: Peer, request: BlocksRequest) =
+ when trEthTracePacketsOk:
+ trace trEthRecvReceived & "GetBlockHeaders (0x03)", peer,
+ count=request.maxResults
+
+ if request.maxResults > uint64(maxHeadersFetch):
+ debug "GetBlockHeaders (0x03) requested too many headers",
+ peer, requested=request.maxResults, max=maxHeadersFetch
+ await peer.disconnect(BreachOfProtocol)
+ return
+
+ let ctx = peer.networkState()
+ let headers = ctx.getBlockHeaders(request)
+ handleHandlerError(headers)
+ if headers.get.len > 0:
+ trace trEthSendReplying & "with BlockHeaders (0x04)", peer,
+ sent=headers.get.len, requested=request.maxResults
+ else:
+ trace trEthSendReplying & "EMPTY BlockHeaders (0x04)", peer,
+ sent=0, requested=request.maxResults
+
+ await response.send(headers.get)
+
+ # User message 0x04: BlockHeaders.
+ proc blockHeaders(p: Peer, headers: openArray[BlockHeader])
+
+ requestResponse:
+ # User message 0x05: GetBlockBodies.
+ proc getBlockBodies(peer: Peer, hashes: openArray[Hash256]) =
+ trace trEthRecvReceived & "GetBlockBodies (0x05)", peer,
+ hashes=hashes.len
+ if hashes.len > maxBodiesFetch:
+ debug "GetBlockBodies (0x05) requested too many bodies",
+ peer, requested=hashes.len, max=maxBodiesFetch
+ await peer.disconnect(BreachOfProtocol)
+ return
+
+ let ctx = peer.networkState()
+ let bodies = ctx.getBlockBodies(hashes)
+ handleHandlerError(bodies)
+ if bodies.get.len > 0:
+ trace trEthSendReplying & "with BlockBodies (0x06)", peer,
+ sent=bodies.get.len, requested=hashes.len
+ else:
+ trace trEthSendReplying & "EMPTY BlockBodies (0x06)", peer,
+ sent=0, requested=hashes.len
+
+ await response.send(bodies.get)
+
+ # User message 0x06: BlockBodies.
+ proc blockBodies(peer: Peer, blocks: openArray[BlockBody])
+
+ # User message 0x07: NewBlock.
+ proc newBlock(peer: Peer, blk: EthBlock, totalDifficulty: DifficultyInt) =
+ # (Note, needs to use `EthBlock` instead of its alias `NewBlockAnnounce`
+ # because either `p2pProtocol` or RLPx doesn't work with an alias.)
+ when trEthTraceGossipOk:
+ trace trEthRecvReceived & "NewBlock (0x07)", peer,
+ totalDifficulty,
+ blockNumber = blk.header.blockNumber,
+ blockDifficulty = blk.header.difficulty
+
+ let ctx = peer.networkState()
+ let res = ctx.handleNewBlock(peer, blk, totalDifficulty)
+ handleHandlerError(res)
+
+ # User message 0x08: NewPooledTransactionHashes.
+ proc newPooledTransactionHashes(
+ peer: Peer,
+ txTypes: Blob,
+ txSizes: openArray[int],
+ txHashes: openArray[Hash256]
+ ) =
+ when trEthTraceGossipOk:
+ trace trEthRecvReceived & "NewPooledTransactionHashes (0x08)", peer,
+ txTypes=txTypes.toHex, txSizes,
+ hashes=txHashes.len
+
+ let ctx = peer.networkState()
+ let res = ctx.handleAnnouncedTxsHashes(peer, txTypes, txSizes, txHashes)
+ handleHandlerError(res)
+
+ requestResponse:
+ # User message 0x09: GetPooledTransactions.
+ proc getPooledTransactions(peer: Peer, txHashes: openArray[Hash256]) =
+ trace trEthRecvReceived & "GetPooledTransactions (0x09)", peer,
+ hashes=txHashes.len
+
+ let ctx = peer.networkState()
+ let txs = ctx.getPooledTxs(txHashes)
+ handleHandlerError(txs)
+ if txs.get.len > 0:
+ trace trEthSendReplying & "with PooledTransactions (0x0a)", peer,
+ sent=txs.get.len, requested=txHashes.len
+ else:
+ trace trEthSendReplying & "EMPTY PooledTransactions (0x0a)", peer,
+ sent=0, requested=txHashes.len
+
+ await response.send(txs.get)
+
+ # User message 0x0a: PooledTransactions.
+ proc pooledTransactions(peer: Peer, transactions: openArray[Transaction])
+
+ # User message 0x0d: GetNodeData -- removed, was so 66ish
+ # User message 0x0e: NodeData -- removed, was so 66ish
+
+ nextId 0x0f
+
+ requestResponse:
+ # User message 0x0f: GetReceipts.
+ proc getReceipts(peer: Peer, hashes: openArray[Hash256]) =
+ trace trEthRecvReceived & "GetReceipts (0x0f)", peer,
+ hashes=hashes.len
+
+ let ctx = peer.networkState()
+ let rec = ctx.getReceipts(hashes)
+ handleHandlerError(rec)
+ if rec.get.len > 0:
+ trace trEthSendReplying & "with Receipts (0x10)", peer,
+ sent=rec.get.len, requested=hashes.len
+ else:
+ trace trEthSendReplying & "EMPTY Receipts (0x10)", peer,
+ sent=0, requested=hashes.len
+
+ await response.send(rec.get)
+
+ # User message 0x10: Receipts.
+ proc receipts(peer: Peer, receipts: openArray[seq[Receipt]])
diff --git a/nimbus/vm_compile_info.nim b/nimbus/vm_compile_info.nim
index 262491a74..2c4249508 100644
--- a/nimbus/vm_compile_info.nim
+++ b/nimbus/vm_compile_info.nim
@@ -1,5 +1,5 @@
# Nimbus
-# Copyright (c) 2018 Status Research & Development GmbH
+# 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)
@@ -18,8 +18,12 @@ const
VmName* = vmName()
warningMsg = block:
var rc = "*** Compiling with " & VmName
- when defined(legacy_eth66_enabled):
- rc &= ", legacy-eth/66"
+ when defined(eth66_enabled):
+ rc &= ", eth/66"
+ when defined(eth67_enabled):
+ rc &= ", eth/67"
+ when defined(eth68_enabled):
+ rc &= ", eth/68"
when defined(chunked_rlpx_enabled):
rc &= ", chunked-rlpx"
when defined(boehmgc):
diff --git a/tests/all_tests.nim b/tests/all_tests.nim
index b6e64c620..55b5fb765 100644
--- a/tests/all_tests.nim
+++ b/tests/all_tests.nim
@@ -14,8 +14,8 @@ cliBuilder:
./test_accounts_cache,
./test_aristo,
./test_coredb,
- ./test_sync_snap,
- ./test_rocksdb_timing,
+ #./test_sync_snap, -- temporarily suspended
+ #./test_rocksdb_timing, -- temporarily suspended
./test_jwt_auth,
./test_gas_meter,
./test_memory,