Skip to content
Merged
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: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ NETWORK_DIR=local-devnet ./spin-node.sh --restart-client zeam_0 \
- **Local** (`NETWORK_DIR=local-devnet`): Uses Docker directly
- **Ansible** (`NETWORK_DIR=ansible-devnet`): Uses Ansible to deploy to remote hosts

**Supported clients:** zeam, ream, qlean, lantern, lighthouse, grandine, ethlambda
**Supported clients:** zeam, ream, qlean, lantern, lighthouse, grandine, ethlambda, peam

> **Note:** All clients accept `--checkpoint-sync-url`. Client implementations may use different parameter names internally; update client-cmd scripts if parameters change.

Expand All @@ -292,6 +292,8 @@ Current following clients are supported:
4. Lantern
5. Lighthouse
6. Grandine
7. Ethlambda
8. Peam

However adding a lean client to this setup is very easy. Feel free to do the PR or reach out to the maintainers.

Expand Down
17 changes: 9 additions & 8 deletions ansible-devnet/genesis/validator-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -115,11 +115,12 @@ validators:
# isAggregator: false
# count: 1

# - name: "peam_0"
# enrFields:
# ip: "95.216.173.151"
# quic: 9001
# metricsPort: 9095
# apiPort: 5055
# isAggregator: false
# count: 1
- name: "peam_0"
privkey: "8c7b62f4c4a35d134649817f44d2839ad15bcfa1fe6b87991f64d6f54f3ef2a1"
enrFields:
ip: ""
quic: 9001
metricsPort: 9095
apiPort: 5055
isAggregator: false
count: 1
2 changes: 1 addition & 1 deletion ansible/playbooks/deploy-nodes.yml
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@
- lighthouse
- grandine
- ethlambda
- peam
- deploy

- name: Include common role
Expand All @@ -223,4 +224,3 @@
include_tasks: helpers/deploy-single-node.yml
tags:
- deploy

12 changes: 10 additions & 2 deletions ansible/playbooks/helpers/deploy-single-node.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,15 @@
- ethlambda
- deploy

- name: Deploy Peam node
include_role:
name: peam
when: client_type == "peam"
tags:
- peam
- deploy

- name: Fail if unknown client type
fail:
msg: "Unknown client type '{{ client_type }}' for node '{{ node_name }}'. Expected: zeam, ream, qlean, lantern, lighthouse, grandine or ethlambda"
when: client_type not in ["zeam", "ream", "qlean", "lantern", "lighthouse", "grandine", "ethlambda"]
msg: "Unknown client type '{{ client_type }}' for node '{{ node_name }}'. Expected: zeam, ream, qlean, lantern, lighthouse, grandine, ethlambda or peam"
when: client_type not in ["zeam", "ream", "qlean", "lantern", "lighthouse", "grandine", "ethlambda", "peam"]
7 changes: 7 additions & 0 deletions ansible/roles/peam/defaults/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
# Default variables for peam role
# Note: These are fallback defaults. Actual values are extracted from client-cmds/peam-cmd.sh
# in the tasks/main.yml file. These defaults are used if extraction fails.

peam_docker_image: "ghcr.io/malik672/peam:sha-f11c8d1"
deployment_mode: docker # docker or binary
155 changes: 155 additions & 0 deletions ansible/roles/peam/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
---
# Peam role: Deploy and manage Peam nodes
# Converts client-cmds/peam-cmd.sh logic to Ansible tasks

- name: Extract docker image from client-cmd.sh
shell: |
project_root="$(cd '{{ playbook_dir }}/../..' && pwd)"
grep -E '^node_docker=' "$project_root/client-cmds/peam-cmd.sh" | head -1 | sed -E 's/.*node_docker="([^ "]+).*/\1/'
register: peam_docker_image_raw
changed_when: false
delegate_to: localhost
run_once: true

- name: Extract deployment mode from client-cmd.sh
shell: |
project_root="$(cd '{{ playbook_dir }}/../..' && pwd)"
grep -E '^node_setup=' "$project_root/client-cmds/peam-cmd.sh" | head -1 | sed -E 's/.*node_setup="([^"]+)".*/\1/'
register: peam_deployment_mode_raw
changed_when: false
delegate_to: localhost
run_once: true

- name: Set docker image and deployment mode from client-cmd.sh
set_fact:
peam_docker_image: "{{ peam_docker_image_raw.stdout | trim | default('ghcr.io/malik672/peam:sha-f11c8d1') }}"
deployment_mode: "{{ peam_deployment_mode_raw.stdout | trim | default('docker') }}"

- name: Extract node configuration from validator-config.yaml
shell: |
yq eval ".validators[] | select(.name == \"{{ node_name }}\") | .{{ item }}" "{{ hostvars['localhost']['local_genesis_dir_path'] }}/validator-config.yaml"
register: peam_node_config
changed_when: false
delegate_to: localhost
loop:
- enrFields.quic
- metricsPort
- apiPort
- isAggregator
- devnet
when: node_name is defined

