Skip to content

Commit 06bf972

Browse files
committed
feat: add readme for setting up the mvp
1 parent 6e33331 commit 06bf972

4 files changed

Lines changed: 216 additions & 6 deletions

File tree

Makefile

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
.PHONY: peerinfo init-peerinfo node1 node2
2+
3+
# Profile configurations
4+
ifneq ($(filter node1,$(MAKECMDGOALS)),)
5+
PORT := 4001
6+
NICKNAME := node1
7+
DATA_DIR := .charon-example-peerinfo-1
8+
METRICS_PORT := 9465
9+
else ifneq ($(filter node2,$(MAKECMDGOALS)),)
10+
PORT := 4002
11+
NICKNAME := node2
12+
DATA_DIR := .charon-example-peerinfo-2
13+
METRICS_PORT := 9466
14+
endif
15+
16+
# Extract dial addresses from command line (multiaddresses starting with /)
17+
DIAL_ADDRS := $(filter /%,$(MAKECMDGOALS))
18+
19+
# Build the dial arguments
20+
ifneq ($(DIAL_ADDRS),)
21+
DIAL_ARGS := $(foreach addr,$(DIAL_ADDRS),--dial $(addr))
22+
endif
23+
24+
# Run peerinfo with the selected profile
25+
peerinfo:
26+
cargo run -p charon-peerinfo --example peerinfo -- \
27+
--port $(PORT) \
28+
--nickname $(NICKNAME) \
29+
--data-dir $(DATA_DIR) \
30+
--metrics-port $(METRICS_PORT) \
31+
$(DIAL_ARGS)
32+
33+
# Initialize peerinfo with a private key
34+
# Usage: make init node1 KEY=<private_key>
35+
init-peerinfo:
36+
cargo run -p charon-peerinfo --example peerinfo -- init \
37+
--data-dir $(DATA_DIR) \
38+
--private-key $(KEY)
39+
40+
# No-op targets for profile selection
41+
node1 node2:
42+
@:
43+
44+
# Catch-all for multiaddresses (prevents "No rule to make target" errors)
45+
/%:
46+
@:

