feat(pusher): add opt-in v2 WeTime ping signing

- Add WELIST_PING_SIG_VERSION (1 legacy, 2 canonical JSON); default 1
- Sign WELIST-PING-V2\n + compact sorted JSON covering url, chain, height,
  median_time, last_block_hash, info, base_fee, version and sig_version
- Fetch info/base_fee/version from the local /{chain}/info endpoint and sign them
- Fall back to null metadata on fetch failure; skip the fetch for onion URLs
- Normalize the advertised URL and bound metadata to match the WeTime service
- Activate v2 in bal-pusher.sh and document the TXT record setup
- Add unit tests for canonical payloads, URL normalization, metadata and onion detection

Includes previously uncommitted local deployment hardening:
- pg-server.sh configurable loopback bind address, volume and recreate handling
- .env loading in bal-server.sh and bal-pusher.sh
- second bal-server/bal-pusher development scripts
This commit is contained in:
2026-11-07 20:37:11 -04:00
parent b8d87ecabd
commit 0eff7a649a
12 changed files with 614 additions and 32 deletions

View File

@@ -193,6 +193,23 @@ This is the async daemon that monitors the blockchain and pushes pending transac
- Stats are inserted into `tbl_stats` with `ON CONFLICT(chain) DO UPDATE`.
- The stats payload is signed with Ed25519 and POSTed to `{welist_url}/ping`.
- The `WELIST_SERVER_URL` is validated via `is_valid_welist_url()` before sending (can be bypassed with `WELIST_SKIP_URL_VALIDATION=true`).
- `WELIST_PING_SIG_VERSION` selects the signature scheme:
- `1` (default): legacy concatenation of URL, chain, height, median time and
block hash. The WeTime service verifies it against `/.pub_key.pem`.
- `2`: canonical JSON signed with the `WELIST-PING-V2\n` prefix. The pusher
fetches `info`, `base_fee` and `version` from the local
`/{chain}/info` endpoint and signs them together with the block metadata.
The WeTime service discovers the public key from a DNSSEC-validated
`_welist-key.<host>` TXT record or from a v3 onion address, so no HTTP
callback is needed. The `url` is trimmed of trailing slashes to match the
service-side normalization; absent metadata is signed as `null`.
- `bal-pusher.sh` sets `WELIST_PING_SIG_VERSION=2` for the development setup;
remove that line to fall back to the legacy scheme.
- To publish the v2 public key, derive the base64 SubjectPublicKeyInfo DER:
`openssl pkey -in private_key.pem -pubout -outform DER | base64 -w0` and
publish it in a DNSSEC-signed TXT record named `_welist-key.<host>` with value
`v=we1; alg=ed25519; key=<base64>`. A v3 onion URL needs no DNS record: its
Ed25519 identity key is embedded in the address.
### ZMQ Timeout Handling
- Uses `set_rcvtimeo(5000)` (5-second timeout).
@@ -208,5 +225,6 @@ All configuration is via environment variables (no config files):
- `SSL_KEY_PATH`: Path to Ed25519 private key for signing stats.
- `WELIST_SERVER_URL`: URL to POST stats to (validated against SSRF).
- `WELIST_SKIP_URL_VALIDATION`: Bypass URL validation (for testing).
- `WELIST_PING_SIG_VERSION`: WeTime ping signature scheme (`1` legacy, `2` canonical JSON).
- `BAL_PUSHER_PREFER_IPV6`: Pin HTTP connection to IPv6 address.
- Per-network: `BAL_PUSHER_{NETWORK}_HOST`, `_PORT`, `_DIR_PATH`, `_DB_FIELD`, `_COOKIE_FILE`, `_RPC_USER`, `_RPC_PASSWORD`, `_ZMQ_HASHBLOCK`.