2018-04-27 08:53:53 +00:00
|
|
|
# Nimbus
|
|
|
|
# Copyright (c) 2018 Status Research & Development GmbH
|
|
|
|
# Licensed under either of
|
|
|
|
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
|
|
|
|
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
|
|
|
|
# at your option.
|
|
|
|
# This file may not be copied, modified, or distributed except according to
|
|
|
|
# those terms.
|
|
|
|
|
2021-04-21 10:15:06 +00:00
|
|
|
import
|
|
|
|
../nimbus/vm_compile_info
|
|
|
|
|
2018-07-22 21:34:43 +00:00
|
|
|
import
|
2019-04-17 01:56:28 +00:00
|
|
|
os, strutils, net, options,
|
2021-09-11 14:58:01 +00:00
|
|
|
stew/shims/net as stewNet,
|
2019-04-17 01:56:28 +00:00
|
|
|
eth/keys, db/[storage_types, db_chain, select_backend],
|
2019-02-05 19:15:50 +00:00
|
|
|
eth/common as eth_common, eth/p2p as eth_p2p,
|
2019-02-06 18:03:36 +00:00
|
|
|
chronos, json_rpc/rpcserver, chronicles,
|
2021-07-21 20:01:12 +00:00
|
|
|
eth/p2p/rlpx_protocols/les_protocol,
|
2022-03-21 17:12:07 +00:00
|
|
|
./sync/protocol_ethxx,
|
Sync: Move `blockchain_sync` code and use it with `eth/65`
Move `blockchain_sync.nim` from `nim-eth` to `nimbus-eth1`.
This lets `blockchain_sync` use the `eth/65` protocol to synchronise with more
modern peers than before.
Practically, the effect is the sync process runs more quickly and reliably than
before. It finds usable peers, and they are up to date.
Note, this is mostly old code, and it mostly performs "classic sync", the
original Ethereum method. Here's a summary of this code:
- It decides on a blockchain canonical head by sampling a few peers.
- Starting from block 0 (genesis), it downloads each block header and
block, mostly in order.
- After it downloads each block, it executes the EVM transactions in that block
and updates state trie from that, before going to the next block.
- This way the database state is updated by EVM executions in block order,
and new state is persisted to the trie database after each block.
Even though it mentions Geth "fast sync" (comments near end of file), and has
some elements, it isn't really. The most obvious missing part is this code
_doesn't download a state trie_, it calculates all state from block 0.
Geth "fast sync" has several parts:
1. Find an agreed common chain among several peers to treat as probably secure,
and a sufficiently long suffix to provide "statistical economic consensus"
when it is validated.
2. Perform a subset of PoW calculations, skipping forward over a segment to
verify some of the PoWs according to a pattern in the relevant paper.
3. Download the state trie from the block at the start of that last segment.
4. Execute only the blocks/transactions in that last segment, using the
downloaded state trie, to fill out the later states and properly validate the
blocks in the last segment.
Some other issues with `blockchain_sync` code:
- If it ever reaches the head of the chain, it doesn't follow new blocks with
increasing block numbers, at least not rapidly.
- If the chain undergoes a reorg, this code won't fetch a block number it has
already fetched, so it can't accept the reorg. It will end up conflicted
with peers. This hasn't mattered because the development focus has been on
the bulk of the catching up process, not the real-time head and reorgs.
- So it probably doesn't work correctly when it gets close to the head due to
many small reorgs, though it might for subtle reasons.
- Some of the network message handling isn't sufficiently robust, and it
discards some replies that have valid data according to specification.
- On rare occasions the initial query mapping block hash to number can
fail (because the peer's state changes).
- It makes some assumptions about the state of peers based on their responses
which may not be valid (I'm not convinced they are). The method for working
out "trusted" peers that agree a common chain prefix is clever. It compares
peers by asking each peer if it has the header matching another peer's
canonical head block by hash. But it's not clear that merely knowing about a
block constitutes agreement about the canonical chain. (If it did, query by
block number would give the same answer more authoritatively.)
Nonetheless, being able to run this sync process on `eth/65` is useful.
<# interactive rebase in progress; onto 66532e8a
Signed-off-by: Jamie Lokier <jamie@shareable.org>
2021-07-22 13:36:10 +00:00
|
|
|
./p2p/blockchain_sync, eth/net/nat, eth/p2p/peer_pool,
|
2021-10-05 23:31:35 +00:00
|
|
|
./p2p/clique/[clique_desc, clique_sealer],
|
|
|
|
config, genesis, rpc/[common, p2p, debug, engine_api], p2p/chain,
|
2021-04-09 07:26:06 +00:00
|
|
|
eth/trie/db, metrics, metrics/[chronos_httpserver, chronicles_support],
|
2022-01-23 11:39:43 +00:00
|
|
|
graphql/ethapi, context, utils/tx_pool,
|
2022-03-27 11:21:15 +00:00
|
|
|
"."/[conf_utils, sealer, constants, utils, version]
|
2018-07-26 20:08:43 +00:00
|
|
|
|
2021-12-05 11:20:27 +00:00
|
|
|
when defined(evmc_enabled):
|
|
|
|
import transaction/evmc_dynamic_loader
|
|
|
|
|
2018-06-20 17:27:32 +00:00
|
|
|
## TODO:
|
|
|
|
## * No IPv6 support
|
|
|
|
## * No multiple bind addresses support
|
|
|
|
## * No database support
|
|
|
|
|
|
|
|
type
|
|
|
|
NimbusState = enum
|
2020-05-21 01:33:11 +00:00
|
|
|
Starting, Running, Stopping
|
2018-06-20 17:27:32 +00:00
|
|
|
|
2020-03-25 18:00:04 +00:00
|
|
|
NimbusNode = ref object
|
2021-09-07 13:45:01 +00:00
|
|
|
rpcServer: RpcHttpServer
|
2021-10-05 23:31:35 +00:00
|
|
|
engineApiServer: RpcHttpServer
|
2022-01-24 13:08:33 +00:00
|
|
|
engineApiWsServer: RpcWebSocketServer
|
2021-09-07 13:45:01 +00:00
|
|
|
ethNode: EthereumNode
|
|
|
|
state: NimbusState
|
|
|
|
graphqlServer: GraphqlHttpServerRef
|
|
|
|
wsRpcServer: RpcWebSocketServer
|
|
|
|
sealingEngine: SealingEngineRef
|
|
|
|
ctx: EthContext
|
2021-09-16 15:59:46 +00:00
|
|
|
chainRef: Chain
|
2022-01-23 11:39:43 +00:00
|
|
|
txPool: TxPoolRef
|
2018-06-20 17:27:32 +00:00
|
|
|
|
2021-09-16 15:59:46 +00:00
|
|
|
proc importBlocks(conf: NimbusConf, chainDB: BaseChainDB) =
|
|
|
|
if string(conf.blocksFile).len > 0:
|
2021-05-17 11:43:44 +00:00
|
|
|
# success or not, we quit after importing blocks
|
2021-09-16 15:59:46 +00:00
|
|
|
if not importRlpBlock(string conf.blocksFile, chainDB):
|
2021-05-17 11:43:44 +00:00
|
|
|
quit(QuitFailure)
|
|
|
|
else:
|
|
|
|
quit(QuitSuccess)
|
2021-03-18 15:05:15 +00:00
|
|
|
|
2021-09-16 15:59:46 +00:00
|
|
|
proc manageAccounts(nimbus: NimbusNode, conf: NimbusConf) =
|
2021-09-11 14:58:01 +00:00
|
|
|
if string(conf.keyStore).len > 0:
|
|
|
|
let res = nimbus.ctx.am.loadKeystores(string conf.keyStore)
|
2021-09-07 13:45:01 +00:00
|
|
|
if res.isErr:
|
|
|
|
echo res.error()
|
|
|
|
quit(QuitFailure)
|
|
|
|
|
2021-09-11 14:58:01 +00:00
|
|
|
if string(conf.importKey).len > 0:
|
|
|
|
let res = nimbus.ctx.am.importPrivateKey(string conf.importKey)
|
2021-09-07 13:45:01 +00:00
|
|
|
if res.isErr:
|
|
|
|
echo res.error()
|
|
|
|
quit(QuitFailure)
|
2021-03-18 15:05:15 +00:00
|
|
|
|
2021-09-16 15:59:46 +00:00
|
|
|
proc setupP2P(nimbus: NimbusNode, conf: NimbusConf,
|
|
|
|
chainDB: BaseChainDB, protocols: set[ProtocolFlag]) =
|
2018-06-20 17:27:32 +00:00
|
|
|
## Creating P2P Server
|
2021-09-11 14:58:01 +00:00
|
|
|
let kpres = nimbus.ctx.hexToKeyPair(conf.nodeKeyHex)
|
2021-09-07 13:45:01 +00:00
|
|
|
if kpres.isErr:
|
|
|
|
echo kpres.error()
|
|
|
|
quit(QuitFailure)
|
|
|
|
|
|
|
|
let keypair = kpres.get()
|
2021-09-11 14:58:01 +00:00
|
|
|
var address = Address(
|
|
|
|
ip: conf.listenAddress,
|
|
|
|
tcpPort: conf.tcpPort,
|
|
|
|
udpPort: conf.udpPort
|
|
|
|
)
|
|
|
|
|
|
|
|
if conf.nat.hasExtIp:
|
|
|
|
# any required port redirection is assumed to be done by hand
|
|
|
|
address.ip = conf.nat.extIp
|
2019-04-17 01:56:28 +00:00
|
|
|
else:
|
|
|
|
# automated NAT traversal
|
2021-09-11 14:58:01 +00:00
|
|
|
let extIP = getExternalIP(conf.nat.nat)
|
2019-04-17 23:17:06 +00:00
|
|
|
# This external IP only appears in the logs, so don't worry about dynamic
|
|
|
|
# IPs. Don't remove it either, because the above call does initialisation
|
|
|
|
# and discovery for NAT-related objects.
|
2019-04-17 01:56:28 +00:00
|
|
|
if extIP.isSome:
|
|
|
|
address.ip = extIP.get()
|
|
|
|
let extPorts = redirectPorts(tcpPort = address.tcpPort,
|
2021-09-11 14:58:01 +00:00
|
|
|
udpPort = address.udpPort,
|
2022-03-27 11:21:15 +00:00
|
|
|
description = NimbusName & " " & NimbusVersion)
|
2019-04-17 01:56:28 +00:00
|
|
|
if extPorts.isSome:
|
|
|
|
(address.tcpPort, address.udpPort) = extPorts.get()
|
2018-06-20 17:27:32 +00:00
|
|
|
|
2022-03-16 08:01:35 +00:00
|
|
|
let bootstrapNodes = conf.getBootNodes()
|
|
|
|
|
|
|
|
nimbus.ethNode = newEthereumNode(
|
|
|
|
keypair, address, conf.networkId, nil, conf.agentString,
|
|
|
|
addAllCapabilities = false, minPeers = conf.maxPeers,
|
|
|
|
bootstrapNodes = bootstrapNodes,
|
|
|
|
bindUdpPort = conf.udpPort, bindTcpPort = conf.tcpPort,
|
|
|
|
bindIp = conf.listenAddress)
|
|
|
|
|
2019-04-23 13:49:49 +00:00
|
|
|
# Add protocol capabilities based on protocol flags
|
2021-09-16 15:59:46 +00:00
|
|
|
if ProtocolFlag.Eth in protocols:
|
2019-04-23 13:49:49 +00:00
|
|
|
nimbus.ethNode.addCapability eth
|
2021-09-16 15:59:46 +00:00
|
|
|
if ProtocolFlag.Les in protocols:
|
2019-04-23 13:49:49 +00:00
|
|
|
nimbus.ethNode.addCapability les
|
2018-06-20 17:27:32 +00:00
|
|
|
|
2021-07-30 14:06:51 +00:00
|
|
|
# chainRef: some name to avoid module-name/filed/function misunderstandings
|
2021-09-16 15:59:46 +00:00
|
|
|
nimbus.chainRef = newChain(chainDB)
|
|
|
|
nimbus.ethNode.chain = nimbus.chainRef
|
2021-09-11 14:58:01 +00:00
|
|
|
if conf.verifyFrom.isSome:
|
|
|
|
let verifyFrom = conf.verifyFrom.get()
|
2021-09-16 15:59:46 +00:00
|
|
|
nimbus.chainRef.extraValidation = 0 < verifyFrom
|
|
|
|
nimbus.chainRef.verifyFrom = verifyFrom
|
|
|
|
|
|
|
|
# Connect directly to the static nodes
|
|
|
|
let staticPeers = conf.getStaticPeers()
|
|
|
|
for enode in staticPeers:
|
|
|
|
asyncCheck nimbus.ethNode.peerPool.connectToNode(newNode(enode))
|
|
|
|
|
2022-03-16 08:01:35 +00:00
|
|
|
# Start Eth node
|
2022-03-23 14:07:11 +00:00
|
|
|
if conf.maxPeers > 0:
|
|
|
|
waitFor nimbus.ethNode.connectToNetwork(
|
|
|
|
enableDiscovery = conf.discovery != DiscoveryType.None)
|
2021-09-16 15:59:46 +00:00
|
|
|
|
|
|
|
proc localServices(nimbus: NimbusNode, conf: NimbusConf,
|
|
|
|
chainDB: BaseChainDB, protocols: set[ProtocolFlag]) =
|
2022-01-23 11:39:43 +00:00
|
|
|
|
|
|
|
# app wide TxPool singleton
|
|
|
|
# TODO: disable some of txPool internal mechanism if
|
|
|
|
# the engineSigner is zero.
|
|
|
|
nimbus.txPool = TxPoolRef.new(chainDB, conf.engineSigner)
|
|
|
|
|
2021-09-16 15:59:46 +00:00
|
|
|
# metrics logging
|
|
|
|
if conf.logMetricsEnabled:
|
|
|
|
# https://github.com/nim-lang/Nim/issues/17369
|
|
|
|
var logMetrics: proc(udata: pointer) {.gcsafe, raises: [Defect].}
|
|
|
|
logMetrics = proc(udata: pointer) =
|
|
|
|
{.gcsafe.}:
|
|
|
|
let registry = defaultRegistry
|
|
|
|
info "metrics", registry
|
|
|
|
discard setTimer(Moment.fromNow(conf.logMetricsInterval.seconds), logMetrics)
|
|
|
|
discard setTimer(Moment.fromNow(conf.logMetricsInterval.seconds), logMetrics)
|
2021-09-11 14:58:01 +00:00
|
|
|
|
|
|
|
# Creating RPC Server
|
|
|
|
if conf.rpcEnabled:
|
2021-09-16 15:59:46 +00:00
|
|
|
nimbus.rpcServer = newRpcHttpServer([initTAddress(conf.rpcAddress, conf.rpcPort)])
|
2021-09-07 15:00:21 +00:00
|
|
|
setupCommonRpc(nimbus.ethNode, conf, nimbus.rpcServer)
|
2020-07-22 16:51:26 +00:00
|
|
|
|
2021-09-11 14:58:01 +00:00
|
|
|
# Enable RPC APIs based on RPC flags and protocol flags
|
2021-09-16 15:59:46 +00:00
|
|
|
let rpcFlags = conf.getRpcFlags()
|
2022-01-24 13:08:33 +00:00
|
|
|
if (RpcFlag.Eth in rpcFlags and ProtocolFlag.Eth in protocols) or
|
|
|
|
(conf.engineApiPort == conf.rpcPort):
|
2022-01-23 11:39:43 +00:00
|
|
|
setupEthRpc(nimbus.ethNode, nimbus.ctx, chainDB, nimbus.txPool, nimbus.rpcServer)
|
2021-09-16 15:59:46 +00:00
|
|
|
if RpcFlag.Debug in rpcFlags:
|
2021-09-11 14:58:01 +00:00
|
|
|
setupDebugRpc(chainDB, nimbus.rpcServer)
|
2021-08-05 07:51:28 +00:00
|
|
|
|
2018-06-20 17:27:32 +00:00
|
|
|
nimbus.rpcServer.rpc("admin_quit") do() -> string:
|
2019-07-10 21:23:11 +00:00
|
|
|
{.gcsafe.}:
|
|
|
|
nimbus.state = Stopping
|
2018-06-20 17:27:32 +00:00
|
|
|
result = "EXITING"
|
2021-09-11 14:58:01 +00:00
|
|
|
|
2018-06-20 17:27:32 +00:00
|
|
|
nimbus.rpcServer.start()
|
2020-03-25 18:00:04 +00:00
|
|
|
|
2021-09-11 14:58:01 +00:00
|
|
|
# Creating Websocket RPC Server
|
|
|
|
if conf.wsEnabled:
|
2021-09-16 15:59:46 +00:00
|
|
|
nimbus.wsRpcServer = newRpcWebSocketServer(initTAddress(conf.wsAddress, conf.wsPort))
|
2021-09-11 14:58:01 +00:00
|
|
|
setupCommonRpc(nimbus.ethNode, conf, nimbus.wsRpcServer)
|
|
|
|
|
|
|
|
# Enable Websocket RPC APIs based on RPC flags and protocol flags
|
2021-09-16 15:59:46 +00:00
|
|
|
let wsFlags = conf.getWsFlags()
|
2022-01-24 13:08:33 +00:00
|
|
|
if (RpcFlag.Eth in wsFlags and ProtocolFlag.Eth in protocols) or
|
|
|
|
(conf.engineApiWsPort == conf.wsPort):
|
2022-01-23 11:39:43 +00:00
|
|
|
setupEthRpc(nimbus.ethNode, nimbus.ctx, chainDB, nimbus.txPool, nimbus.wsRpcServer)
|
2021-09-16 15:59:46 +00:00
|
|
|
if RpcFlag.Debug in wsFlags:
|
2021-09-11 14:58:01 +00:00
|
|
|
setupDebugRpc(chainDB, nimbus.wsRpcServer)
|
|
|
|
|
|
|
|
nimbus.wsRpcServer.start()
|
|
|
|
|
|
|
|
if conf.graphqlEnabled:
|
2022-01-23 11:39:43 +00:00
|
|
|
nimbus.graphqlServer = setupGraphqlHttpServer(conf, chainDB, nimbus.ethNode, nimbus.txPool)
|
2021-04-24 04:01:09 +00:00
|
|
|
nimbus.graphqlServer.start()
|
|
|
|
|
2021-08-24 07:34:58 +00:00
|
|
|
if conf.engineSigner != ZERO_ADDRESS:
|
2022-03-08 07:51:27 +00:00
|
|
|
let res = nimbus.ctx.am.getAccount(conf.engineSigner)
|
|
|
|
if res.isErr:
|
|
|
|
error "Failed to get account",
|
|
|
|
msg = res.error,
|
|
|
|
hint = "--key-store or --import-key"
|
|
|
|
quit(QuitFailure)
|
|
|
|
|
2021-09-16 15:59:46 +00:00
|
|
|
let rs = validateSealer(conf, nimbus.ctx, nimbus.chainRef)
|
2021-08-24 07:34:58 +00:00
|
|
|
if rs.isErr:
|
|
|
|
echo rs.error
|
|
|
|
quit(QuitFailure)
|
2021-10-05 23:31:35 +00:00
|
|
|
|
|
|
|
proc signFunc(signer: EthAddress, message: openArray[byte]): Result[RawSignature, cstring] {.gcsafe.} =
|
|
|
|
let
|
|
|
|
hashData = keccakHash(message)
|
|
|
|
acc = nimbus.ctx.am.getAccount(signer).tryGet()
|
|
|
|
rawSign = sign(acc.privateKey, SkMessage(hashData.data)).toRaw
|
|
|
|
|
|
|
|
ok(rawSign)
|
|
|
|
|
|
|
|
# TODO: There should be a better place to initialize this
|
|
|
|
nimbus.chainRef.clique.authorize(conf.engineSigner, signFunc)
|
2022-02-22 08:55:04 +00:00
|
|
|
var initialState = EngineStopped
|
|
|
|
if chainDB.totalDifficulty > chainDB.ttd:
|
|
|
|
initialState = EnginePostMerge
|
2021-09-07 13:45:01 +00:00
|
|
|
nimbus.sealingEngine = SealingEngineRef.new(
|
2022-01-23 11:39:43 +00:00
|
|
|
nimbus.chainRef, nimbus.ctx, conf.engineSigner,
|
2022-02-22 08:55:04 +00:00
|
|
|
nimbus.txPool, initialState
|
2022-01-23 11:39:43 +00:00
|
|
|
)
|
2021-08-24 07:34:58 +00:00
|
|
|
nimbus.sealingEngine.start()
|
|
|
|
|
2021-10-05 23:31:35 +00:00
|
|
|
if conf.engineApiEnabled:
|
2022-01-24 13:08:33 +00:00
|
|
|
if conf.engineApiPort != conf.rpcPort:
|
|
|
|
nimbus.engineApiServer = newRpcHttpServer([
|
|
|
|
initTAddress(conf.engineApiAddress, conf.engineApiPort)
|
|
|
|
])
|
|
|
|
setupEngineAPI(nimbus.sealingEngine, nimbus.engineApiServer)
|
|
|
|
setupEthRpc(nimbus.ethNode, nimbus.ctx, chainDB, nimbus.txPool, nimbus.engineApiServer)
|
|
|
|
nimbus.engineApiServer.start()
|
|
|
|
else:
|
|
|
|
setupEngineAPI(nimbus.sealingEngine, nimbus.rpcServer)
|
|
|
|
|
2021-10-05 23:31:35 +00:00
|
|
|
info "Starting engine API server", port = conf.engineApiPort
|
|
|
|
|
2022-01-24 13:08:33 +00:00
|
|
|
if conf.engineApiWsEnabled:
|
|
|
|
if conf.engineApiWsPort != conf.wsPort:
|
|
|
|
nimbus.engineApiWsServer = newRpcWebSocketServer(
|
|
|
|
initTAddress(conf.engineApiWsAddress, conf.engineApiWsPort))
|
|
|
|
setupEngineAPI(nimbus.sealingEngine, nimbus.engineApiWsServer)
|
|
|
|
setupEthRpc(nimbus.ethNode, nimbus.ctx, chainDB, nimbus.txPool, nimbus.engineApiWsServer)
|
|
|
|
nimbus.engineApiWsServer.start()
|
|
|
|
else:
|
|
|
|
setupEngineAPI(nimbus.sealingEngine, nimbus.wsRpcServer)
|
|
|
|
|
|
|
|
info "Starting WebSocket engine API server", port = conf.engineApiWsPort
|
2022-03-08 07:51:27 +00:00
|
|
|
else:
|
|
|
|
if conf.engineApiEnabled or conf.engineApiWsEnabled:
|
|
|
|
warn "Cannot enable engine API without sealing engine",
|
2022-03-23 14:07:11 +00:00
|
|
|
hint = "use --engine-signer to enable sealing engine"
|
2022-01-24 13:08:33 +00:00
|
|
|
|
2019-07-16 10:43:05 +00:00
|
|
|
# metrics server
|
2021-09-11 14:58:01 +00:00
|
|
|
if conf.metricsEnabled:
|
|
|
|
info "Starting metrics HTTP server", address = conf.metricsAddress, port = conf.metricsPort
|
|
|
|
startMetricsHttpServer($conf.metricsAddress, conf.metricsPort)
|
2019-06-26 12:51:59 +00:00
|
|
|
|
2021-09-16 15:59:46 +00:00
|
|
|
proc start(nimbus: NimbusNode, conf: NimbusConf) =
|
|
|
|
## logging
|
|
|
|
setLogLevel(conf.logLevel)
|
|
|
|
if conf.logFile.isSome:
|
|
|
|
let logFile = string conf.logFile.get()
|
|
|
|
defaultChroniclesStream.output.outFile = nil # to avoid closing stdout
|
|
|
|
discard defaultChroniclesStream.output.open(logFile, fmAppend)
|
2019-04-23 13:49:49 +00:00
|
|
|
|
2021-12-05 11:20:27 +00:00
|
|
|
when defined(evmc_enabled):
|
|
|
|
evmcSetLibraryPath(conf.evm)
|
EVMC: Option `--evm`, load third-party EVM as a shared library
This patch adds:
- Load and use a third-party EVM in a shared library, instead of Nimbus EVM.
- New option `--evm` to specify which library to load.
- The library and this loader conforms to the [EVMC]
(https://evmc.ethereum.org/) 9.x specification.
Any third-party EVM which is compatible with EVMC version 9.x and supports EVM1
contract code will be accepted. The operating system's shared library format
applies. These are `.so*` files on Linux, `.dll` files on Windows and `.dylib`
files on Mac.
The alternative EVM can be selected in two ways:
- Nimbus command line option `--evm:<path>`.
- Environment variable `NIMBUS_EVM=<path>`.
The reason for an environment variable is this allows all the test programs to
run with a third-party EVM as well. Some don't parse command line options.
There are some limitations to be aware of:
- The third-party EVM must use EVMC version 9.x, no other major version.
EVMC 9.x supports EIP-1559 / London fork and older transactions.
- Nested `*CALL` and `CREATE*` operations don't use the third-party EVM yet.
These call the built-in Nimbus EVM. This mixing of different EVMs between
levels is explicitly allowed in specs, so there is no problem doing it.
- The third-party EVM doesn't need to support precompiles, because those are
nested calls, which use the built-in Nimbus EVM.
- Third-party EVMs execute contracts correctly, but fail the final `rootHash`
match. The reason is that some account state changes, which are correct, are
currently inside the Nimbus EVM and need to be moved to EVMC host logic.
*This is a known work in progress*. The EVM execution itself is fine.
Test results using "evmone" third-party EVM:
- [evmone](https://github.com/ethereum/evmone) has been tested. Only on
Linux but it "should" work on Windows and Mac equally well.
- [Version 0.8.1](https://github.com/ethereum/evmone/releases/tag/v0.8.1) was
used because it is compatible with EVMC 9.x, which is required for the
EIP-1559 / London fork, which Nimbus supports. Version 0.8.0 could be used
but it looks like an important bug was fixed in 0.8.1.
- evmone runs fine and the trace output looks good. The calls and arguments
are the same as the built-in Nimbus EVM for tests that have been checked
manually, except evmone skips some calls that can be safely skipped.
- The final `rootHash` is incorrect, due to the *work in progress* mentioned
above which is not part of the evmone execution. Due to this, it's possible
to try evmone and verify expected behaviours, which also validates our own
EVMC implementation, but it can't be used as a full substitute yet.
Signed-off-by: Jamie Lokier <jamie@shareable.org>
2021-12-05 11:20:27 +00:00
|
|
|
|
2021-09-16 15:59:46 +00:00
|
|
|
createDir(string conf.dataDir)
|
|
|
|
let trieDB = trieDB newChainDb(string conf.dataDir)
|
|
|
|
var chainDB = newBaseChainDB(trieDB,
|
|
|
|
conf.pruneMode == PruneMode.Full,
|
|
|
|
conf.networkId,
|
|
|
|
conf.networkParams
|
|
|
|
)
|
|
|
|
chainDB.populateProgress()
|
2018-07-22 21:34:43 +00:00
|
|
|
|
2021-09-16 15:59:46 +00:00
|
|
|
if canonicalHeadHashKey().toOpenArray notin trieDB:
|
|
|
|
initializeEmptyDb(chainDb)
|
|
|
|
doAssert(canonicalHeadHashKey().toOpenArray in trieDB)
|
2018-07-12 11:14:04 +00:00
|
|
|
|
2021-09-16 15:59:46 +00:00
|
|
|
let protocols = conf.getProtocolFlags()
|
|
|
|
|
|
|
|
case conf.cmd
|
|
|
|
of NimbusCmd.`import`:
|
|
|
|
importBlocks(conf, chainDB)
|
|
|
|
else:
|
|
|
|
manageAccounts(nimbus, conf)
|
|
|
|
setupP2P(nimbus, conf, chainDB, protocols)
|
|
|
|
localServices(nimbus, conf, chainDB, protocols)
|
|
|
|
|
|
|
|
if ProtocolFlag.Eth in protocols:
|
|
|
|
# TODO: temp code until the CLI/RPC interface is fleshed out
|
|
|
|
let status = waitFor nimbus.ethNode.fastBlockchainSync()
|
|
|
|
if status != syncSuccess:
|
|
|
|
debug "Block sync failed: ", status
|
|
|
|
|
|
|
|
if nimbus.state == Starting:
|
|
|
|
# it might have been set to "Stopping" with Ctrl+C
|
|
|
|
nimbus.state = Running
|
2018-06-20 17:27:32 +00:00
|
|
|
|
2021-09-11 14:58:01 +00:00
|
|
|
proc stop*(nimbus: NimbusNode, conf: NimbusConf) {.async, gcsafe.} =
|
2018-12-06 23:16:34 +00:00
|
|
|
trace "Graceful shutdown"
|
2021-09-11 14:58:01 +00:00
|
|
|
if conf.rpcEnabled:
|
2021-11-30 07:13:20 +00:00
|
|
|
await nimbus.rpcServer.stop()
|
2021-10-05 23:31:35 +00:00
|
|
|
if conf.engineApiEnabled:
|
|
|
|
await nimbus.engineAPiServer.stop()
|
2021-09-11 14:58:01 +00:00
|
|
|
if conf.wsEnabled:
|
2021-09-16 15:59:46 +00:00
|
|
|
nimbus.wsRpcServer.stop()
|
2022-01-24 13:08:33 +00:00
|
|
|
if conf.engineApiWsEnabled:
|
|
|
|
nimbus.engineApiWsServer.stop()
|
2021-09-11 14:58:01 +00:00
|
|
|
if conf.graphqlEnabled:
|
2021-04-24 04:01:09 +00:00
|
|
|
await nimbus.graphqlServer.stop()
|
2021-08-24 07:34:58 +00:00
|
|
|
if conf.engineSigner != ZERO_ADDRESS:
|
|
|
|
await nimbus.sealingEngine.stop()
|
2018-06-20 17:27:32 +00:00
|
|
|
|
2021-09-11 14:58:01 +00:00
|
|
|
proc process*(nimbus: NimbusNode, conf: NimbusConf) =
|
2020-05-21 01:33:11 +00:00
|
|
|
# Main event loop
|
|
|
|
while nimbus.state == Running:
|
|
|
|
try:
|
|
|
|
poll()
|
|
|
|
except CatchableError as e:
|
|
|
|
debug "Exception in poll()", exc = e.name, err = e.msg
|
2020-07-21 08:12:59 +00:00
|
|
|
discard e # silence warning when chronicles not activated
|
2018-06-20 17:27:32 +00:00
|
|
|
|
2019-07-10 21:23:11 +00:00
|
|
|
# Stop loop
|
2021-09-11 14:58:01 +00:00
|
|
|
waitFor nimbus.stop(conf)
|
2018-04-27 08:53:53 +00:00
|
|
|
|
|
|
|
when isMainModule:
|
2021-09-07 13:45:01 +00:00
|
|
|
var nimbus = NimbusNode(state: Starting, ctx: newEthContext())
|
2020-03-25 18:00:04 +00:00
|
|
|
|
|
|
|
## Ctrl+C handling
|
|
|
|
proc controlCHandler() {.noconv.} =
|
|
|
|
when defined(windows):
|
|
|
|
# workaround for https://github.com/nim-lang/Nim/issues/4057
|
|
|
|
setupForeignThreadGc()
|
|
|
|
nimbus.state = Stopping
|
|
|
|
echo "\nCtrl+C pressed. Waiting for a graceful shutdown."
|
|
|
|
setControlCHook(controlCHandler)
|
|
|
|
|
2019-07-10 21:23:11 +00:00
|
|
|
## Show logs on stdout until we get the user's logging choice
|
2019-04-17 01:56:28 +00:00
|
|
|
discard defaultChroniclesStream.output.open(stdout)
|
|
|
|
|
2018-06-20 17:27:32 +00:00
|
|
|
## Processing command line arguments
|
2021-09-11 14:58:01 +00:00
|
|
|
let conf = makeConfig()
|
2019-07-10 21:23:11 +00:00
|
|
|
|
2021-09-11 14:58:01 +00:00
|
|
|
nimbus.start(conf)
|
|
|
|
nimbus.process(conf)
|