- name: Set node ports and Peam flags
set_fact:
peam_quic_port: "{{ peam_node_config.results[0].stdout }}"
peam_metrics_port: "{{ peam_node_config.results[1].stdout }}"
peam_api_port: "{{ peam_node_config.results[2].stdout }}"
peam_is_aggregator: "{{ 'true' if (peam_node_config.results[3].stdout | default('') | trim) == 'true' else 'false' }}"
peam_topic_domain: "{{ peam_node_config.results[4].stdout | default('') | trim | default('devnet0', true) }}"
when: peam_node_config is defined

- name: Extract local validator index from validator-config ordering
shell: |
yq eval '.validators[].name' "{{ hostvars['localhost']['local_genesis_dir_path'] }}/validator-config.yaml" | nl -v0 | awk '$2=="{{ node_name }}" {print $1; exit}'
register: peam_validator_index
changed_when: false
delegate_to: localhost

- name: Fail if Peam node index was not resolved
fail:
msg: "Could not resolve validator index for {{ node_name }} from validator-config.yaml"
when: peam_validator_index.stdout | trim == ""

- name: Extract genesis time from generated config.yaml
shell: |
yq eval '.GENESIS_TIME // .genesis_time' "{{ genesis_dir }}/config.yaml"
register: peam_genesis_time
changed_when: false

- name: Extract total validator count from validator-config.yaml
shell: |
yq eval '.validators[].count // 1' "{{ hostvars['localhost']['local_genesis_dir_path'] }}/validator-config.yaml" | awk '{sum += $1} END {print sum + 0}'
register: peam_total_validator_count
changed_when: false
delegate_to: localhost

- name: Extract attestation committee count from validator-config.yaml
shell: |
yq eval '.config.attestation_committee_count // 1' "{{ hostvars['localhost']['local_genesis_dir_path'] }}/validator-config.yaml"
register: peam_attestation_committee_count_raw
changed_when: false
delegate_to: localhost

- name: Set attestation committee count
set_fact:
peam_attestation_committee_count: "{{ peam_attestation_committee_count_raw.stdout | trim | default('1', true) }}"

- name: Ensure node key file exists
stat:
path: "{{ genesis_dir }}/{{ node_name }}.key"
register: node_key_stat

- name: Fail if node key file is missing
fail:
msg: "Node key file {{ node_name }}.key not found in {{ genesis_dir }}"
when: not (node_key_stat.stat.exists | default(false))

- name: Create node data directory
file:
path: "{{ data_dir }}/{{ node_name }}"
state: directory
mode: "0755"

- name: Write Peam runtime config
copy:
dest: "{{ data_dir }}/{{ node_name }}/peam.conf"
mode: "0644"
content: |
genesis_time={{ peam_genesis_time.stdout | trim }}
metrics=true
metrics_address=0.0.0.0
metrics_port={{ peam_metrics_port }}
http_api=true
listen_addr=/ip4/0.0.0.0/udp/{{ peam_quic_port }}/quic-v1
node_key_path=/config/{{ node_name }}.key
bootnodes_file=/config/nodes.yaml
validator_count={{ peam_total_validator_count.stdout | trim }}
local_validator_index={{ peam_validator_index.stdout | trim }}
attestation_committee_count={{ peam_attestation_committee_count }}
validator_config_path=/config/validator-config.yaml
allowed_topics=/leanconsensus/{{ peam_topic_domain }}/block/ssz_snappy,/leanconsensus/{{ peam_topic_domain }}/aggregation/ssz_snappy{% for subnet in range(peam_attestation_committee_count | int) %},/leanconsensus/{{ peam_topic_domain }}/attestation_{{ subnet }}/ssz_snappy{% endfor %}
topic_scores=/leanconsensus/{{ peam_topic_domain }}/block/ssz_snappy:2,/leanconsensus/{{ peam_topic_domain }}/aggregation/ssz_snappy:1{% for subnet in range(peam_attestation_committee_count | int) %},/leanconsensus/{{ peam_topic_domain }}/attestation_{{ subnet }}/ssz_snappy:1{% endfor %}
topic_validators=/leanconsensus/{{ peam_topic_domain }}/block/ssz_snappy=block,/leanconsensus/{{ peam_topic_domain }}/aggregation/ssz_snappy=aggregation{% for subnet in range(peam_attestation_committee_count | int) %},/leanconsensus/{{ peam_topic_domain }}/attestation_{{ subnet }}/ssz_snappy=attestation{% endfor %}
metrics_node_name={{ node_name }}
metrics_client_name=peam

- name: Deploy Peam node using Docker
block:
- name: Stop existing Peam container (if any)
command: docker rm -f {{ node_name }}
register: peam_stop
failed_when: false
changed_when: peam_stop.rc == 0

