2023-12-01 16:20:52 +00:00
|
|
|
# Fluffy
|
2024-01-25 10:04:09 +00:00
|
|
|
# Copyright (c) 2021-2024 Status Research & Development GmbH
|
2021-06-16 19:18:45 +00:00
|
|
|
# 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.
|
|
|
|
|
2023-01-31 12:38:08 +00:00
|
|
|
{.push raises: [].}
|
2021-06-16 19:18:45 +00:00
|
|
|
|
|
|
|
import
|
2021-09-28 17:58:41 +00:00
|
|
|
std/os,
|
2024-02-28 17:31:45 +00:00
|
|
|
confutils,
|
|
|
|
confutils/std/net,
|
|
|
|
chronicles,
|
|
|
|
chronicles/topics_registry,
|
|
|
|
chronos,
|
|
|
|
metrics,
|
|
|
|
metrics/chronos_httpserver,
|
|
|
|
json_rpc/clients/httpclient,
|
|
|
|
json_rpc/rpcproxy,
|
|
|
|
stew/[byteutils, io2, results],
|
|
|
|
eth/keys,
|
|
|
|
eth/net/nat,
|
2021-06-16 19:18:45 +00:00
|
|
|
eth/p2p/discoveryv5/protocol as discv5_protocol,
|
2023-02-26 18:18:03 +00:00
|
|
|
beacon_chain/beacon_clock,
|
|
|
|
beacon_chain/spec/forks,
|
|
|
|
beacon_chain/spec/datatypes/altair,
|
|
|
|
beacon_chain/gossip_processing/light_client_processor,
|
2024-02-28 17:31:45 +00:00
|
|
|
./conf,
|
|
|
|
./network_metadata,
|
|
|
|
./common/common_utils,
|
|
|
|
./rpc/
|
|
|
|
[rpc_web3_api, rpc_eth_api, rpc_discovery_api, rpc_portal_api, rpc_portal_debug_api],
|
2021-09-28 17:58:41 +00:00
|
|
|
./network/state/[state_network, state_content],
|
2021-10-09 11:22:03 +00:00
|
|
|
./network/history/[history_network, history_content],
|
2024-02-28 17:31:45 +00:00
|
|
|
./network/beacon/[beacon_init_loader, beacon_light_client],
|
2022-01-18 08:01:22 +00:00
|
|
|
./network/wire/[portal_stream, portal_protocol_config],
|
2023-01-05 14:26:58 +00:00
|
|
|
./eth_data/history_data_ssz_e2s,
|
2023-12-01 16:20:52 +00:00
|
|
|
./database/content_db,
|
2024-02-28 17:31:45 +00:00
|
|
|
./version,
|
|
|
|
./logging
|
2023-04-19 15:01:01 +00:00
|
|
|
|
2024-02-28 17:31:45 +00:00
|
|
|
chronicles.formatIt(IoErrorCode):
|
|
|
|
$it
|
2021-07-02 05:30:48 +00:00
|
|
|
|
2023-09-28 16:16:41 +00:00
|
|
|
# Application callbacks used when new finalized header or optimistic header is
|
|
|
|
# available.
|
|
|
|
proc onFinalizedHeader(
|
2024-02-28 17:31:45 +00:00
|
|
|
lightClient: LightClient, finalizedHeader: ForkedLightClientHeader
|
|
|
|
) =
|
2023-09-28 16:16:41 +00:00
|
|
|
withForkyHeader(finalizedHeader):
|
|
|
|
when lcDataFork > LightClientDataFork.None:
|
2024-02-28 17:31:45 +00:00
|
|
|
info "New LC finalized header", finalized_header = shortLog(forkyHeader)
|
2023-09-28 16:16:41 +00:00
|
|
|
|
|
|
|
proc onOptimisticHeader(
|
2024-02-28 17:31:45 +00:00
|
|
|
lightClient: LightClient, optimisticHeader: ForkedLightClientHeader
|
|
|
|
) =
|
2023-09-28 16:16:41 +00:00
|
|
|
withForkyHeader(optimisticHeader):
|
|
|
|
when lcDataFork > LightClientDataFork.None:
|
2024-02-28 17:31:45 +00:00
|
|
|
info "New LC optimistic header", optimistic_header = shortLog(forkyHeader)
|
2023-03-17 09:19:17 +00:00
|
|
|
|
2023-03-13 20:30:57 +00:00
|
|
|
proc run(config: PortalConf) {.raises: [CatchableError].} =
|
2023-04-19 15:01:01 +00:00
|
|
|
setupLogging(config.logLevel, config.logStdout)
|
|
|
|
|
2024-02-28 17:31:45 +00:00
|
|
|
notice "Launching Fluffy", version = fullVersionStr, cmdParams = commandLineParams()
|
2023-04-19 15:01:01 +00:00
|
|
|
|
2022-03-18 12:06:57 +00:00
|
|
|
# Make sure dataDir exists
|
|
|
|
let pathExists = createPath(config.dataDir.string)
|
|
|
|
if pathExists.isErr():
|
2024-02-28 17:31:45 +00:00
|
|
|
fatal "Failed to create data directory",
|
|
|
|
dataDir = config.dataDir, error = pathExists.error
|
2022-03-18 12:06:57 +00:00
|
|
|
quit 1
|
|
|
|
|
2021-06-16 19:18:45 +00:00
|
|
|
let
|
|
|
|
rng = newRng()
|
|
|
|
bindIp = config.listenAddress
|
|
|
|
udpPort = Port(config.udpPort)
|
|
|
|
# TODO: allow for no TCP port mapping!
|
|
|
|
(extIp, _, extUdpPort) =
|
2024-02-28 17:31:45 +00:00
|
|
|
try:
|
|
|
|
setupAddress(config.nat, config.listenAddress, udpPort, udpPort, "fluffy")
|
|
|
|
except CatchableError as exc:
|
|
|
|
raise exc # TODO: Ideally we don't have the Exception here
|
|
|
|
except Exception as exc:
|
|
|
|
raiseAssert exc.msg
|
2023-09-25 19:08:10 +00:00
|
|
|
(netkey, newNetKey) =
|
2022-02-17 13:13:39 +00:00
|
|
|
if config.networkKey.isSome():
|
2023-09-25 19:08:10 +00:00
|
|
|
(config.networkKey.get(), true)
|
2022-02-17 13:13:39 +00:00
|
|
|
else:
|
2023-09-25 19:08:10 +00:00
|
|
|
getPersistentNetKey(rng[], config.networkKeyFile)
|
|
|
|
|
|
|
|
enrFilePath = config.dataDir / "fluffy_node.enr"
|
|
|
|
previousEnr =
|
|
|
|
if not newNetKey:
|
|
|
|
getPersistentEnr(enrFilePath)
|
|
|
|
else:
|
|
|
|
Opt.none(enr.Record)
|
2021-06-16 19:18:45 +00:00
|
|
|
|
2021-11-24 11:12:25 +00:00
|
|
|
var bootstrapRecords: seq[Record]
|
|
|
|
loadBootstrapFile(string config.bootstrapNodesFile, bootstrapRecords)
|
|
|
|
bootstrapRecords.add(config.bootstrapNodes)
|
|
|
|
|
2024-03-05 12:45:48 +00:00
|
|
|
var portalNetwork: PortalNetwork
|
|
|
|
if config.portalNetworkDeprecated.isSome():
|
|
|
|
warn "DEPRECATED: The --network flag will be removed in the future, please use the drop in replacement --portal-network flag instead"
|
|
|
|
portalNetwork = config.portalNetworkDeprecated.get()
|
|
|
|
else:
|
|
|
|
portalNetwork = config.portalNetwork
|
|
|
|
|
|
|
|
case portalNetwork
|
2022-03-17 17:39:24 +00:00
|
|
|
of testnet0:
|
|
|
|
for enrURI in testnet0BootstrapNodes:
|
|
|
|
var record: Record
|
|
|
|
if fromURI(record, enrURI):
|
|
|
|
bootstrapRecords.add(record)
|
|
|
|
else:
|
|
|
|
discard
|
|
|
|
|
2022-02-02 21:48:33 +00:00
|
|
|
let
|
2024-02-28 17:31:45 +00:00
|
|
|
discoveryConfig =
|
|
|
|
DiscoveryConfig.init(config.tableIpLimit, config.bucketIpLimit, config.bitsPerHop)
|
2022-02-02 21:48:33 +00:00
|
|
|
d = newProtocol(
|
2022-02-17 13:13:39 +00:00
|
|
|
netkey,
|
2024-02-28 17:31:45 +00:00
|
|
|
extIp,
|
|
|
|
none(Port),
|
|
|
|
extUdpPort,
|
2023-09-20 10:19:40 +00:00
|
|
|
# Note: The addition of default clientInfo to the ENR is a temporary
|
|
|
|
# measure to easily identify & debug the clients used in the testnet.
|
|
|
|
# Might make this into a, default off, cli option.
|
|
|
|
localEnrFields = {"c": enrClientInfoShort},
|
2022-02-02 21:48:33 +00:00
|
|
|
bootstrapRecords = bootstrapRecords,
|
2024-02-28 17:31:45 +00:00
|
|
|
previousRecord =
|
|
|
|
# TODO: discv5/enr code still uses Option, to be changed.
|
2023-09-25 19:08:10 +00:00
|
|
|
if previousEnr.isSome():
|
|
|
|
some(previousEnr.get())
|
|
|
|
else:
|
2024-02-28 17:31:45 +00:00
|
|
|
none(enr.Record)
|
|
|
|
,
|
|
|
|
bindIp = bindIp,
|
|
|
|
bindPort = udpPort,
|
2022-02-02 21:48:33 +00:00
|
|
|
enrAutoUpdate = config.enrAutoUpdate,
|
|
|
|
config = discoveryConfig,
|
2024-02-28 17:31:45 +00:00
|
|
|
rng = rng,
|
|
|
|
)
|
2021-06-16 19:18:45 +00:00
|
|
|
|
|
|
|
d.open()
|
|
|
|
|
2023-11-21 15:16:15 +00:00
|
|
|
# Force pruning
|
2023-11-23 10:44:57 +00:00
|
|
|
if config.forcePrune:
|
2024-02-28 17:31:45 +00:00
|
|
|
let db = ContentDB.new(
|
|
|
|
config.dataDir / "db" / "contentdb_" &
|
|
|
|
d.localNode.id.toBytesBE().toOpenArray(0, 8).toHex(),
|
2023-11-21 15:16:15 +00:00
|
|
|
storageCapacity = config.storageCapacityMB * 1_000_000,
|
2024-02-28 17:31:45 +00:00
|
|
|
manualCheckpoint = true,
|
|
|
|
)
|
2023-11-23 10:44:57 +00:00
|
|
|
|
|
|
|
let radius =
|
|
|
|
if config.radiusConfig.kind == Static:
|
|
|
|
UInt256.fromLogRadius(config.radiusConfig.logRadius)
|
|
|
|
else:
|
|
|
|
let oldRadiusApproximation = db.getLargestDistance(d.localNode.id)
|
|
|
|
db.estimateNewRadius(oldRadiusApproximation)
|
|
|
|
|
|
|
|
# Note: In the case of dynamical radius this is all an approximation that
|
|
|
|
# heavily relies on uniformly distributed content and thus will always
|
|
|
|
# have an error margin, either down or up of the requested capacity.
|
|
|
|
# TODO I: Perhaps we want to add an offset to counter the latter.
|
|
|
|
# TODO II: Perhaps for dynamical radius, we want to also apply the vacuum
|
|
|
|
# without the forcePrune flag and purely by checking the amount of free
|
2024-04-02 18:50:05 +00:00
|
|
|
# space versus the pruning fraction. The problem with this is that the
|
|
|
|
# vacuum will temporarily double the space usage (WAL + DB) and thus to do
|
|
|
|
# this automatically without user requesting it could be dangerous.
|
2023-11-23 10:44:57 +00:00
|
|
|
# TODO III: Adding Radius metadata to the db could be yet another way to
|
|
|
|
# decide whether or not to force prune, instead of this flag.
|
|
|
|
db.forcePrune(d.localNode.id, radius)
|
2023-11-21 15:16:15 +00:00
|
|
|
db.close()
|
|
|
|
|
2021-09-28 17:58:41 +00:00
|
|
|
# Store the database at contentdb prefixed with the first 8 chars of node id.
|
|
|
|
# This is done because the content in the db is dependant on the `NodeId` and
|
|
|
|
# the selected `Radius`.
|
2022-01-14 15:07:14 +00:00
|
|
|
let
|
2024-02-28 17:31:45 +00:00
|
|
|
db = ContentDB.new(
|
|
|
|
config.dataDir / "db" / "contentdb_" &
|
|
|
|
d.localNode.id.toBytesBE().toOpenArray(0, 8).toHex(),
|
|
|
|
storageCapacity = config.storageCapacityMB * 1_000_000,
|
|
|
|
)
|
2021-09-28 17:58:41 +00:00
|
|
|
|
2022-01-18 08:01:22 +00:00
|
|
|
portalConfig = PortalProtocolConfig.init(
|
2024-02-28 17:31:45 +00:00
|
|
|
config.tableIpLimit, config.bucketIpLimit, config.bitsPerHop, config.radiusConfig,
|
|
|
|
config.disablePoke,
|
2022-05-12 16:04:37 +00:00
|
|
|
)
|
2022-08-17 07:32:06 +00:00
|
|
|
streamManager = StreamManager.new(d)
|
|
|
|
|
2024-02-16 22:38:58 +00:00
|
|
|
stateNetwork =
|
2024-03-05 12:45:48 +00:00
|
|
|
if Network.state in config.networks:
|
2024-02-28 17:31:45 +00:00
|
|
|
Opt.some(
|
|
|
|
StateNetwork.new(
|
|
|
|
d,
|
|
|
|
db,
|
|
|
|
streamManager,
|
2024-02-16 22:38:58 +00:00
|
|
|
bootstrapRecords = bootstrapRecords,
|
2024-02-28 17:31:45 +00:00
|
|
|
portalConfig = portalConfig,
|
|
|
|
)
|
|
|
|
)
|
2024-02-16 22:38:58 +00:00
|
|
|
else:
|
|
|
|
Opt.none(StateNetwork)
|
2021-10-09 11:22:03 +00:00
|
|
|
|
2022-10-17 18:38:51 +00:00
|
|
|
accumulator =
|
|
|
|
# Building an accumulator from header epoch files takes > 2m30s and is
|
|
|
|
# thus not really a viable option at start-up.
|
|
|
|
# Options are:
|
|
|
|
# - Start with baked-in accumulator
|
|
|
|
# - Start with file containing SSZ encoded accumulator
|
|
|
|
if config.accumulatorFile.isSome():
|
|
|
|
readAccumulator(string config.accumulatorFile.get()).expect(
|
2024-02-28 17:31:45 +00:00
|
|
|
"Need a file with a valid SSZ encoded accumulator"
|
|
|
|
)
|
2022-10-17 18:38:51 +00:00
|
|
|
else:
|
|
|
|
# Get it from binary file containing SSZ encoded accumulator
|
2024-03-19 15:45:32 +00:00
|
|
|
loadAccumulator()
|
2022-10-17 18:38:51 +00:00
|
|
|
|
2024-03-05 12:45:48 +00:00
|
|
|
historyNetwork =
|
|
|
|
if Network.history in config.networks:
|
|
|
|
Opt.some(
|
|
|
|
HistoryNetwork.new(
|
|
|
|
d,
|
|
|
|
db,
|
|
|
|
streamManager,
|
|
|
|
accumulator,
|
|
|
|
bootstrapRecords = bootstrapRecords,
|
|
|
|
portalConfig = portalConfig,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
else:
|
|
|
|
Opt.none(HistoryNetwork)
|
2023-03-17 09:19:17 +00:00
|
|
|
|
|
|
|
beaconLightClient =
|
|
|
|
# TODO: Currently disabled by default as it is not sufficiently polished.
|
|
|
|
# Eventually this should be always-on functionality.
|
2024-03-05 12:45:48 +00:00
|
|
|
if Network.beacon in config.networks and config.trustedBlockRoot.isSome():
|
2023-03-17 09:19:17 +00:00
|
|
|
let
|
2023-09-28 16:16:41 +00:00
|
|
|
# Portal works only over mainnet data currently
|
2023-03-17 09:19:17 +00:00
|
|
|
networkData = loadNetworkData("mainnet")
|
2024-02-28 17:31:45 +00:00
|
|
|
beaconDb = BeaconDb.new(networkData, config.dataDir / "db" / "beacon_db")
|
2023-10-20 10:06:25 +00:00
|
|
|
beaconNetwork = BeaconNetwork.new(
|
2023-03-17 09:19:17 +00:00
|
|
|
d,
|
2023-10-20 10:06:25 +00:00
|
|
|
beaconDb,
|
2023-03-17 09:19:17 +00:00
|
|
|
streamManager,
|
|
|
|
networkData.forks,
|
2023-10-05 06:57:45 +00:00
|
|
|
bootstrapRecords = bootstrapRecords,
|
2024-02-28 17:31:45 +00:00
|
|
|
portalConfig = portalConfig,
|
|
|
|
)
|
2023-03-17 09:19:17 +00:00
|
|
|
|
2023-10-20 10:06:25 +00:00
|
|
|
let beaconLightClient = LightClient.new(
|
2024-02-28 17:31:45 +00:00
|
|
|
beaconNetwork, rng, networkData, LightClientFinalizationMode.Optimistic
|
|
|
|
)
|
2023-09-28 16:16:41 +00:00
|
|
|
|
2023-10-20 10:06:25 +00:00
|
|
|
beaconLightClient.onFinalizedHeader = onFinalizedHeader
|
|
|
|
beaconLightClient.onOptimisticHeader = onOptimisticHeader
|
|
|
|
beaconLightClient.trustedBlockRoot = config.trustedBlockRoot
|
2023-09-28 16:16:41 +00:00
|
|
|
|
|
|
|
# TODO:
|
|
|
|
# Quite dirty. Use register validate callbacks instead. Or, revisit
|
|
|
|
# the object relationships regarding the beacon light client.
|
2023-10-20 10:06:25 +00:00
|
|
|
beaconNetwork.processor = beaconLightClient.processor
|
2023-09-28 16:16:41 +00:00
|
|
|
|
2023-10-20 10:06:25 +00:00
|
|
|
Opt.some(beaconLightClient)
|
2023-03-17 09:19:17 +00:00
|
|
|
else:
|
|
|
|
Opt.none(LightClient)
|
2022-02-11 13:43:10 +00:00
|
|
|
|
2021-11-24 11:12:25 +00:00
|
|
|
# TODO: If no new network key is generated then we should first check if an
|
|
|
|
# enr file exists, and in the case it does read out the seqNum from it and
|
|
|
|
# reuse that.
|
|
|
|
let enrFile = config.dataDir / "fluffy_node.enr"
|
|
|
|
if io2.writeFile(enrFile, d.localNode.record.toURI()).isErr:
|
|
|
|
fatal "Failed to write the enr file", file = enrFile
|
|
|
|
quit 1
|
2021-06-16 19:18:45 +00:00
|
|
|
|
2023-03-17 09:19:17 +00:00
|
|
|
## Start metrics HTTP server
|
2021-06-16 19:18:45 +00:00
|
|
|
if config.metricsEnabled:
|
|
|
|
let
|
|
|
|
address = config.metricsAddress
|
|
|
|
port = config.metricsPort
|
2022-03-18 12:06:57 +00:00
|
|
|
info "Starting metrics HTTP server",
|
2021-06-16 19:18:45 +00:00
|
|
|
url = "http://" & $address & ":" & $port & "/metrics"
|
|
|
|
try:
|
|
|
|
chronos_httpserver.startMetricsHttpServer($address, port)
|
2024-02-28 17:31:45 +00:00
|
|
|
except CatchableError as exc:
|
|
|
|
raise exc
|
2021-06-16 19:18:45 +00:00
|
|
|
# TODO: Ideally we don't have the Exception here
|
2024-02-28 17:31:45 +00:00
|
|
|
except Exception as exc:
|
|
|
|
raiseAssert exc.msg
|
2021-06-16 19:18:45 +00:00
|
|
|
|
2023-03-17 09:19:17 +00:00
|
|
|
## Starting the different networks.
|
|
|
|
d.start()
|
|
|
|
if stateNetwork.isSome():
|
|
|
|
stateNetwork.get().start()
|
|
|
|
if historyNetwork.isSome():
|
|
|
|
historyNetwork.get().start()
|
|
|
|
if beaconLightClient.isSome():
|
|
|
|
let lc = beaconLightClient.get()
|
|
|
|
lc.network.start()
|
|
|
|
lc.start()
|
|
|
|
|
2023-09-28 16:16:41 +00:00
|
|
|
proc onSecond(time: Moment) =
|
2024-01-25 10:04:09 +00:00
|
|
|
discard
|
2023-09-28 16:16:41 +00:00
|
|
|
# TODO:
|
|
|
|
# Figure out what to do with this one.
|
2024-01-25 10:04:09 +00:00
|
|
|
# let wallSlot = lc.getBeaconTime().slotOrZero()
|
2023-09-28 16:16:41 +00:00
|
|
|
# lc.updateGossipStatus(wallSlot + 1)
|
|
|
|
|
|
|
|
proc runOnSecondLoop() {.async.} =
|
|
|
|
let sleepTime = chronos.seconds(1)
|
|
|
|
while true:
|
|
|
|
let start = chronos.now(chronos.Moment)
|
|
|
|
await chronos.sleepAsync(sleepTime)
|
|
|
|
let afterSleep = chronos.now(chronos.Moment)
|
|
|
|
let sleepTime = afterSleep - start
|
|
|
|
onSecond(start)
|
|
|
|
let finished = chronos.now(chronos.Moment)
|
|
|
|
let processingTime = finished - afterSleep
|
|
|
|
trace "onSecond task completed", sleepTime, processingTime
|
|
|
|
|
|
|
|
onSecond(Moment.now())
|
|
|
|
|
|
|
|
asyncSpawn runOnSecondLoop()
|
|
|
|
|
2023-03-17 09:19:17 +00:00
|
|
|
## Starting the JSON-RPC APIs
|
2021-06-18 17:20:48 +00:00
|
|
|
if config.rpcEnabled:
|
2021-07-07 12:13:27 +00:00
|
|
|
let ta = initTAddress(config.rpcAddress, config.rpcPort)
|
2024-03-15 09:41:41 +00:00
|
|
|
|
|
|
|
let rpcHttpServer = RpcHttpServer.new()
|
|
|
|
# Note: Set maxRequestBodySize to 4MB instead of 1MB as there are blocks
|
|
|
|
# that reach that limit (in hex, for gossip method).
|
|
|
|
rpcHttpServer.addHttpServer(ta, maxRequestBodySize = 4 * 1_048_576)
|
|
|
|
var rpcHttpServerWithProxy = RpcProxy.new(rpcHttpServer, config.proxyUri)
|
|
|
|
|
2021-08-27 16:04:55 +00:00
|
|
|
rpcHttpServerWithProxy.installDiscoveryApiHandlers(d)
|
2023-04-05 14:14:42 +00:00
|
|
|
rpcHttpServerWithProxy.installWeb3ApiHandlers()
|
2023-03-17 09:19:17 +00:00
|
|
|
if stateNetwork.isSome():
|
|
|
|
rpcHttpServerWithProxy.installPortalApiHandlers(
|
2024-02-28 17:31:45 +00:00
|
|
|
stateNetwork.get().portalProtocol, "state"
|
|
|
|
)
|
2023-03-17 09:19:17 +00:00
|
|
|
if historyNetwork.isSome():
|
2023-10-09 15:49:15 +00:00
|
|
|
rpcHttpServerWithProxy.installEthApiHandlers(
|
2024-02-28 17:31:45 +00:00
|
|
|
historyNetwork.get(), beaconLightClient
|
|
|
|
)
|
2023-03-17 09:19:17 +00:00
|
|
|
rpcHttpServerWithProxy.installPortalApiHandlers(
|
2024-02-28 17:31:45 +00:00
|
|
|
historyNetwork.get().portalProtocol, "history"
|
|
|
|
)
|
2023-03-17 09:19:17 +00:00
|
|
|
rpcHttpServerWithProxy.installPortalDebugApiHandlers(
|
2024-02-28 17:31:45 +00:00
|
|
|
historyNetwork.get().portalProtocol, "history"
|
|
|
|
)
|
2023-03-17 09:19:17 +00:00
|
|
|
if beaconLightClient.isSome():
|
|
|
|
rpcHttpServerWithProxy.installPortalApiHandlers(
|
2024-02-28 17:31:45 +00:00
|
|
|
beaconLightClient.get().network.portalProtocol, "beacon"
|
|
|
|
)
|
2023-03-17 09:19:17 +00:00
|
|
|
# TODO: Test proxy with remote node over HTTPS
|
2021-08-05 06:14:25 +00:00
|
|
|
waitFor rpcHttpServerWithProxy.start()
|
2021-06-18 17:20:48 +00:00
|
|
|
|
2021-06-16 19:18:45 +00:00
|
|
|
runForever()
|
|
|
|
|
|
|
|
when isMainModule:
|
|
|
|
{.pop.}
|
2023-09-07 14:27:13 +00:00
|
|
|
let config = PortalConf.load(
|
|
|
|
version = clientName & " " & fullVersionStr & "\p\p" & nimBanner,
|
2024-02-28 17:31:45 +00:00
|
|
|
copyrightBanner = copyrightBanner,
|
2023-09-07 14:27:13 +00:00
|
|
|
)
|
2023-01-31 12:38:08 +00:00
|
|
|
{.push raises: [].}
|
2021-06-16 19:18:45 +00:00
|
|
|
|
|
|
|
case config.cmd
|
2022-01-18 14:08:02 +00:00
|
|
|
of PortalCmd.noCommand:
|
|
|
|
run(config)
|