Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -230,8 +230,8 @@ jobs:
- name: Check copyright year
if: ${{ !cancelled() }} && github.event_name == 'pull_request'
run: |
excluded_files="config.yaml|config.nims|\\.gitmodules|config/deployment-settings\\.yaml|tests/fixtures/deployment-settings-empty-scalars\\.yaml|tests/fixtures/deployment-settings-incomplete\\.yaml|tests/fixtures/deployment-settings-malformed\\.yaml|tests/fixtures/deployment-settings-no-leading-slash\\.yaml|tests/fixtures/deployment-settings-wrong-types\\.yaml"
excluded_extensions="ans|bin|cfg|yml|json|json\\.template|md|png|service|ssz|tpl|txt|lock|nix|gitignore|envrc|sh"
excluded_files="config.nims|\\.gitmodules"
excluded_extensions="ans|bin|cfg|yml|yaml|json|json\\.template|md|png|service|ssz|tpl|txt|lock|nix|gitignore|envrc|sh"

current_year=$(date +"%Y")
problematic_files=()
Expand Down
197 changes: 197 additions & 0 deletions config/user_config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
network:
backend:
swarm:
host: 0.0.0.0
port: 3000
node_key: e23ddddb2e2e0ff2cf27517cc2241f6bd8a7bb03c4083ab5271ffe400ceba51e
gossipsub:
history_length: 5
history_gossip: 3
mesh_n: 6
mesh_n_low: 5
mesh_n_high: 12
retain_scores: 4
gossip_lazy: 6
gossip_factor: 0.25
heartbeat_initial_delay:
secs: 5
nanos: 0
heartbeat_interval:
secs: 1
nanos: 0
fanout_ttl:
secs: 60
nanos: 0
check_explicit_peers_ticks: 300
duplicate_cache_time:
secs: 60
nanos: 0
validate_messages: false
allow_self_origin: false
do_px: false
prune_peers: 0
prune_backoff:
secs: 60
nanos: 0
unsubscribe_backoff:
secs: 10
nanos: 0
backoff_slack: 1
flood_publish: true
graft_flood_threshold:
secs: 10
nanos: 0
mesh_outbound_min: 2
opportunistic_graft_ticks: 60
opportunistic_graft_peers: 2
gossip_retransimission: 3
max_messages_per_rpc: null
max_ihave_length: 5000
max_ihave_messages: 10
iwant_followup_time:
secs: 3
nanos: 0
published_message_ids_cache_time:
secs: 10
nanos: 0
kademlia:
query_timeout_secs: null
replication_factor: null
parallelism: null
disjoint_query_paths: null
max_packet_size: null
kbucket_inserts: null
caching: null
periodic_bootstrap_interval_secs: null
identify:
agent_version: null
interval_secs: null
push_listen_addr_updates: null
cache_size: null
hide_listen_addrs: true
chain_sync:
peer_response_timeout: 5000
nat:
type: traversal
autonat:
max_candidates: null
probe_interval_millisecs: null
retest_successful_external_addresses_interval:
secs: 60
nanos: 0
mapping:
timeout: '1.000000000'
lease_duration:
secs: 7200
nanos: 0
max_retries: 3
renewal_delay_fraction: 0.8
retry_interval:
secs: 30
nanos: 0
gateway_monitor:
check_interval:
secs: 300
nanos: 0
initial_peers:
- /ip4/209.38.241.182/udp/3000/quic-v1/p2p/12D3KooW9wycYQTk8HPKHHyVzF2tUEpVSu4WHu8ySAbTmXGreuV5
- /ip4/65.109.51.37/udp/3000/quic-v1/p2p/12D3KooWFrouXfmrR4nsLMtE7wu15DoMJ6VtoUtHinREZCvbWHar
- /ip4/65.109.51.37/udp/3001/quic-v1/p2p/12D3KooWJRGau8M1rjT7R5e4YYsgdFhsMX35nRDtMwCDjxQkXAHz
- /ip4/65.109.51.37/udp/3002/quic-v1/p2p/12D3KooWQXJavMDTRscjauFSgVAB1VLB6Rzpy2uY5SU9Tk7927tb
- /ip4/65.109.51.37/udp/3003/quic-v1/p2p/12D3KooWSQc7CcGtvWDPF1yCbBthFnQjprfCVHmfmNDUrSmqQsU1
blend:
non_ephemeral_signing_key_id: 741d3c4be7d2c0d4f87ce582a2da38368739a621987b6cd003a807768b34115d
core:
backend:
listening_address: /ip4/0.0.0.0/udp/3400/quic-v1
core_peering_degree:
start: 2
end: 3
edge_node_connection_timeout: '1.000000000'
max_edge_node_incoming_connections: 300
max_dial_attempts_per_peer: 3
zk:
secret_key_kms_id: 8a81da6155a8c2af8d6589f343f309a29b93eec45d2ecbf9236e55eb0a6d7205
edge:
backend:
max_dial_attempts_per_peer_per_message: 1
replication_factor: 1
cryptarchia:
service:
bootstrap:
prolonged_bootstrap_period: '300.000000000'
force_bootstrap: false
offline_grace_period:
grace_period: '1200.000000000'
state_recording_interval: '60.000000000'
network:
bootstrap:
ibd:
peers:
- 12D3KooW9wycYQTk8HPKHHyVzF2tUEpVSu4WHu8ySAbTmXGreuV5
delay_before_new_download:
secs: 10
nanos: 0
sync:
orphan:
max_orphan_cache_size: 1000
network:
max_connected_peers_to_try_download: 16
max_discovered_peers_to_try_download: 16
leader:
wallet:
max_tx_fee: 18446744073709551615
funding_pk: 479bc98b3494dade3d50b2110e96ca46ac5d6db32e98371ca8c399d2cbf2d805
time:
backend:
server: pool.ntp.org:123
client:
timeout: '5.000000000'
listening_interface: 0.0.0.0
update_interval: '15.000000000'
sdp:
declaration_id: null
wallet:
max_tx_fee: 18446744073709551615
funding_pk: 479bc98b3494dade3d50b2110e96ca46ac5d6db32e98371ca8c399d2cbf2d805
api:
backend:
listen_address: 0.0.0.0:8080
cors_origins: []
timeout: 30
max_body_size: 10485760
max_concurrent_requests: 500
storage:
backend:
folder_name: ./db
read_only: false
column_family: blocks
kms:
backend:
keys:
3c0999d8b5c32c36d2d49918cb09e69429a50de71776ade8ffdeeebcf9c4312a: !Zk f84adcef2416d7f414af14163f81ebe9290d146a7a0afbe7214a67220e399c09
479bc98b3494dade3d50b2110e96ca46ac5d6db32e98371ca8c399d2cbf2d805: !Zk bd6b46f655db596f7c5aa52a5c8129bf03c8f9b9d498a222f544ecd1aae01b1c
8a81da6155a8c2af8d6589f343f309a29b93eec45d2ecbf9236e55eb0a6d7205: !Zk 731d3c5b53ddde90670c2c095af2040e2ae124a0e1351c18da07d69418e6ac2c
741d3c4be7d2c0d4f87ce582a2da38368739a621987b6cd003a807768b34115d: !Ed25519 611760acd0ddc6cbe68f7759132699c8dc478faec43e167e281cc5a1b2a7dd50
wallet:
known_keys:
479bc98b3494dade3d50b2110e96ca46ac5d6db32e98371ca8c399d2cbf2d805: 479bc98b3494dade3d50b2110e96ca46ac5d6db32e98371ca8c399d2cbf2d805
3c0999d8b5c32c36d2d49918cb09e69429a50de71776ade8ffdeeebcf9c4312a: 3c0999d8b5c32c36d2d49918cb09e69429a50de71776ade8ffdeeebcf9c4312a
voucher_master_key_id: 3c0999d8b5c32c36d2d49918cb09e69429a50de71776ade8ffdeeebcf9c4312a
tracing:
logger:
file:
directory: .
prefix: '1776126665'
loki: null
gelf: null
otlp: null
stdout: true
stderr: false
tracing: None
filter: None
metrics: None
console: None
level: DEBUG
state:
base_folder: ./state
15 changes: 6 additions & 9 deletions logos_chain/conf.nim
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ const
defaultAdminListenAddressDesc* = $defaultAdminListenAddress
## Default ``--deployment-settings`` path (canonical cfgsync layout; run from repo root or override).
defaultDeploymentSettingsPath* = "config/deployment-settings.yaml"
## Default ``--user-config`` path (cfgsync user config; bootstrap peers from ``initial_peers``).
defaultUserConfigPath* = "config/user_config.yaml"