- name: Start Peam container
command: >-
docker run -d
--pull=always
--name {{ node_name }}
--restart unless-stopped
--network host
{{ '--init --ulimit core=-1 --workdir /data' if (enable_core_dumps | default('') == 'all') or (node_name in (enable_core_dumps | default('')).split(',')) or (node_name.split('_')[0] in (enable_core_dumps | default('')).split(',')) else '' }}
-v {{ genesis_dir }}:/config:ro
-v {{ data_dir }}/{{ node_name }}:/data
{{ peam_docker_image }}
--run
--config /data/peam.conf
--data-dir /data
--node-id {{ node_name }}
--validator-keys /config/hash-sig-keys
--api-port {{ peam_api_port }}
{{ '--is-aggregator' if (peam_is_aggregator | default('false')) == 'true' else '' }}
{{ ('--checkpoint-sync-url ' + checkpoint_sync_url) if (checkpoint_sync_url is defined and checkpoint_sync_url | length > 0) else '' }}
register: peam_container
changed_when: peam_container.rc == 0
when: deployment_mode == 'docker'
100 changes: 100 additions & 0 deletions client-cmds/peam-cmd.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
#!/bin/bash

#-----------------------peam setup----------------------

binary_path="$scriptDir/../Peam/target/release/peam"
default_peam_docker_image="ghcr.io/malik672/peam:sha-f11c8d1"
peam_docker_image="${PEAM_DOCKER_IMAGE:-$default_peam_docker_image}"
runtime_config_host="$dataDir/$item/peam.conf"
runtime_config_container="/data/peam.conf"
genesis_config="$configDir/config.yaml"
validator_config="$configDir/validator-config.yaml"
hash_sig_keys_dir="$configDir/hash-sig-keys"

genesis_time=$(yq eval '.GENESIS_TIME // .genesis_time' "$genesis_config")
validator_count=$(yq eval '.validators[].count // 1' "$validator_config" | awk '{sum += $1} END {print sum + 0}')
local_validator_index="${HASH_SIG_KEY_INDEX:-0}"
committee_count="${attestationCommitteeCount:-1}"
topic_domain="${devnet:-devnet0}"

allowed_topics="/leanconsensus/$topic_domain/block/ssz_snappy,/leanconsensus/$topic_domain/aggregation/ssz_snappy"
topic_scores="/leanconsensus/$topic_domain/block/ssz_snappy:2,/leanconsensus/$topic_domain/aggregation/ssz_snappy:1"
topic_validators="/leanconsensus/$topic_domain/block/ssz_snappy=block,/leanconsensus/$topic_domain/aggregation/ssz_snappy=aggregation"

for ((subnet = 0; subnet < committee_count; subnet++)); do
att_topic="/leanconsensus/$topic_domain/attestation_${subnet}/ssz_snappy"
allowed_topics="$allowed_topics,$att_topic"
topic_scores="$topic_scores,$att_topic:1"
topic_validators="$topic_validators,$att_topic=attestation"
done

cat > "$runtime_config_host" <<EOF
genesis_time=$genesis_time
metrics=true
metrics_address=0.0.0.0
metrics_port=$metricsPort
http_api=true
listen_addr=/ip4/0.0.0.0/udp/$quicPort/quic-v1
node_key_path=/config/$item.key
bootnodes_file=/config/nodes.yaml
validator_count=$validator_count
local_validator_index=$local_validator_index
attestation_committee_count=$committee_count
validator_config_path=/config/validator-config.yaml
allowed_topics=$allowed_topics
topic_scores=$topic_scores
topic_validators=$topic_validators
metrics_node_name=$item
metrics_client_name=peam
EOF

aggregator_flag=""
if [ "$isAggregator" == "true" ]; then
aggregator_flag="--is-aggregator"
fi

checkpoint_sync_flag=""
if [ -n "${checkpoint_sync_url:-}" ]; then
checkpoint_sync_flag="--checkpoint-sync-url $checkpoint_sync_url"
fi

validator_keys_flag=""
if [ -d "$hash_sig_keys_dir" ]; then
validator_keys_flag="--validator-keys $hash_sig_keys_dir"
fi

api_port_flag=""
if [ -n "$apiPort" ]; then
api_port_flag="--api-port $apiPort"
fi

node_binary="$binary_path \
--run \
--config $runtime_config_host \
--data-dir $dataDir/$item \
--node-id $item \
$validator_keys_flag \
$api_port_flag \
$aggregator_flag \
$checkpoint_sync_flag"

validator_keys_flag_container=""
if [ -d "$hash_sig_keys_dir" ]; then
validator_keys_flag_container="--validator-keys /config/hash-sig-keys"
fi

node_docker="ghcr.io/malik672/peam:sha-f11c8d1 \
--run \
--config $runtime_config_container \
--data-dir /data \
--node-id $item \
$validator_keys_flag_container \
$api_port_flag \
$aggregator_flag \
$checkpoint_sync_flag"

if [ -n "${PEAM_DOCKER_IMAGE:-}" ]; then
node_docker="${peam_docker_image}${node_docker#"ghcr.io/malik672/peam:sha-f11c8d1"}"
fi

node_setup="docker"
10 changes: 10 additions & 0 deletions local-devnet/genesis/validator-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,13 @@ validators:
apiPort: 5058
isAggregator: false
count: 1

- name: "peam_0"
privkey: "8c7b62f4c4a35d134649817f44d2839ad15bcfa1fe6b87991f64d6f54f3ef2a1"
enrFields:
ip: "127.0.0.1"
quic: 9009
metricsPort: 8089
apiPort: 5059
isAggregator: false
count: 1
Loading