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: 4 additions & 0 deletions ansible/inventory/host_vars/decdn-node-1.yml.example
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ decdn_payment_channel_address: "0xREPLACE_PaymentChannel_FROM_DEPLOYMENT"
decdn_capacity_bond_address: "0xREPLACE_CapacityBond_FROM_DEPLOYMENT"
decdn_slash_judge_address: "0xREPLACE_SlashJudge_FROM_DEPLOYMENT"

# --- Slash appeals (optional; ADR 028) ----------------------------------------
# decdn_slash_appeal_address: "0xREPLACE_SlashAppeal_FROM_DEPLOYMENT" # for `decdn appeal slash`
# decdn_slash_judge_from_block: 0 # SlashJudge deploy block — set to bound per-restart RPC rescan

# --- Node identity / locale ---------------------------------------------------
decdn_region: "US" # ISO 3166-1 alpha-2 of the node's physical location
# decdn_relay_url: "" # optional iroh relay for NAT traversal
Expand Down
12 changes: 11 additions & 1 deletion ansible/roles/decdn_node/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,12 @@ Per `decdn/adr/019-node-onboarding.md`, a node only serves paid traffic after
`decdn_payment_channel_address`, `decdn_capacity_bond_address`,
`decdn_slash_judge_address` (all `0x`+40-hex; SlashJudge non-zero),
`decdn_region` (ISO 3166-1 alpha-2). Contract addresses/chain-id are protocol
facts — source them from the deployment / an ADR, never guess. See
facts — source them from the deployment / an ADR, never guess.