when defined(windows):
{.pragma: windowsOnly.}
Expand Down Expand Up @@ -194,15 +196,10 @@ type
desc: "Run as a Windows service"
name: "run-as-service" .}: bool

bootstrapNodes* {.
desc: "Specifies one or more bootstrap nodes to use when connecting to the network"
abbr: "b"
name: "bootstrap-node" .}: seq[string]

bootstrapNodesFile* {.
desc: "Specifies a line-delimited file of bootstrap Ethereum network addresses"
defaultValue: ""
name: "bootstrap-file" .}: InputFile
userConfigFile* {.
desc: "cfgsync user-config YAML (bootstrap peers from network.backend.initial_peers)"
defaultValue: InputFile(defaultUserConfigPath)
name: "user-config" .}: InputFile

listenAddress* {.
desc: "Listening address for Logos Chain libp2p and Discovery v5 traffic"
Expand Down
50 changes: 50 additions & 0 deletions logos_chain/deployment/user_config.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# nimbos
# Copyright (c) 2026 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.

{.push raises: [].}

import
std/[options, strutils],
results,
stew/io2,
yaml/dom,
./helpers

export dom

const initialPeersPath* = ["network", "backend", "initial_peers"]

proc parseUserConfigYaml*(text: string): Result[YamlNode, string] =
let root = ? parseDeploymentSettingsYaml(text)
if root.kind != yMapping:
return err("user-config: expected top-level mapping")
ok(root)

proc initialPeersFromYaml*(root: YamlNode): Result[seq[string], string] =
let node = yamlGetPathNode(root, initialPeersPath)
if node.isNone:
return ok(newSeq[string]())
let peersNode = node.get
if peersNode.kind != ySequence:
return err("user-config: network.backend.initial_peers must be a sequence")
var peers: seq[string]
for i in 0 ..< peersNode.len:
let item = peersNode[i]
if item.kind != yScalar:
return err("user-config: network.backend.initial_peers[" & $i & "] must be a string")
let peerAddr = item.content.strip()
if peerAddr.len > 0:
peers.add(peerAddr)
ok(peers)

proc loadUserConfigInitialPeers*(path: string): Result[seq[string], string] =
let text = readAllChars(path).valueOr:
return err("user-config: cannot read " & path & ": " & ioErrorMsg(error))
let root = ? parseUserConfigYaml(text)
initialPeersFromYaml(root)

{.pop.}
48 changes: 13 additions & 35 deletions logos_chain/networking/discovery.nim
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@ import
libp2p/[peerinfo, multiaddress, multicodec],
eth/common/keys,
eth/p2p/discoveryv5/[protocol, node, random2],
results,
ssz_serialization,
../conf
../conf,
../deployment/user_config

from std/os import splitFile
from std/strutils import cmpIgnoreCase, split, startsWith, strip
from std/strutils import split, startsWith, strip

export protocol, node

Expand Down Expand Up @@ -53,19 +54,8 @@ func parseBootstrapAddress*(address: string):

ok((peerId, baseAddr))

iterator strippedLines(filename: string): string {.raises: [ref IOError].} =
## Yields non-empty, trimmed, non-comment lines from ``filename``.
for line in lines(filename):
let stripped = strip(line)
if stripped.startsWith('#'):
continue

if stripped.len > 0:
yield stripped

proc addBootstrapNode*(bootstrapAddr: string,
proc addBootstrapPeer*(bootstrapAddr: string,
bootstrapPeers: var seq[(PeerId, MultiAddress)]) =
# Ignore empty lines or lines starting with #
if bootstrapAddr.len == 0 or bootstrapAddr[0] == '#':
return

Expand All @@ -76,27 +66,15 @@ proc addBootstrapNode*(bootstrapAddr: string,
warn "Ignoring invalid bootstrap address",
bootstrapAddr, reason = addrRes.error

proc loadBootstrapFile*(bootstrapFile: string,
bootstrapPeers: var seq[(PeerId, MultiAddress)]) =
if bootstrapFile.len == 0: return
let ext = splitFile(bootstrapFile).ext
if cmpIgnoreCase(ext, ".txt") == 0:
try:
for ln in strippedLines(bootstrapFile):
addBootstrapNode(ln, bootstrapPeers)
except IOError as e:
error "Could not read bootstrap file", msg = e.msg
quit 1
else:
error "Unknown bootstrap file format", ext
quit 1

proc loadBootstrapNodes*(config: LBNodeConf): seq[(PeerId, MultiAddress)] =
proc loadBootstrapNodes*(config: LBNodeConf): Result[seq[(PeerId, MultiAddress)], string] =
## YAML load is not statically GC-safe; safe here (sync init, no shared refs).
var initialPeers: seq[string]
{.cast(gcsafe).}:
initialPeers = ? loadUserConfigInitialPeers(string config.userConfigFile)
var bootstrapPeers: seq[(PeerId, MultiAddress)]
for node in config.bootstrapNodes:
addBootstrapNode(node, bootstrapPeers)
loadBootstrapFile(string config.bootstrapNodesFile, bootstrapPeers)
bootstrapPeers
for peerAddr in initialPeers:
addBootstrapPeer(peerAddr, bootstrapPeers)
ok(bootstrapPeers)

proc new*(T: type Eth2DiscoveryProtocol,
config: LBNodeConf,
Expand Down
10 changes: 6 additions & 4 deletions logos_chain/networking/network.nim
Original file line number Diff line number Diff line change
Expand Up @@ -2030,9 +2030,11 @@ func quicEndPoint(address: IpAddress, port: Port): Result[MultiAddress, string]
except MaError as exc:
err(exc.msg)

proc loadBootstrapPeers(config: LBNodeConf): seq[PeerAddr] =
for (peerId, maddr) in loadBootstrapNodes(config):
result.add(PeerAddr(peerId: peerId, addrs: @[maddr]))
proc loadBootstrapPeers(config: LBNodeConf): Result[seq[PeerAddr], string] =
var peers: seq[PeerAddr]
for (peerId, maddr) in ? loadBootstrapNodes(config):
peers.add(PeerAddr(peerId: peerId, addrs: @[maddr]))
ok(peers)

func initNetKeys(privKey: PrivateKey): NetKeyPair =
let pubKey = privKey.getPublicKey().expect("working public key from random")
Expand Down Expand Up @@ -2128,7 +2130,7 @@ proc createLBNode*(
err(ValidationResult.Reject)

let
bootstrapPeers = loadBootstrapPeers(config)
bootstrapPeers = ? loadBootstrapPeers(config)
params = GossipSubParams.init(
pruneBackoff = chronos.minutes(1),
unsubscribeBackoff = chronos.seconds(10),
Expand Down
1 change: 1 addition & 0 deletions tests/all_tests.nim
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import # Unit test
./test_logos_p2p,
./test_api_handlers,
./test_deployment_settings,
./test_user_config,
./test_poseidon_hasher

summarizeLongTests("AllTests")
Expand Down
3 changes: 3 additions & 0 deletions tests/fixtures/user_config-empty-peers.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
network:
backend:
initial_peers: []
Loading
Loading