crates/peerinfo/examples/README.md

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
# Peerinfo Example
2+
3+
Demonstrates the peerinfo protocol with mDNS auto-discovery.
4+
5+
## Setup
6+
7+
### Using Nix (recommended)
8+
9+
```bash
10+
nix develop
11+
```
12+
13+
### Manual
14+
15+
Ensure you have:
16+
17+
- Rust (stable + nightly for fmt)
18+
- protobuf compiler (`protoc`)
19+
20+
## Creating a cluster
21+
22+
For now we use simple approach by creating cluster in charon and taking private keys from the generated nodes.
23+
24+
### Building charon
25+
26+
So to do this you need to clone [charon repository](https://github.com/ObolNetwork/charon.git)
27+
28+
Then, in the cloned repository build charon:
29+
30+
```bash
31+
make charon
32+
```
33+
34+
Output binary (`charon`) will be put in the project's root directory and will be accessible via `./charon <args>`.
35+
Now, you have everything needed to create test cluster.
36+
37+
### Creating cluster configuration
38+
39+
Creating a cluster could be done by simply running:
40+
41+
```bash
42+
./charon create cluster --nodes 3 --network mainnet --num-validators 1 --cluster-dir ./test-cluster --insecure-keys --fee-recipient-addresses 0x0000000000000000000000000000000000000000 --withdrawal-addresses 0x0000000000000000000000000000000000000000
43+
```
44+
45+
This command will initialize testing cluster with 3 nodes and 1 validator and put output artifacts in the `./test-cluster` folder. Each node configuration will have separate subfolder in the format of node<NODE_NUMBER> (i.e. node1, node2, node3 etc.).
46+
47+
Next, thing we will do is to actually run first node:
48+
49+
```bash
50+
./charon run --simnet-beacon-mock --no-verify --nickname=charon-1 --lock-file=test-cluster/node0/cluster-lock.json --private-key-file=test-cluster/node0/charon-enr-private-key --p2p-tcp-address=0.0.0.0:3610 --validator-api-address=0.0.0.0:3680 --monitoring-address=0.0.0.0:9464 --log-level=debug
51+
```
52+
53+
### Initializing the peerinfo example
54+
55+
In order to connect to the `node0` we need to initialize our example with 2 node profiles (node1, node2).
56+
57+
```bash
58+
make init-peerinfo node1 KEY=<PRIVATE_KEY_NODE_1>
59+
make init-peerinfo node1 KEY=<PRIVATE_KEY_NODE_2>
60+
```
61+
62+
This will initialize data-dirs for two pluto nodes.
63+
64+
### Running pluto's nodes
65+
66+
To run the node you will need the following commands in different terminals:
67+
68+
```bash
69+
make peerinfo node1 /ip4/127.0.0.1/tcp/3610
70+
```
71+
72+
```bash
73+
make peerinfo node2 /ip4/127.0.0.1/tcp/3610
74+
```
75+
76+
`/ip4/127.0.0.1/tcp/3610` is tcp address of charon's node in [multiaddr format](https://multiformats.io/multiaddr/).
77+
78+
### Working with results
79+
80+
Charon node is configured to send `peerinfo` every one minute and you will be able to see corresponding logs in the terminal.
81+
82+
On charon's side you can track the by accessing metrics endpoint:
83+
84+
```bash
85+
curl 0.0.0.0:9464/metrics | grep app_peerinfo
86+
```
87+
88+
For pluto:
89+
90+
`node1`:
91+
92+
```bash
93+
curl 0.0.0.0:9465/metrics | grep app_peerinfo
94+
```
95+
96+
`node2`:
97+
98+
```bash
99+
curl 0.0.0.0:9466/metrics | grep app_peerinfo
100+
```
101+
102+
### Running grafana locally
103+
104+
To run grafana we use `docker-compose`. All infra files located at `test-infra` folder.
105+
106+
To run it use this command:
107+
108+
```bash
109+
cd test-infra
110+
docker-compose up
111+
```
112+
113+
And grafana will be accessible via `http://localhost:3000`.
114+
115+
> Note, that you may need to add prometheus as a data source

crates/peerinfo/examples/peerinfo.rs

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,12 @@ use std::{
2121

2222
use charon_p2p::{
2323
config::P2PConfig,
24-
k1,
24+
k1::{self, key_path},
2525
p2p::{Node, NodeType},
2626
};
2727
use charon_peerinfo::{Behaviour, Config, Event, LocalPeerInfo};
2828
use clap::Parser;
29+
use k256::SecretKey;
2930
use libp2p::{
3031
Multiaddr, Swarm,
3132
futures::StreamExt,
@@ -41,13 +42,16 @@ use vise_exporter::MetricsExporter;
4142
#[command(name = "peerinfo-example")]
4243
#[command(about = "Demonstrates the peerinfo protocol with mDNS discovery")]
4344
pub struct Args {
45+
#[command(subcommand)]
46+
pub command: Option<Command>,
47+
4448
/// The port to listen on
4549
#[arg(short, long, default_value = "4001")]
4650
pub port: u16,
4751

48-
/// Optional address to dial
52+
/// Optional addresses to dial
4953
#[arg(short, long)]
50-
pub dial: Option<Multiaddr>,
54+
pub dial: Vec<Multiaddr>,
5155

5256
/// Nickname for this node
5357
#[arg(short, long, default_value = "example-node")]
@@ -66,6 +70,20 @@ pub struct Args {
6670
pub metrics_port: u16,
6771
}
6872

73+
#[derive(Debug, Parser)]
74+
pub enum Command {
75+
/// Initialize the node with a private key
76+
Init {
77+
/// Data directory for storing the private key
78+
#[arg(long, default_value = ".charon-example")]
79+
data_dir: PathBuf,
80+
81+
/// Private key as a hex string
82+
#[arg(long)]
83+
private_key: String,
84+
},
85+
}
86+
6987
/// Combined behaviour with peerinfo, identify, ping, and mdns
7088
#[derive(NetworkBehaviour)]
7189
pub struct CombinedBehaviour {
@@ -149,6 +167,28 @@ fn handle_event(event: SwarmEvent<CombinedEvent>, swarm: &mut Swarm<CombinedBeha
149167
}
150168
}
151169

170+
fn init_node(data_dir: &PathBuf, private_key: &str) -> anyhow::Result<()> {
171+
// Decode the hex string
172+
let key_bytes = hex::decode(private_key.trim().trim_start_matches("0x"))?;
173+
174+
// Parse the secret key
175+
let key = SecretKey::from_slice(&key_bytes)?;
176+
177+
// Create the data directory
178+
std::fs::create_dir_all(data_dir)?;
179+
180+
// Save the key
181+
let key_file = key_path(data_dir);
182+
std::fs::write(&key_file, hex::encode(key.to_bytes()))?;
183+
184+
tracing::info!(
185+
"Initialized node with private key in {}",
186+
key_file.display()
187+
);
188+
189+
Ok(())
190+
}
191+
152192
#[tokio::main]
153193
async fn main() -> anyhow::Result<()> {
154194
// Initialize logging
@@ -158,6 +198,15 @@ async fn main() -> anyhow::Result<()> {
158198

159199
let args = Args::parse();
160200

201+
// Handle init subcommand
202+
if let Some(Command::Init {
203+
data_dir,
204+
private_key,
205+
}) = args.command
206+
{
207+
return init_node(&data_dir, &private_key);
208+
}
209+
161210
// Run the metrics exporter
162211
let bind_address = SocketAddr::from(([0, 0, 0, 0], args.metrics_port));
163212

@@ -238,8 +287,8 @@ async fn main() -> anyhow::Result<()> {
238287
let listen_addr: Multiaddr = format!("/ip4/0.0.0.0/tcp/{}", args.port).parse()?;
239288
swarm.listen_on(listen_addr)?;
240289

241-
// Dial the specified address if provided
242-
if let Some(dial_addr) = &args.dial {
290+
// Dial the specified addresses if provided
291+
for dial_addr in &args.dial {
243292
tracing::info!("Dialing {dial_addr}");
244293
swarm.dial(dial_addr.clone())?;
245294
}

test-infra/prometheus.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ global:
55
scrape_configs:
66
- job_name: 'local-exporter'
77
static_configs:
8-
- targets: ['host.docker.internal:9464']
8+
- targets: ['host.docker.internal:9464', 'host.docker.internal:9465', 'host.docker.internal:9466']
99
labels:
1010
instance: 'local-exporter'
1111
environment: 'dev'

0 commit comments

Comments
 (0)