Optional (omitted from `node.toml` unless set): `decdn_slash_appeal_address`
(SlashAppeal contract, source from the deployment / ADR 028 — only needed to file
appeals with `decdn appeal slash`) and `decdn_slash_judge_from_block` (SlashJudge
deploy block; bounds the slash-detection watcher's per-restart chain rescan). See
`roles/decdn_node/defaults/main.yml` for the full knob list and defaults.

## Network
Expand All @@ -77,6 +82,11 @@ systemctl status decdn-node
journalctl -u decdn-node -e
decdn node health # admin RPC (127.0.0.1:9191)
decdn node peers

# If this node is slashed (surfaced via the admin RPC — admin_v1_slashes), file an
# appeal within the ADR 028 window. Needs decdn_slash_appeal_address set in node.toml
# (else pass --slash-appeal-address / DECDN_SLASH_APPEAL_ADDRESS):
decdn appeal slash <SLASH_ID> <EVIDENCE_BUNDLE_HASH>
```

Upgrades: bump `decdn_node_version` (+ `decdn_node_sha256`) and re-deploy — the
Expand Down
7 changes: 7 additions & 0 deletions ansible/roles/decdn_node/defaults/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ decdn_rpc_url: "" # SENSITIVE; may embed an API key
decdn_payment_channel_address: ""
decdn_capacity_bond_address: ""
decdn_slash_judge_address: ""
# Optional (ADR 028). SlashAppeal contract — only needed to file appeals with
# `decdn appeal slash`; the daemon ignores it. Empty => key omitted from node.toml.
decdn_slash_appeal_address: ""
# Optional. SlashJudge deploy block: the slash-detection watcher rescans from
# here on EVERY daemon start, so 0 (the upstream default) re-scans the full chain
# each restart — RPC-heavy on an established L2. Set to bound restart cost.
decdn_slash_judge_from_block: 0
decdn_region: "" # ISO 3166-1 alpha-2
decdn_chain_id: 421614 # Arbitrum Sepolia (matches decdn-node's --chain-id default)

Expand Down
32 changes: 32 additions & 0 deletions ansible/roles/decdn_node/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,38 @@
vars:
decdn_zero_address: "0x0000000000000000000000000000000000000000"

# slash_appeal_address is optional (only `decdn appeal slash` uses it — the daemon
# ignores the key), so it is not in the hard assert above. But if an operator sets
# it, a half-filled/zero value would silently render a bad node.toml, so validate
# it the same fail-loud way as the required addresses when present.
- name: Validate the optional SlashAppeal address when set
ansible.builtin.assert:
that:
- decdn_slash_appeal_address is match('^0x[0-9a-fA-F]{40}$')
- decdn_slash_appeal_address != decdn_zero_address
fail_msg: >-
decdn_slash_appeal_address is set but is not a valid contract address
(0x + 40 hex, not the zero address). Leave it empty to omit it, or set the
deployed SlashAppeal address (source it from the deployment / ADR 028).
vars:
decdn_zero_address: "0x0000000000000000000000000000000000000000"
when: decdn_slash_appeal_address | length > 0

# slash_judge_from_block feeds a `| int > 0` gate in node.toml.j2, and Jinja's int
# filter silently coerces any unparseable value to 0 — which drops the key and
# reverts the daemon to a full-chain rescan on every restart, discarding the
# operator's input with no trace in the rendered config. Assert integer shape so a
# typo fails loud at deploy time instead. Runs unconditionally: the default 0
# passes, so a malformed value can never slip through the coercion silently.
- name: Validate the SlashJudge scan-floor block is a non-negative integer
ansible.builtin.assert:
that:
- decdn_slash_judge_from_block | string is match('^[0-9]+$')
fail_msg: >-
decdn_slash_judge_from_block must be a non-negative integer (the SlashJudge
deploy block). Got "{{ decdn_slash_judge_from_block }}". Leave it 0 to scan
from genesis, or set the deploy block to bound per-restart rescan cost.
Comment on lines +58 to +65

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed via the | int render fix (495bf93) rather than tightening the regex. Once the value is emitted through | int, a leading-zero input normalizes to the mathematically-identical block number (001 -> 1) and always produces valid TOML, so the invalid-config path this flags can no longer occur. Keeping the regex as ^[0-9]+$ deliberately: rejecting 001 would fail loud on a harmless, value-preserving form, whereas normalizing it is friendlier and safe. The regex still catches genuine typos (letters, negatives, floats).


# --- User & directories -------------------------------------------------------
- name: Create decdn system group
ansible.builtin.group:
Expand Down
6 changes: 6 additions & 0 deletions ansible/roles/decdn_node/templates/node.toml.j2
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ eth_keystore = "{{ decdn_keystore_file }}"
payment_channel_address = "{{ decdn_payment_channel_address }}"
capacity_bond_address = "{{ decdn_capacity_bond_address }}"
slash_judge_address = "{{ decdn_slash_judge_address }}"
{% if decdn_slash_appeal_address | length > 0 %}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Following the general rules, use | default('') instead of | length > 0 to safely check if a variable is defined and non-empty. This prevents template compilation errors if the variable is undefined, None, or null.

{% if decdn_slash_appeal_address | default('') %}
References
  1. In Jinja2 templates, use | default('') instead of | length > 0 to safely check if a variable is defined and non-empty. Using | length > 0 can cause template compilation errors if the variable is undefined, None, or null (especially when using StrictUndefined).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not adopting this one. The StrictUndefined failure mode does not apply here: this repo uses Ansible's default undefined handling (no StrictUndefined in ansible.cfg), and decdn_slash_appeal_address has a string default of "" in defaults/main.yml, so it is never undefined/None in normal use. | length > 0 also matches the established idiom already used for decdn_relay_url two lines up in this same template. Finally, this template gate is paired with a when: decdn_slash_appeal_address | length > 0 assert in tasks/main.yml that must stay identical to it — switching only the template to | default() truthiness would desync the two. If we want repo-wide null-safe hardening it should be a separate consistency pass covering relay_url and the asserts too.

slash_appeal_address = "{{ decdn_slash_appeal_address }}"
{% endif %}
{% if decdn_slash_judge_from_block | int > 0 %}
slash_judge_from_block = {{ decdn_slash_judge_from_block | int }}
{% endif %}
Comment thread
Copilot marked this conversation as resolved.

[payment]
rate_per_mb = {{ decdn_rate_per_mb }}
Expand Down
Loading