Merge v0.5.x hot fixes#114
Conversation
- Add AdaptiveSplit50 staking strategy that adapts stake amount based on current election state - Emulate TON Elector's selection algorithm to compute minimum effective stake - Split funds in half when viable, stake everything when half is below threshold - Top-up stake when competitors raise theirs
Reserve the logical wallet name `master_wallet` in `nodectl`: operators cannot add a regular wallet with that name, and `config wallet rm` fails immediately with a clear error when that name is used.
Add nodectl e2e test for CI.
* fix: max factor load from config * fix: default factor func not pub * fix: fix test * fix: corrections according to comments * fix: corrections according to comments * fix(naming):validate_elections_max_factor_vs_chain → validate_max_factor * fix(naming):max factor warning text * fix: added tests,replaced code duplicationg
Add `nodectl vote` with subcommands to manage config proposal voting
…#86) - Add REST API handlers for all `config ls` subcommands in the nodectl service - Refactor `config`, `config bind/elections/log/node/pool/wallet`, and `master_wallet` CLI commands to call the REST API instead of touching files directly - Use a fallback URL and default config path in commands
- `POST /v1/{nodes,wallets,pools,bindings}` and `DELETE /v1/{nodes,wallets,pools,bindings}/{name}`, all behind `require_operator`.
- Validation moved server-side.
- CLI `add`/`rm` become thin REST clients; `--url` / `--token` / `NODECTL_API_TOKEN` work the same as in SMA-19. Best-effort vault check kept in CLI for `node add` / `wallet add` until the key REST API lands.
## Summary - Unified `POST /v1/elections/settings` replaces `/v1/stake_strategy`, individual tick-interval and max-factor endpoints. Accepts any combination of policy, tick_interval, max_factor in one request. - Unified `POST /v1/ton-http-api` with `append` flag replaces separate set/add endpoints - `POST /v1/log` for log settings mutation - Removed `config stake-policy` CLI alias (use `config elections stake-policy`) ## Breaking changes - `config ton-http-api`: renamed `--url` option to `--endpoint` (disambiguates from the root `config --url` option).
- Add support for the TON Core Nominator Pool alongside existing Single Nominator Pool
- Fixed calculation of the next elections range in `/v1/elections` response.
- bump nodectl version to 0.4.0 - upd changelog for v0.4.0 - upd nodectl docs
#95) - `config pool ls` and `GET /v1/pools` now render TONCore pools as a per-slot table with state (active / uninit / not deployed / error), balance, and on-chain pool params (validator share, max nominators, min stakes, nominators count, staked amount, pool state, last election id). - added `POST /v1/pools/core` so `config pool add core` goes through REST like the rest of v0.4.0 entity CRUD instead of writing the local config file.
- ** run_singlehost_nodectl.py**: added scenarios - set of predefined env vars and parameters; two variants present - default + snp-toncore
- fixed race condition in force_reload()
Hot fix: Liteserver ListBlockTransactions
#99) * Fix TONCore frozen stake accounting and singlehost election_id parsing Only count frozen stake from past elections when the frozen entry wallet matches the current staking address, so dual-pool TONCore nodes do not double-count the other pool. Update singlehost nodectl bootstrap to read election_id from nested API result when present. Adjust runner test frozen_map wallet to pool address. Made-with: Cursor * fix: delete warning that not needed * fix: copilot comments * fix: rewrite log text
- **Bug**: `build_validators_snapshot` sourced `adnl`, `pubkey`, `key_id`, `key_election_id`, `key_expires_at`, `is_key_active`, and `stake` from elections/bid data instead of the active validator set — showing stale or incorrect values when a node is validating AND has submitted a new election bid - **Fix**: All vset-related snapshot fields now come from the current validator set (p34) and `past_elections.frozen_map`. Extended `find_validator_entries` to return the matched `ValidatorEntry` from the node's config. Fields are `None` when the node is not in the validator set.
release/node:v0.5.1
* feat(docs): update info about toncore pool * fix(docs): add info about distinct addresses * fix: set right value for min validator stake param
## Summary - Add `elections.static_adnls` config map to store pre-generated ADNL addresses per node (base64) - Add `generate_adnl_addr()` and `register_adnl_addr()` to `ElectionsProvider` trait - Runner reuses stored ADNL address via `register_adnl_addr()` instead of generating a new one each cycle - New `POST /v1/elections/static-adnl` endpoint: generates ADNL key on the validator node and saves it to config - New CLI command: `nodectl config elections static-adnl --node <name>`
Merge master to release/nodectl/v0.4.0
* test: enhance create-proposal.ts script * refactor(contracts): actualize comment to NominatorWrapper trait * fix: after review * fmt: make fmt
Release/nodectl/v0.4.0
Fix two-step broadcast neighbour count for two-step broadcasts
TVM stack slice TL-serialization fix
Changes for release node/v0.5.2
release/node/v0.5.2
* docs(nodectl): upd info about nominator pools and adaptive-split50 policy
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
This PR merges hotfixes and feature work across the TON node and node-control (“nodectl”) tooling, including QUIC fast-sync overlay improvements, TONCore nominator pool support, and a shift to REST-based config mutations.
Changes:
- Node networking: extend semiprivate overlays with separate ADNL IDs/public keys, add two-step broadcast, and optimize LiteServer transaction traversal/proof handling.
- Nodectl/service: add TONCore pool support, unified elections settings endpoints, and atomic config update+persist flow with cache rebuild notifications.
- Tooling/docs/CI: add new load-net scripts, update Helm chart/app versions, and introduce a nodectl E2E GitHub workflow.
Reviewed changes
Copilot reviewed 89 out of 95 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| src/node/tests/test_run_net_py/requirements.txt | Adds Python dependency for singlehost/e2e tooling. |
| src/node/tests/test_load_net/scripts/withdraw-nominators-from-pool.ts | New TONCore withdrawal load-test script. |
| src/node/tests/test_load_net/scripts/topup.ts | Adds polling to await balance updates after transfers. |
| src/node/tests/test_load_net/scripts/toncore_pool_info.ts | Adds TONCore pool introspection helpers. |
| src/node/tests/test_load_net/scripts/create-proposal.ts | Updates proposal tooling (tx parsing + configurable endpoints). |
| src/node/tests/test_load_net/scripts/add-nominators-to-pool.ts | New TONCore nominator deposit/load script. |
| src/node/tests/test_load_net/package.json | Exposes new scripts via npm/bun commands. |
| src/node/src/rpc_server/token.rs | Fixes address parsing for TVM stack slices. |
| src/node/src/network/overlay_client.rs | Updates semiprivate overlay creation + adds two-step broadcast API. |
| src/node/src/network/liteserver.rs | Optimizes listBlockTransactions traversal and proof generation flow. |
| src/node/src/network/full_node_overlays.rs | Expands validator set handling and enables two-step broadcasts for fast sync. |
| src/node/src/network/fast_sync_overlay_client.rs | Adds root public keys and QUIC/two-step broadcast support. |
| src/node/Cargo.toml | Bumps node crate version to 0.5.1. |
| src/node-control/ton-http-api-client/Cargo.toml | Bumps ton-http-api-client version. |
| src/node-control/service/src/voting/voting_task.rs | Skips voting on non-validators and updates config contract address API usage. |
| src/node-control/service/src/task/task_manager.rs | Updates RuntimeConfig trait usage in tests. |
| src/node-control/service/src/service_main_task.rs | Adds config mutation notifications for cache rebuild + task restart. |
| src/node-control/service/src/runtime_config.rs | Adds atomic update+persist, cache reload paths, and TONCore pool wiring. |
| src/node-control/service/src/http/mod.rs | Splits config-related handlers into a module + adds tests module. |
| src/node-control/service/src/http/http_server_task.rs | Adds REST config CRUD endpoints and updates OpenAPI wiring. |
| src/node-control/service/src/http/auth_tests.rs | Updates auth tests to the new endpoints. |
| src/node-control/service/src/contracts/contracts_task.rs | Adds TONCore pool validator-set update and multi-pool deploy support. |
| src/node-control/service/src/auth/user_store.rs | Switches to atomic update+persist for auth user mutations. |
| src/node-control/service/Cargo.toml | Bumps service version. |
| src/node-control/nodectl/Cargo.toml | Bumps nodectl version. |
| src/node-control/elections/src/providers/traits.rs | Extends provider trait with config params + ADNL management APIs. |
| src/node-control/elections/src/providers/default.rs | Implements new provider methods via control-client. |
| src/node-control/elections/src/lib.rs | Exposes new internal modules. |
| src/node-control/elections/Cargo.toml | Bumps elections crate version. |
| src/node-control/docs/staking-strategies.md | Adds detailed staking strategies documentation (incl. adaptive_split50). |
| src/node-control/docs/nodectl-security.md | Updates operator capabilities list for new endpoints. |
| src/node-control/control-client/src/config_params.rs | Adds parsing for config params 16/17. |
| src/node-control/control-client/Cargo.toml | Bumps control-client version. |
| src/node-control/contracts/src/wallet/wallet_contract.rs | Makes SmartContract::address async and updates callers. |
| src/node-control/contracts/src/smart_contract.rs | Changes SmartContract::address signature to async. |
| src/node-control/contracts/src/nominator/wrapper.rs | Expands nominator abstraction (kind, reserves, inner pools). |
| src/node-control/contracts/src/nominator/ton_core_nominator_router.rs | Adds TONCore dual-pool router wrapper. |
| src/node-control/contracts/src/nominator/single_nominator.rs | Renames wrapper and adds address+state calc helper. |
| src/node-control/contracts/src/nominator/messages.rs | Adds TONCore pool message builders and tests. |
| src/node-control/contracts/src/nominator.rs | Wires new nominator modules and exports. |
| src/node-control/contracts/src/lib.rs | Re-exports new nominator types and helpers. |
| src/node-control/contracts/src/elector/elector_impl.rs | Updates SmartContract::address to async. |
| src/node-control/contracts/src/config_contract/config_impl.rs | Updates SmartContract::address to async + fixes tests accordingly. |
| src/node-control/contracts/Cargo.toml | Bumps contracts version. |
| src/node-control/common/src/ton_utils.rs | Adds max_factor extraction and TON address normalization helpers. |
| src/node-control/common/src/serde_utils.rs | Adjusts derive usage in serde tests. |
| src/node-control/common/src/app_config.rs | Adds TONCore pool config, adaptive policy, elections timing/static_adnls. |
| src/node-control/common/Cargo.toml | Bumps common version. |
| src/node-control/commands/src/commands/nodectl/utils.rs | Adds service API helpers + TONCore pool address resolution helpers. |
| src/node-control/commands/src/commands/nodectl/service_api_cmd.rs | Updates REST endpoints and adds adaptive_split50 flag. |
| src/node-control/commands/src/commands/nodectl/mod.rs | Adds vote_cmd module export. |
| src/node-control/commands/src/commands/nodectl/master_wallet_cmd.rs | Switches master wallet info to REST-based retrieval. |
| src/node-control/commands/src/commands/nodectl/deploy_cmd.rs | Adds TONCore pool deploy targets and binding-based deploy flow. |
| src/node-control/commands/src/commands/nodectl/config_wallet_cmd.rs | Moves wallet CRUD to REST and updates staking max_factor validation. |
| src/node-control/commands/src/commands/nodectl/config_ton_http_api_cmd.rs | Switches TON HTTP API config to unified REST endpoint. |
| src/node-control/commands/src/commands/nodectl/config_node_cmd.rs | Moves node CRUD/list to REST. |
| src/node-control/commands/src/commands/nodectl/config_log_cmd.rs | Moves log config to REST endpoints. |
| src/node-control/commands/src/commands/nodectl/config_cmd.rs | Adds global REST URL/token flags and removes local stake-policy alias. |
| src/node-control/commands/src/commands/nodectl/config_bind_cmd.rs | Moves bindings CRUD/list to REST. |
| src/node-control/commands/src/commands/nodectl/auth_cmd.rs | Adds password stdin option for non-interactive user creation. |
| src/node-control/commands/src/commands/cli_cmd.rs | Adds vote command entry point. |
| src/node-control/commands/src/command_manager.rs | Routes vote command execution. |
| src/node-control/commands/Cargo.toml | Adds hex dep and bumps commands version. |
| src/node-control/Makefile | Adds singlehost e2e targets and venv bootstrap. |
| src/node-control/CHANGELOG.md | Documents nodectl v0.4.0 changes and breaking changes. |
| src/adnl/tests/test_overlay.rs | Updates semiprivate overlay test for new API signature. |
| src/adnl/src/overlay/mod.rs | Adds CertifiedMembers roots by ADNL/public keys + QUIC/twostep behavior changes. |
| helm/nodectl/values.yaml | Bumps default image tag to v0.4.0. |
| helm/nodectl/docs/setup.md | Documents TONCore pools and new CLI flows. |
| helm/nodectl/docs/elections.md | Updates elections docs for new policies/pools. |
| helm/nodectl/README.md | Updates chart README for TONCore pool support. |
| helm/nodectl/Chart.yaml | Bumps chart/app versions. |
| helm/nodectl/CHANGELOG.md | Adds chart changelog entry for v0.4.0 appVersion. |
| CHANGELOG.md | Documents node v0.5.2/v0.5.1 entries. |
| .github/workflows/nodectl-e2e.yml | Adds PR-triggered nodectl e2e workflow. |
| .github/workflows/ci.yml | Ignores docs-only changes for CI runs. |
Comments suppressed due to low confidence (1)
src/node-control/CHANGELOG.md:1
- The PR title suggests this is a merge of v0.5.x hot fixes, but the diff includes a substantial nodectl v0.4.0 feature release (new REST config API, TONCore pools, new staking strategy, new CLI commands, Helm/app version bumps). Consider updating the PR title (or splitting) so reviewers can accurately scope changes and risk.
# Changelog
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| lt, hash, | ||
| }); | ||
| for (const tx of transactions) { | ||
| const transactions = await getTransactions(configAddress, 10, parseInt(lt), hash); |
There was a problem hiding this comment.
Parsing lt with parseInt(lt) is unsafe because transaction LTs can exceed JS Number.MAX_SAFE_INTEGER, leading to precision loss and incorrect pagination/lookup. Pass lt through as a string (as returned by the node) or use BigInt, and update getTransactions to serialize it accordingly.
| const transactions = await getTransactions(configAddress, 10, parseInt(lt), hash); | |
| const transactions = await getTransactions(configAddress, 10, lt, hash); |
| async function getTransactions(address: Address, limit: number, lt: number, hash: string) { | ||
| const result = await fetch(`${getBaseUrl()}jsonRPC`, { | ||
| method: 'POST', | ||
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| }, | ||
| body: JSON.stringify({ | ||
| jsonrpc: '2.0', | ||
| method: 'getTransactions', | ||
| params: { | ||
| address: address.toString(), | ||
| limit, | ||
| lt, | ||
| hash, | ||
| }, | ||
| }), | ||
| }); | ||
| const data = await result.json(); | ||
| if (!data.ok) { | ||
| throw new Error(`Failed to get config: ${data.error}`); | ||
| throw new Error(`Failed to get transactions: ${data.error}`); | ||
| } | ||
| return Cell.fromBase64(data.result.config.bytes as string); | ||
| return data.result; | ||
| } |
There was a problem hiding this comment.
getTransactions is using a JSON-RPC endpoint but checks data.ok, which is not part of standard JSON-RPC responses. This will falsely throw (or miss errors) depending on the server. Handle JSON-RPC by checking data.error (and optionally result) and include an id field in the request body.
| let mut deploy_targets: Vec<(MsgAddressInt, ton_block::StateInit)> = match pool_cfg { | ||
| PoolConfig::TONCore { pools } => { | ||
| let mut targets = Vec::new(); | ||
| for slot in pools.iter() { | ||
| let Some(cfg) = slot else { continue }; | ||
| match (&cfg.address, &cfg.params) { | ||
| (_, Some(params)) => { | ||
| let resolved = resolve_toncore_pool( | ||
| &wallet_address, | ||
| cfg.address.as_deref(), | ||
| params.clone(), | ||
| ) | ||
| .map_err(set_err)?; | ||
| targets.push((resolved.address, resolved.state_init)); | ||
| } | ||
| (Some(address), None) => { | ||
| return Err(set_err(anyhow::anyhow!( | ||
| "TONCore pool slot has address '{}' but no deploy params", | ||
| address | ||
| ))); | ||
| } | ||
| (None, None) => {} | ||
| } | ||
| } | ||
| targets | ||
| } | ||
| PoolConfig::SNP { .. } => { | ||
| let owner = self.owner.as_ref().ok_or_else(|| { | ||
| set_err(anyhow::anyhow!( | ||
| "SNP deploy requires --owner (use TONCore pool for deploy without --owner)" | ||
| )) | ||
| })?; | ||
| let (pool_address, state_init) = | ||
| SingleNominatorWrapper::calculate_address_and_state(-1, owner, &wallet_address) | ||
| .map_err(set_err)?; | ||
| vec![(pool_address, state_init)] | ||
| } | ||
| }; | ||
|
|
||
| if matches!(pool_cfg, PoolConfig::TONCore { pools } if pools[1].is_some()) { | ||
| let slot = toncore_pool_slot_from_cli_flags(self.pool_even, self.pool_odd); | ||
| let one = deploy_targets | ||
| .get(slot) | ||
| .cloned() | ||
| .ok_or_else(|| { | ||
| anyhow::anyhow!("TONCore nominator deploy: invalid pool slot {}", slot) | ||
| }) | ||
| .map_err(set_err)?; | ||
| res.borrow_mut().address = pool_address.to_string(); | ||
| deploy_targets = vec![one]; | ||
| } |
There was a problem hiding this comment.
TONCore deploy target selection is index-based after filtering out None slots, so deploy_targets[1] is not guaranteed to correspond to the odd slot (e.g., if only slot 1 is configured, it becomes index 0 and --pool-odd fails). Preserve the slot index when building targets (e.g., build [Option<(addr, state_init)>; 2] or push (slot_index, addr, state_init)), then select by slot index.
| tokio::task::spawn_blocking(move || { | ||
| let root = read_single_root_boc(&raw)?; | ||
| let usage = UsageTree::with_params(root.clone(), true); | ||
| let usage = UsageTree::with_params(root.clone(), false); |
There was a problem hiding this comment.
This path constructs UsageTree with the second parameter set to false, but later potentially derives a Merkle proof via MerkleProof::create_by_usage_tree(&root, &usage) (when WANT_PROOF is set). If false disables usage tracking, the generated proof can be incomplete/invalid. Consider enabling usage tracking when WANT_PROOF is requested (or always, if proof correctness relies on it).
| let usage = UsageTree::with_params(root.clone(), false); | |
| let track_usage = mode & WANT_PROOF != 0; | |
| let usage = UsageTree::with_params(root.clone(), track_usage); |
| let parse_coins = |key: &str| -> anyhow::Result<Coins> { | ||
| let val = p17.get(key).ok_or_else(|| anyhow::anyhow!("{} not found", key))?; | ||
| // It can be a string (decimal) or a number | ||
| if let Some(s) = val.as_str() { | ||
| Ok(Coins::from(u64::from_str_radix(s, 10).context(format!("parse {} as u64", key))?)) | ||
| } else if let Some(n) = val.as_u64() { | ||
| Ok(Coins::from(n)) | ||
| } else { | ||
| anyhow::bail!("{} is not a valid coins value", key) | ||
| } | ||
| }; |
There was a problem hiding this comment.
Coins can represent values larger than u64, but the string parsing path forces u64, which can fail or truncate on larger networks/fields. Parse decimal strings as u128 (or use Coins::from_str / ton_block-provided parsing) and only downcast if you explicitly want to cap the value.
| endpoint: process.env.API_ENDPOINTS!.split(",")[0] + "jsonRPC", | ||
| }); | ||
| const master = tonClient.open(masterWallet); | ||
| const balanceBefore = await tonClient.getBalance(address); |
There was a problem hiding this comment.
On success, the function returns without confirming completion (and without printing the updated balance), which makes CLI behavior less clear compared to the previous log message. Consider logging a success message (optionally including fromNano(balance)) before returning.
| sendMode: SendMode.PAY_GAS_SEPARATELY, | ||
| }); | ||
| console.log(`Topped up ${address} with ${fromNano(amount)} TON`); | ||
| console.log(`Sent ${fromNano(amount)} TON to ${address}, waiting for balance update...`); |
There was a problem hiding this comment.
On success, the function returns without confirming completion (and without printing the updated balance), which makes CLI behavior less clear compared to the previous log message. Consider logging a success message (optionally including fromNano(balance)) before returning.
| while (Date.now() - start < timeout) { | ||
| const balance = await tonClient.getBalance(address); | ||
| if (balance > balanceBefore) { | ||
| return; | ||
| } | ||
| await new Promise((r) => setTimeout(r, poll)); | ||
| } | ||
| throw new Error(`Timed out waiting for balance update after ${timeout / 1000}s`); |
There was a problem hiding this comment.
On success, the function returns without confirming completion (and without printing the updated balance), which makes CLI behavior less clear compared to the previous log message. Consider logging a success message (optionally including fromNano(balance)) before returning.
| ### Breaking Changes | ||
|
|
||
| - **Removed `POST /v1/stake_strategy`** — use `POST /v1/elections/settings` with `{"policy": ...}`. | ||
| - **Removed `config stake-policy` top-level alias** — use `config elections stake-policy`. | ||
| - **`config ton-http-api set --url` → `--endpoint`** — the flag was renamed (short form `-e`) to disambiguate from the root `--url` service-URL flag introduced for REST client commands. Update any scripts invoking `nodectl config ton-http-api --url ...`. | ||
| - **Configuration mutations require a running service** — `config {node,wallet,pool,bind,elections,log,ton-http-api,master-wallet}` subcommands are now REST clients and need the service to be running with an operator user. Only `config generate` still writes to disk directly. |
There was a problem hiding this comment.
The PR title suggests this is a merge of v0.5.x hot fixes, but the diff includes a substantial nodectl v0.4.0 feature release (new REST config API, TONCore pools, new staking strategy, new CLI commands, Helm/app version bumps). Consider updating the PR title (or splitting) so reviewers can accurately scope changes and risk.
No description provided.